Merge "Instruction: Add new formats 45cc and 4rcc."
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/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));
}
}