Merge "Rename callee save enumerators."
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 5eaf11e..ab85c12 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2531,7 +2531,7 @@
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
@@ -2568,13 +2568,18 @@
break;
case Primitive::kPrimLong: {
- DCHECK(second.IsRegisterPair());
- __ adds(out.AsRegisterPairLow<Register>(),
- first.AsRegisterPairLow<Register>(),
- ShifterOperand(second.AsRegisterPairLow<Register>()));
- __ adc(out.AsRegisterPairHigh<Register>(),
- first.AsRegisterPairHigh<Register>(),
- ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ if (second.IsConstant()) {
+ uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
+ GenerateAddLongConst(out, first, value);
+ } else {
+ DCHECK(second.IsRegisterPair());
+ __ adds(out.AsRegisterPairLow<Register>(),
+ first.AsRegisterPairLow<Register>(),
+ ShifterOperand(second.AsRegisterPairLow<Register>()));
+ __ adc(out.AsRegisterPairHigh<Register>(),
+ first.AsRegisterPairHigh<Register>(),
+ ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ }
break;
}
@@ -2608,7 +2613,7 @@
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
@@ -2644,13 +2649,18 @@
}
case Primitive::kPrimLong: {
- DCHECK(second.IsRegisterPair());
- __ subs(out.AsRegisterPairLow<Register>(),
- first.AsRegisterPairLow<Register>(),
- ShifterOperand(second.AsRegisterPairLow<Register>()));
- __ sbc(out.AsRegisterPairHigh<Register>(),
- first.AsRegisterPairHigh<Register>(),
- ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ if (second.IsConstant()) {
+ uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
+ GenerateAddLongConst(out, first, -value);
+ } else {
+ DCHECK(second.IsRegisterPair());
+ __ subs(out.AsRegisterPairLow<Register>(),
+ first.AsRegisterPairLow<Register>(),
+ ShifterOperand(second.AsRegisterPairLow<Register>()));
+ __ sbc(out.AsRegisterPairHigh<Register>(),
+ first.AsRegisterPairHigh<Register>(),
+ ShifterOperand(second.AsRegisterPairHigh<Register>()));
+ }
break;
}
@@ -4052,31 +4062,51 @@
Opcode opcode) {
uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
if (Primitive::Is64BitType(input_cst->GetType())) {
- return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) &&
- CanEncodeConstantAsImmediate(High32Bits(value), opcode);
+ Opcode high_opcode = opcode;
+ SetCc low_set_cc = kCcDontCare;
+ switch (opcode) {
+ case SUB:
+ // Flip the operation to an ADD.
+ value = -value;
+ opcode = ADD;
+ FALLTHROUGH_INTENDED;
+ case ADD:
+ if (Low32Bits(value) == 0u) {
+ return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
+ }
+ high_opcode = ADC;
+ low_set_cc = kCcSet;
+ break;
+ default:
+ break;
+ }
+ return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
+ CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
} else {
return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
}
}
-bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) {
+bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value,
+ Opcode opcode,
+ SetCc set_cc) {
ShifterOperand so;
ArmAssembler* assembler = codegen_->GetAssembler();
- if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) {
+ if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, set_cc, &so)) {
return true;
}
Opcode neg_opcode = kNoOperand;
switch (opcode) {
- case AND:
- neg_opcode = BIC;
- break;
- case ORR:
- neg_opcode = ORN;
- break;
+ case AND: neg_opcode = BIC; value = ~value; break;
+ case ORR: neg_opcode = ORN; value = ~value; break;
+ case ADD: neg_opcode = SUB; value = -value; break;
+ case ADC: neg_opcode = SBC; value = ~value; break;
+ case SUB: neg_opcode = ADD; value = -value; break;
+ case SBC: neg_opcode = ADC; value = ~value; break;
default:
return false;
}
- return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so);
+ return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, value, set_cc, &so);
}
void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
@@ -6202,6 +6232,34 @@
__ eor(out, first, ShifterOperand(value));
}
+void InstructionCodeGeneratorARM::GenerateAddLongConst(Location out,
+ Location first,
+ uint64_t value) {
+ Register out_low = out.AsRegisterPairLow<Register>();
+ Register out_high = out.AsRegisterPairHigh<Register>();
+ Register first_low = first.AsRegisterPairLow<Register>();
+ Register first_high = first.AsRegisterPairHigh<Register>();
+ uint32_t value_low = Low32Bits(value);
+ uint32_t value_high = High32Bits(value);
+ if (value_low == 0u) {
+ if (out_low != first_low) {
+ __ mov(out_low, ShifterOperand(first_low));
+ }
+ __ AddConstant(out_high, first_high, value_high);
+ return;
+ }
+ __ AddConstantSetFlags(out_low, first_low, value_low);
+ ShifterOperand so;
+ if (__ ShifterOperandCanHold(out_high, first_high, ADC, value_high, kCcDontCare, &so)) {
+ __ adc(out_high, first_high, so);
+ } else if (__ ShifterOperandCanHold(out_low, first_low, SBC, ~value_high, kCcDontCare, &so)) {
+ __ sbc(out_high, first_high, so);
+ } else {
+ LOG(FATAL) << "Unexpected constant " << value_high;
+ UNREACHABLE();
+ }
+}
+
void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location first = locations->InAt(0);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index fa7709b..5d9b2dc 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -183,7 +183,7 @@
Location ArithmeticZeroOrFpuRegister(HInstruction* input);
Location ArmEncodableConstantOrRegister(HInstruction* constant, Opcode opcode);
bool CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode);
- bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode);
+ bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode, SetCc set_cc = kCcDontCare);
CodeGeneratorARM* const codegen_;
InvokeDexCallingConventionVisitorARM parameter_visitor_;
@@ -220,6 +220,7 @@
void GenerateAndConst(Register out, Register first, uint32_t value);
void GenerateOrrConst(Register out, Register first, uint32_t value);
void GenerateEorConst(Register out, Register first, uint32_t value);
+ void GenerateAddLongConst(Location out, Location first, uint64_t value);
void HandleBitwiseOperation(HBinaryOperation* operation);
void HandleCondition(HCondition* condition);
void HandleIntegerRotate(LocationSummary* locations);
diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h
index dd65f2c..3d0fea0 100644
--- a/runtime/dex_instruction-inl.h
+++ b/runtime/dex_instruction-inl.h
@@ -49,6 +49,8 @@
case k32x: return true;
case k35c: return true;
case k3rc: return true;
+ case k45cc: return true;
+ case k4rcc: return true;
case k51l: return true;
default: return false;
}
@@ -79,6 +81,8 @@
case k32x: return VRegA_32x();
case k35c: return VRegA_35c();
case k3rc: return VRegA_3rc();
+ case k45cc: return VRegA_45cc();
+ case k4rcc: return VRegA_4rcc();
case k51l: return VRegA_51l();
default:
LOG(FATAL) << "Tried to access vA of instruction " << Name() << " which has no A operand.";
@@ -206,6 +210,16 @@
return InstAA(inst_data);
}
+inline uint4_t Instruction::VRegA_45cc(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return InstB(inst_data); // This is labeled A in the spec.
+}
+
+inline uint8_t Instruction::VRegA_4rcc(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return InstAA(inst_data);
+}
+
//------------------------------------------------------------------------------
// VRegB
//------------------------------------------------------------------------------
@@ -229,6 +243,8 @@
case k32x: return true;
case k35c: return true;
case k3rc: return true;
+ case k45cc: return true;
+ case k4rcc: return true;
case k51l: return true;
default: return false;
}
@@ -258,6 +274,8 @@
case k32x: return VRegB_32x();
case k35c: return VRegB_35c();
case k3rc: return VRegB_3rc();
+ case k45cc: return VRegB_45cc();
+ case k4rcc: return VRegB_4rcc();
case k51l: return VRegB_51l();
default:
LOG(FATAL) << "Tried to access vB of instruction " << Name() << " which has no B operand.";
@@ -359,6 +377,16 @@
return Fetch16(1);
}
+inline uint16_t Instruction::VRegB_45cc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return Fetch16(1);
+}
+
+inline uint16_t Instruction::VRegB_4rcc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return Fetch16(1);
+}
+
inline uint64_t Instruction::VRegB_51l() const {
DCHECK_EQ(FormatOf(Opcode()), k51l);
uint64_t vB_wide = Fetch32(1) | ((uint64_t) Fetch32(3) << 32);
@@ -377,6 +405,8 @@
case k23x: return true;
case k35c: return true;
case k3rc: return true;
+ case k45cc: return true;
+ case k4rcc: return true;
default: return false;
}
}
@@ -390,6 +420,8 @@
case k23x: return VRegC_23x();
case k35c: return VRegC_35c();
case k3rc: return VRegC_3rc();
+ case k45cc: return VRegC_45cc();
+ case k4rcc: return VRegC_4rcc();
default:
LOG(FATAL) << "Tried to access vC of instruction " << Name() << " which has no C operand.";
exit(EXIT_FAILURE);
@@ -431,11 +463,52 @@
return Fetch16(2);
}
+inline uint4_t Instruction::VRegC_45cc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return static_cast<uint4_t>(Fetch16(2) & 0x0f);
+}
+
+inline uint16_t Instruction::VRegC_4rcc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return Fetch16(2);
+}
+
+//------------------------------------------------------------------------------
+// VRegH
+//------------------------------------------------------------------------------
+inline bool Instruction::HasVRegH() const {
+ switch (FormatOf(Opcode())) {
+ case k45cc: return true;
+ case k4rcc: return true;
+ default : return false;
+ }
+}
+
+inline int32_t Instruction::VRegH() const {
+ switch (FormatOf(Opcode())) {
+ case k45cc: return VRegH_45cc();
+ case k4rcc: return VRegH_4rcc();
+ default :
+ LOG(FATAL) << "Tried to access vH of instruction " << Name() << " which has no H operand.";
+ exit(EXIT_FAILURE);
+ }
+}
+
+inline uint16_t Instruction::VRegH_45cc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return Fetch16(3);
+}
+
+inline uint16_t Instruction::VRegH_4rcc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return Fetch16(3);
+}
+
inline bool Instruction::HasVarArgs() const {
return FormatOf(Opcode()) == k35c;
}
-inline void Instruction::GetVarArgs(uint32_t arg[5], uint16_t inst_data) const {
+inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k35c);
/*
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index fabc47b..c31d236 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -69,11 +69,12 @@
int const Instruction::kInstructionSizeInCodeUnits[] = {
#define INSTRUCTION_SIZE(opcode, c, p, format, i, a, v) \
- (((opcode) == NOP) ? -1 : \
- (((format) >= k10x) && ((format) <= k10t)) ? 1 : \
- (((format) >= k20t) && ((format) <= k22c)) ? 2 : \
- (((format) >= k32x) && ((format) <= k3rc)) ? 3 : \
- ((format) == k51l) ? 5 : -1),
+ (((opcode) == NOP) ? -1 : \
+ (((format) >= k10x) && ((format) <= k10t)) ? 1 : \
+ (((format) >= k20t) && ((format) <= k22c)) ? 2 : \
+ (((format) >= k32x) && ((format) <= k3rc)) ? 3 : \
+ (((format) >= k45cc) && ((format) <= k4rcc)) ? 4 : \
+ ((format) == k51l) ? 5 : -1),
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_SIZE)
#undef DEX_INSTRUCTION_LIST
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 1ac0f11..f437fde 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -112,6 +112,15 @@
k31c, // op vAA, thing@BBBBBBBB
k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+
+ // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count)
+ // format: AG op BBBB FEDC HHHH
+ k45cc,
+
+ // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
+ // format: AA op BBBB CCCC HHHH
+ k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
+
k51l, // op vAA, #+BBBBBBBBBBBBBBBB
};
@@ -227,6 +236,12 @@
return RelativeAt(3);
}
+ // Returns a pointer to the instruction after this 4xx instruction in the stream.
+ const Instruction* Next_4xx() const {
+ DCHECK(FormatOf(Opcode()) >= k45cc && FormatOf(Opcode()) <= k4rcc);
+ return RelativeAt(4);
+ }
+
// Returns a pointer to the instruction after this 51l instruction in the stream.
const Instruction* Next_51l() const {
DCHECK(FormatOf(Opcode()) == k51l);
@@ -313,6 +328,12 @@
uint8_t VRegA_51l() const {
return VRegA_51l(Fetch16(0));
}
+ uint4_t VRegA_45cc() const {
+ return VRegA_45cc(Fetch16(0));
+ }
+ uint8_t VRegA_4rcc() const {
+ return VRegA_4rcc(Fetch16(0));
+ }
// The following methods return the vA operand for various instruction formats. The "inst_data"
// parameter holds the first 16 bits of instruction which the returned value is decoded from.
@@ -337,6 +358,8 @@
uint4_t VRegA_35c(uint16_t inst_data) const;
uint8_t VRegA_3rc(uint16_t inst_data) const;
uint8_t VRegA_51l(uint16_t inst_data) const;
+ uint4_t VRegA_45cc(uint16_t inst_data) const;
+ uint8_t VRegA_4rcc(uint16_t inst_data) const;
// VRegB
bool HasVRegB() const;
@@ -374,6 +397,8 @@
uint16_t VRegB_35c() const;
uint16_t VRegB_3rc() const;
uint64_t VRegB_51l() const; // vB_wide
+ uint16_t VRegB_45cc() const;
+ uint16_t VRegB_4rcc() const;
// The following methods return the vB operand for all instruction formats where it is encoded in
// the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned
@@ -395,6 +420,15 @@
uint8_t VRegC_23x() const;
uint4_t VRegC_35c() const;
uint16_t VRegC_3rc() const;
+ uint4_t VRegC_45cc() const;
+ uint16_t VRegC_4rcc() const;
+
+
+ // VRegH
+ bool HasVRegH() const;
+ int32_t VRegH() const;
+ uint16_t VRegH_45cc() const;
+ uint16_t VRegH_4rcc() const;
// Fills the given array with the 'arg' array of the instruction.
bool HasVarArgs() const;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index 40ea285..e974932 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -269,8 +269,9 @@
V(0xF7, UNUSED_F7, "unused-f7", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xF8, UNUSED_F8, "unused-f8", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \
- V(0xFA, UNUSED_FA, "unused-fa", k10x, kIndexUnknown, 0, kVerifyError) \
- V(0xFB, UNUSED_FB, "unused-fb", k10x, kIndexUnknown, 0, kVerifyError) \
+ /* TODO(narayan): The following two entries are placeholders. */ \
+ V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexUnknown, 0, kVerifyError) \
+ V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexUnknown, 0, kVerifyError) \
V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \
diff --git a/runtime/dex_instruction_test.cc b/runtime/dex_instruction_test.cc
index 671ac0e..00c8e07 100644
--- a/runtime/dex_instruction_test.cc
+++ b/runtime/dex_instruction_test.cc
@@ -28,4 +28,96 @@
EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop));
}
+static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx,
+ uint16_t arg_regs, uint16_t* out) {
+ // A = num argument registers
+ // B = method_idx
+ // C - G = argument registers
+ // H = proto_idx
+ //
+ // op = 0xFA
+ //
+ // format:
+ // AG op BBBB FEDC HHHH
+ out[0] = 0;
+ out[0] |= (num_args << 12);
+ out[0] |= 0x00FA;
+
+ out[1] = method_idx;
+ out[2] = arg_regs;
+ out[3] = proto_idx;
+}
+
+static void Build4rcc(uint16_t num_args, uint16_t method_idx, uint16_t proto_idx,
+ uint16_t arg_regs_start, uint16_t* out) {
+ // A = num argument registers
+ // B = method_idx
+ // C = first argument register
+ // H = proto_idx
+ //
+ // op = 0xFB
+ //
+ // format:
+ // AA op BBBB CCCC HHHH
+ out[0] = 0;
+ out[0] |= (num_args << 8);
+ out[0] |= 0x00FB;
+
+ out[1] = method_idx;
+ out[2] = arg_regs_start;
+ out[3] = proto_idx;
+}
+
+TEST(Instruction, PropertiesOf45cc) {
+ uint16_t instruction[4];
+ Build45cc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
+ 0xcafe /* arg_regs */, instruction);
+
+ const Instruction* ins = Instruction::At(instruction);
+ ASSERT_EQ(4u, ins->SizeInCodeUnits());
+
+ ASSERT_TRUE(ins->HasVRegA());
+ ASSERT_EQ(4, ins->VRegA());
+ ASSERT_EQ(4u, ins->VRegA_45cc());
+ ASSERT_EQ(4u, ins->VRegA_45cc(instruction[0]));
+
+ ASSERT_TRUE(ins->HasVRegB());
+ ASSERT_EQ(16, ins->VRegB());
+ ASSERT_EQ(16u, ins->VRegB_45cc());
+
+ ASSERT_TRUE(ins->HasVRegC());
+ ASSERT_EQ(0xe, ins->VRegC());
+ ASSERT_EQ(0xe, ins->VRegC_45cc());
+
+ ASSERT_TRUE(ins->HasVRegH());
+ ASSERT_EQ(32, ins->VRegH());
+ ASSERT_EQ(32, ins->VRegH_45cc());
+}
+
+TEST(Instruction, PropertiesOf4rcc) {
+ uint16_t instruction[4];
+ Build4rcc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
+ 0xcafe /* arg_regs */, instruction);
+
+ const Instruction* ins = Instruction::At(instruction);
+ ASSERT_EQ(4u, ins->SizeInCodeUnits());
+
+ ASSERT_TRUE(ins->HasVRegA());
+ ASSERT_EQ(4, ins->VRegA());
+ ASSERT_EQ(4u, ins->VRegA_4rcc());
+ ASSERT_EQ(4u, ins->VRegA_4rcc(instruction[0]));
+
+ ASSERT_TRUE(ins->HasVRegB());
+ ASSERT_EQ(16, ins->VRegB());
+ ASSERT_EQ(16u, ins->VRegB_4rcc());
+
+ ASSERT_TRUE(ins->HasVRegC());
+ ASSERT_EQ(0xcafe, ins->VRegC());
+ ASSERT_EQ(0xcafe, ins->VRegC_4rcc());
+
+ ASSERT_TRUE(ins->HasVRegH());
+ ASSERT_EQ(32, ins->VRegH());
+ ASSERT_EQ(32, ins->VRegH_4rcc());
+}
+
} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 3623db2..a6349fc 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -2322,9 +2322,12 @@
inst = inst->Next_2xx();
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_F3 ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
+ case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
+ case Instruction::INVOKE_POLYMORPHIC:
+ case Instruction::INVOKE_POLYMORPHIC_RANGE:
UnexpectedOpcode(inst, shadow_frame);
}
} while (!interpret_one_instruction);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index daf5ec4..40f12e9 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3331,9 +3331,12 @@
/* These should never appear during verification. */
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_F3 ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
+ case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
+ case Instruction::INVOKE_POLYMORPHIC:
+ case Instruction::INVOKE_POLYMORPHIC_RANGE:
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
break;
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index f791adf..f6713a2 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -473,7 +473,7 @@
}
/**
- * Test that the `-1` constant is not synthesized in a register and that we
+ * ARM/ARM64: Test that the `-1` constant is not synthesized in a register and that we
* instead simply switch between `add` and `sub` instructions with the
* constant embedded.
* We need two uses (or more) of the constant because the compiler always
@@ -491,10 +491,137 @@
/// CHECK: sub x{{\d+}}, x{{\d+}}, #0x1
/// CHECK: add x{{\d+}}, x{{\d+}}, #0x1
+ /// CHECK-START-ARM: long Main.addM1(long) register (after)
+ /// CHECK: <<Arg:j\d+>> ParameterValue
+ /// CHECK: <<ConstM1:j\d+>> LongConstant -1
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: Add [<<Arg>>,<<ConstM1>>]
+ /// CHECK: Sub [<<Arg>>,<<ConstM1>>]
+
+ /// CHECK-START-ARM: long Main.addM1(long) disassembly (after)
+ /// CHECK: <<Arg:j\d+>> ParameterValue
+ /// CHECK: <<ConstM1:j\d+>> LongConstant -1
+ /// CHECK: Add [<<Arg>>,<<ConstM1>>]
+ /// CHECK-NEXT: subs r{{\d+}}, #1
+ /// CHECK-NEXT: adc r{{\d+}}, r{{\d+}}, #-1
+ /// CHECK: Sub [<<Arg>>,<<ConstM1>>]
+ /// CHECK-NEXT: adds r{{\d+}}, #1
+ /// CHECK-NEXT: adc r{{\d+}}, r{{\d+}}, #0
+
public static long addM1(long arg) {
return (arg + (-1)) | (arg - (-1));
}
+ /**
+ * ARM: Test that some long constants are not synthesized in a register for add-long.
+ * Also test some negative cases where we do synthetize constants in registers.
+ */
+
+ /// CHECK-START-ARM: long Main.addLongConstants(long) disassembly (after)
+ /// CHECK: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<ConstA:j\d+>> LongConstant 4486007727657233
+ /// CHECK-DAG: <<ConstB:j\d+>> LongConstant 4486011735248896
+ /// CHECK-DAG: <<ConstC:j\d+>> LongConstant -1071856711330889728
+ /// CHECK-DAG: <<ConstD:j\d+>> LongConstant 17587891077120
+ /// CHECK-DAG: <<ConstE:j\d+>> LongConstant -8808977924096
+ /// CHECK-DAG: <<ConstF:j\d+>> LongConstant 17587891077121
+ /// CHECK-DAG: <<ConstG:j\d+>> LongConstant 4095
+ /// CHECK: Add [<<Arg>>,<<ConstA>>]
+ /// CHECK-NEXT: adds r{{\d+}}, r{{\d+}}, #286331153
+ /// CHECK-NEXT: adc r{{\d+}}, r{{\d+}}, #1044480
+ /// CHECK: Add [<<Arg>>,<<ConstB>>]
+ /// CHECK-NEXT: subs r{{\d+}}, r{{\d+}}, #1044480
+ /// CHECK-NEXT: adc r{{\d+}}, r{{\d+}}, #1044480
+ /// CHECK: Add [<<Arg>>,<<ConstC>>]
+ /// CHECK-NEXT: subs r{{\d+}}, r{{\d+}}, #16711680
+ /// CHECK-NEXT: sbc r{{\d+}}, r{{\d+}}, #249561088
+ /// CHECK: Add [<<Arg>>,<<ConstD>>]
+ // There may or may not be a MOV here.
+ /// CHECK: addw r{{\d+}}, r{{\d+}}, #4095
+ /// CHECK: Add [<<Arg>>,<<ConstE>>]
+ // There may or may not be a MOV here.
+ /// CHECK: subw r{{\d+}}, r{{\d+}}, #2051
+ /// CHECK: Add [<<Arg>>,<<ConstF>>]
+ /// CHECK-NEXT: adds{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK-NEXT: adc{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK: Add [<<Arg>>,<<ConstG>>]
+ /// CHECK-NEXT: adds{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK-NEXT: adc{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+
+ public static long addLongConstants(long arg) {
+ return
+ // Modified immediates.
+ (arg + 0x000ff00011111111L) ^ // 4486007727657233
+ // Modified immediates high and -low.
+ (arg + 0x000ff000fff01000L) ^ // 4486011735248896
+ // Modified immediates ~high and -low.
+ (arg + 0xf11fffffff010000L) ^ // -1071856711330889728
+ // Low word 0 (no carry), high is imm12.
+ (arg + 0x00000fff00000000L) ^ // 17587891077120
+ // Low word 0 (no carry), -high is imm12.
+ (arg + 0xfffff7fd00000000L) ^ // -8808977924096
+ // Cannot embed imm12 in ADC/SBC for high word.
+ (arg + 0x00000fff00000001L) ^ // 17587891077121
+ // Cannot embed imm12 in ADDS/SUBS for low word (need to set flags).
+ (arg + 0x0000000000000fffL) ^ // 4095
+ arg;
+ }
+
+ /**
+ * ARM: Test that some long constants are not synthesized in a register for add-long.
+ * Also test some negative cases where we do synthetize constants in registers.
+ */
+
+ /// CHECK-START-ARM: long Main.subLongConstants(long) disassembly (after)
+ /// CHECK: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<ConstA:j\d+>> LongConstant 4486007727657233
+ /// CHECK-DAG: <<ConstB:j\d+>> LongConstant 4486011735248896
+ /// CHECK-DAG: <<ConstC:j\d+>> LongConstant -1071856711330889728
+ /// CHECK-DAG: <<ConstD:j\d+>> LongConstant 17587891077120
+ /// CHECK-DAG: <<ConstE:j\d+>> LongConstant -8808977924096
+ /// CHECK-DAG: <<ConstF:j\d+>> LongConstant 17587891077121
+ /// CHECK-DAG: <<ConstG:j\d+>> LongConstant 4095
+ /// CHECK: Sub [<<Arg>>,<<ConstA>>]
+ /// CHECK-NEXT: subs r{{\d+}}, r{{\d+}}, #286331153
+ /// CHECK-NEXT: sbc r{{\d+}}, r{{\d+}}, #1044480
+ /// CHECK: Sub [<<Arg>>,<<ConstB>>]
+ /// CHECK-NEXT: adds r{{\d+}}, r{{\d+}}, #1044480
+ /// CHECK-NEXT: sbc r{{\d+}}, r{{\d+}}, #1044480
+ /// CHECK: Sub [<<Arg>>,<<ConstC>>]
+ /// CHECK-NEXT: adds r{{\d+}}, r{{\d+}}, #16711680
+ /// CHECK-NEXT: adc r{{\d+}}, r{{\d+}}, #249561088
+ /// CHECK: Sub [<<Arg>>,<<ConstD>>]
+ // There may or may not be a MOV here.
+ /// CHECK: subw r{{\d+}}, r{{\d+}}, #4095
+ /// CHECK: Sub [<<Arg>>,<<ConstE>>]
+ // There may or may not be a MOV here.
+ /// CHECK: addw r{{\d+}}, r{{\d+}}, #2051
+ /// CHECK: Sub [<<Arg>>,<<ConstF>>]
+ /// CHECK-NEXT: subs{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK-NEXT: sbc{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK: Sub [<<Arg>>,<<ConstG>>]
+ /// CHECK-NEXT: subs{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK-NEXT: sbc{{(\.w)?}} r{{\d+}}, r{{\d+}}, r{{\d+}}
+
+ public static long subLongConstants(long arg) {
+ return
+ // Modified immediates.
+ (arg - 0x000ff00011111111L) ^ // 4486007727657233
+ // Modified immediates high and -low.
+ (arg - 0x000ff000fff01000L) ^ // 4486011735248896
+ // Modified immediates ~high and -low.
+ (arg - 0xf11fffffff010000L) ^ // -1071856711330889728
+ // Low word 0 (no carry), high is imm12.
+ (arg - 0x00000fff00000000L) ^ // 17587891077120
+ // Low word 0 (no carry), -high is imm12.
+ (arg - 0xfffff7fd00000000L) ^ // -8808977924096
+ // Cannot embed imm12 in ADC/SBC for high word.
+ (arg - 0x00000fff00000001L) ^ // 17587891077121
+ // Cannot embed imm12 in ADDS/SUBS for low word (need to set flags).
+ (arg - 0x0000000000000fffL) ^ // 4095
+ arg;
+ }
+
public static void main(String[] args) {
int arg = 0x87654321;
assertIntEquals(and255(arg), 0x21);
@@ -522,7 +649,7 @@
assertLongEquals(xor0xfffffff00000000f(longArg), 0xedcba9888765432eL);
assertLongEquals(xor0xf00000000000000f(longArg), 0xe23456788765432eL);
- assertLongEquals(14, addM1(7));
+ assertLongEquals(14L, addM1(7));
assertLongEquals(shl1(longArg), 0x2468acf10eca8642L);
assertLongEquals(shl2(longArg), 0x48d159e21d950c84L);
@@ -562,5 +689,30 @@
assertLongEquals(ushr32(~longArg), 0x00000000edcba987L);
assertLongEquals(ushr33(~longArg), 0x0000000076e5d4c3L);
assertLongEquals(ushr63(~longArg), 0x0000000000000001L);
+
+ // Test -1, 0, +1 and arbitrary constants just before and after overflow
+ // on low word in subexpressions of addLongConstants()/subLongConstants(),
+ // so that we check that we carry the overflow correctly to the high word.
+ // For example
+ // 0x111eeeeeeee+0x000ff00011111111 = 0x000ff111ffffffff (carry=0),
+ // 0x111eeeeeeef+0x000ff00011111111 = 0x000ff11200000000 (carry=1).
+ assertLongEquals(0xf11ff7fdee1e1111L, addLongConstants(0xffffffffffffffffL));
+ assertLongEquals(0xee0080211e00eefL, addLongConstants(0x0L));
+ assertLongEquals(0xee0080211e01111L, addLongConstants(0x1L));
+ assertLongEquals(0xedff81c12201113L, addLongConstants(0x111eeeeeeeeL));
+ assertLongEquals(0xedff81feddfeef1L, addLongConstants(0x111eeeeeeefL));
+ assertLongEquals(0xedff83e11c1f111L, addLongConstants(0x222000fefffL));
+ assertLongEquals(0xedff83fee3e0eefL, addLongConstants(0x222000ff000L));
+ assertLongEquals(0xedff805edfe1111L, addLongConstants(0x33300feffffL));
+ assertLongEquals(0xedff80412000eefL, addLongConstants(0x33300ff0000L));
+ assertLongEquals(0xee0080211e00eefL, subLongConstants(0xffffffffffffffffL));
+ assertLongEquals(0xf11ff7fdee1e1111L, subLongConstants(0x0L));
+ assertLongEquals(0xf11ff7fc11e1eef3L, subLongConstants(0x1L));
+ assertLongEquals(0xee0080412201113L, subLongConstants(0x44411111111L));
+ assertLongEquals(0xee0080412201111L, subLongConstants(0x44411111112L));
+ assertLongEquals(0xee0080e11c1f111L, subLongConstants(0x555fff01000L));
+ assertLongEquals(0xee0080e11c1eef3L, subLongConstants(0x555fff01001L));
+ assertLongEquals(0xee0080dedfe1111L, subLongConstants(0x666ff010000L));
+ assertLongEquals(0xee0080dedffeef3L, subLongConstants(0x666ff010001L));
}
}