diff options
-rwxr-xr-x | compiler/dex/quick/x86/int_x86.cc | 15 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.cc | 4 | ||||
-rw-r--r-- | runtime/verifier/register_line-inl.h | 10 | ||||
-rw-r--r-- | test/082-inline-execute/src/Main.java | 39 | ||||
-rw-r--r-- | test/800-smali/expected.txt | 1 | ||||
-rw-r--r-- | test/800-smali/smali/b_22331663.smali | 35 | ||||
-rw-r--r-- | test/800-smali/src/Main.java | 2 |
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() { |