summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/dex/quick/x86/int_x86.cc15
-rw-r--r--runtime/verifier/method_verifier.cc4
-rw-r--r--runtime/verifier/register_line-inl.h10
-rw-r--r--test/082-inline-execute/src/Main.java39
-rw-r--r--test/800-smali/expected.txt1
-rw-r--r--test/800-smali/smali/b_22331663.smali35
-rw-r--r--test/800-smali/src/Main.java2
7 files changed, 100 insertions, 6 deletions
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index d993d93..d1fe167 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -1336,9 +1336,24 @@ bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
}
OpRegReg(kOpRev, rl_result.reg.GetLow(), rl_i.reg.GetHigh());
OpRegReg(kOpRev, rl_result.reg.GetHigh(), r_i_low);
+ // Free up at least one input register if it was a temp. Otherwise we may be in the bad
+ // situation of not having a temp available for SwapBits. Make sure it's not overlapping
+ // with the output, though.
if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+ // There's definitely a free temp after this.
FreeTemp(r_i_low);
+ } else {
+ // We opportunistically release both here. That saves duplication of the register state
+ // lookup (to see if it's actually a temp).
+ if (rl_i.reg.GetLowReg() != rl_result.reg.GetHighReg()) {
+ FreeTemp(rl_i.reg.GetLow());
+ }
+ if (rl_i.reg.GetHighReg() != rl_result.reg.GetLowReg() &&
+ rl_i.reg.GetHighReg() != rl_result.reg.GetHighReg()) {
+ FreeTemp(rl_i.reg.GetHigh());
+ }
}
+
SwapBits(rl_result.reg.GetLow(), 1, 0x55555555);
SwapBits(rl_result.reg.GetLow(), 2, 0x33333333);
SwapBits(rl_result.reg.GetLow(), 4, 0x0f0f0f0f);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 085f741..e3999c1 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4061,7 +4061,9 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType&
<< " to be compatible with type '" << insn_type
<< "' but found type '" << *field_type
<< "' in get-object";
- work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
+ if (error != VERIFY_ERROR_BAD_CLASS_HARD) {
+ work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
+ }
return;
}
}
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 244deed..9cd2bdf 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -38,10 +38,9 @@ inline bool RegisterLine::SetRegisterType(MethodVerifier* verifier, uint32_t vds
verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
<< new_type << "'";
return false;
- } else if (new_type.IsConflict()) { // should only be set during a merge
- verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
- return false;
} else {
+ // Note: previously we failed when asked to set a conflict. However, conflicts are OK as long
+ // as they are not accessed, and our backends can handle this nowadays.
line_[vdst] = new_type.GetId();
}
// Clear the monitor entry bits for this register.
@@ -93,8 +92,9 @@ inline void RegisterLine::CopyRegister1(MethodVerifier* verifier, uint32_t vdst,
if (!SetRegisterType(verifier, vdst, type)) {
return;
}
- if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
- (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
+ if (!type.IsConflict() && // Allow conflicts to be copied around.
+ ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
+ (cat == kTypeCategoryRef && !type.IsReferenceTypes()))) {
verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
<< " cat=" << static_cast<int>(cat);
} else if (cat == kTypeCategoryRef) {
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 4dfa73c..177c5a4 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -1000,6 +1000,45 @@ public class Main {
Assert.assertEquals(Long.reverse(0x8765432187654321L), 0x84c2a6e184c2a6e1L);
Assert.assertEquals(Long.reverse(Long.MAX_VALUE), 0xfffffffffffffffeL);
Assert.assertEquals(Long.reverse(Long.MIN_VALUE), 1L);
+
+ Assert.assertEquals(test_Long_reverse_b22324327(0xaaaaaaaaaaaaaaaaL, 0x5555555555555555L),
+ 157472205507277347L);
+ }
+
+ // A bit more complicated than the above. Use local variables to stress register allocation.
+ private static long test_Long_reverse_b22324327(long l1, long l2) {
+ // A couple of local integers. Use them in a loop, so they get promoted.
+ int i1 = 0, i2 = 1, i3 = 2, i4 = 3, i5 = 4, i6 = 5, i7 = 6, i8 = 7;
+ for (int k = 0; k < 10; k++) {
+ i1 += 1;
+ i2 += 2;
+ i3 += 3;
+ i4 += 4;
+ i5 += 5;
+ i6 += 6;
+ i7 += 7;
+ i8 += 8;
+ }
+
+ // Do the Long.reverse() calls, save the results.
+ long r1 = Long.reverse(l1);
+ long r2 = Long.reverse(l2);
+
+ // Some more looping with the ints.
+ for (int k = 0; k < 10; k++) {
+ i1 += 1;
+ i2 += 2;
+ i3 += 3;
+ i4 += 4;
+ i5 += 5;
+ i6 += 6;
+ i7 += 7;
+ i8 += 8;
+ }
+
+ // Include everything in the result, so things are kept live. Try to be a little bit clever to
+ // avoid things being folded somewhere.
+ return (r1 / i1) + (r2 / i2) + i3 + i4 + i5 + i6 + i7 + i8;
}
static Object runtime;
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index c32bfc2..aa997a6 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -24,4 +24,5 @@ b/21886894
b/22080519
b/21645819
b/22244733
+b/22331663
Done!
diff --git a/test/800-smali/smali/b_22331663.smali b/test/800-smali/smali/b_22331663.smali
new file mode 100644
index 0000000..af99152
--- /dev/null
+++ b/test/800-smali/smali/b_22331663.smali
@@ -0,0 +1,35 @@
+.class public LB22331663;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ if-eqz v5, :Label2
+
+:Label1
+ # Construct a java.lang.Object completely, and throw a new exception.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+ new-instance v3, Ljava/lang/RuntimeException;
+ invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+ throw v3
+
+:Label2
+ # Allocate a java.lang.Object (do not initialize), and throw a new exception.
+ new-instance v4, Ljava/lang/Object;
+
+ new-instance v3, Ljava/lang/RuntimeException;
+ invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+ throw v3
+
+:Label3
+ # Catch handler. Here we had to merge the uninitialized with the initialized reference,
+ # which creates a conflict. Copy the conflict, and then return. This should not make the
+ # verifier fail the method.
+ move-object v0, v4
+
+ return-void
+
+.catchall {:Label1 .. :Label3} :Label3
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index a7f30d0..e0872c3 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -95,6 +95,8 @@ public class Main {
null, null));
testCases.add(new TestCase("b/22244733", "B22244733", "run", new Object[] { "abc" },
null, "abc"));
+ testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
+ null, null));
}
public void runTests() {