[arm] Support splitting add with immediate instructions
When an immediate does not fit an add instruction we use a temporary register to hold the value, using movw/movt to encode it. However, in order to remove a use of r9 in TurboFan's code generator, we need to cope with no scratch registers being available. That is to say that the destination and source registers are the same, and `ip` is not available to use. In this case, we can split an add instruction into a sequence of additions: ``` UseScratchRegisterScope temps(...); Register my_scratch = temps.Acquire(); __ add(r0, r0, Operand(0xabcd); // add r0, r0, #0xcd // add r0, r0, #0xab00 ``` As a drive-by fix, make the disassembler test fail if we expected a different number of instructions generated. Bug: v8:6553 Change-Id: Ib7fcc765d28bccafe39257f47cd73f922c5873bf Reviewed-on: https://chromium-review.googlesource.com/685014 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Pierre Langlois <pierre.langlois@arm.com> Cr-Commit-Position: refs/heads/master@{#48491}
This commit is contained in:
parent
a0cde81112
commit
d5b29f43de
@ -1207,6 +1207,26 @@ void Assembler::AddrMode1(Instr instr, Register rd, Register rn,
|
||||
// pool only for a MOV instruction which does not set the flags.
|
||||
DCHECK(!rn.is_valid());
|
||||
Move32BitImmediate(rd, x, cond);
|
||||
} else if ((opcode == ADD) && !set_flags && (rd == rn) &&
|
||||
(scratch_register_list_ == 0)) {
|
||||
// Split the operation into a sequence of additions if we cannot use a
|
||||
// scratch register. In this case, we cannot re-use rn and the assembler
|
||||
// does not have any scratch registers to spare.
|
||||
uint32_t imm = x.immediate();
|
||||
do {
|
||||
// The immediate encoding format is composed of 8 bits of data and 4
|
||||
// bits encoding a rotation. Each of the 16 possible rotations accounts
|
||||
// for a rotation by an even number.
|
||||
// 4 bits -> 16 rotations possible
|
||||
// -> 16 rotations of 2 bits each fits in a 32-bit value.
|
||||
// This means that finding the even number of trailing zeroes of the
|
||||
// immediate allows us to more efficiently split it:
|
||||
int trailing_zeroes = base::bits::CountTrailingZeros(imm) & ~1u;
|
||||
uint32_t mask = (0xff << trailing_zeroes);
|
||||
add(rd, rd, Operand(imm & mask), LeaveCC, cond);
|
||||
imm = imm & ~mask;
|
||||
} while (!ImmediateFitsAddrMode1Instruction(imm));
|
||||
add(rd, rd, Operand(imm), LeaveCC, cond);
|
||||
} else {
|
||||
// The immediate operand cannot be encoded as a shifter operand, so load
|
||||
// it first to a scratch register and change the original instruction to
|
||||
|
@ -2837,14 +2837,15 @@ void CodeGenerator::AssembleConstructFrame() {
|
||||
// If the frame is bigger than the stack, we throw the stack overflow
|
||||
// exception unconditionally. Thereby we can avoid the integer overflow
|
||||
// check in the condition code.
|
||||
if (shrink_slots * kPointerSize < FLAG_stack_size * 1024) {
|
||||
__ Move(kScratchReg,
|
||||
if ((shrink_slots * kPointerSize) < (FLAG_stack_size * 1024)) {
|
||||
UseScratchRegisterScope temps(tasm());
|
||||
Register scratch = temps.Acquire();
|
||||
__ Move(scratch,
|
||||
Operand(ExternalReference::address_of_real_stack_limit(
|
||||
__ isolate())));
|
||||
__ ldr(kScratchReg, MemOperand(kScratchReg));
|
||||
__ add(kScratchReg, kScratchReg,
|
||||
Operand(shrink_slots * kPointerSize));
|
||||
__ cmp(sp, kScratchReg);
|
||||
__ ldr(scratch, MemOperand(scratch));
|
||||
__ add(scratch, scratch, Operand(shrink_slots * kPointerSize));
|
||||
__ cmp(sp, scratch);
|
||||
__ b(cs, &done);
|
||||
}
|
||||
|
||||
|
@ -3993,6 +3993,79 @@ TEST(use_scratch_register_scope) {
|
||||
CHECK_EQ(*assm.GetScratchRegisterList(), ip.bit());
|
||||
}
|
||||
|
||||
TEST(split_add_immediate) {
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
HandleScope scope(isolate);
|
||||
|
||||
{
|
||||
Assembler assm(isolate, NULL, 0);
|
||||
__ mov(r1, r0);
|
||||
// Re-use the destination as a scratch.
|
||||
__ add(r0, r1, Operand(0x12345678));
|
||||
__ blx(lr);
|
||||
|
||||
CodeDesc desc;
|
||||
assm.GetCode(isolate, &desc);
|
||||
Handle<Code> code =
|
||||
isolate->factory()->NewCode(desc, Code::STUB, Handle<Code>());
|
||||
#ifdef DEBUG
|
||||
OFStream os(stdout);
|
||||
code->Print(os);
|
||||
#endif
|
||||
F1 f = FUNCTION_CAST<F1>(code->entry());
|
||||
uint32_t res =
|
||||
reinterpret_cast<int>(CALL_GENERATED_CODE(isolate, f, 0, 0, 0, 0, 0));
|
||||
::printf("f() = 0x%x\n", res);
|
||||
CHECK_EQ(0x12345678, res);
|
||||
}
|
||||
|
||||
{
|
||||
Assembler assm(isolate, NULL, 0);
|
||||
// Use ip as a scratch.
|
||||
__ add(r0, r0, Operand(0x12345678));
|
||||
__ blx(lr);
|
||||
|
||||
CodeDesc desc;
|
||||
assm.GetCode(isolate, &desc);
|
||||
Handle<Code> code =
|
||||
isolate->factory()->NewCode(desc, Code::STUB, Handle<Code>());
|
||||
#ifdef DEBUG
|
||||
OFStream os(stdout);
|
||||
code->Print(os);
|
||||
#endif
|
||||
F1 f = FUNCTION_CAST<F1>(code->entry());
|
||||
uint32_t res =
|
||||
reinterpret_cast<int>(CALL_GENERATED_CODE(isolate, f, 0, 0, 0, 0, 0));
|
||||
::printf("f() = 0x%x\n", res);
|
||||
CHECK_EQ(0x12345678, res);
|
||||
}
|
||||
|
||||
{
|
||||
Assembler assm(isolate, NULL, 0);
|
||||
UseScratchRegisterScope temps(&assm);
|
||||
Register reserved = temps.Acquire();
|
||||
USE(reserved);
|
||||
// If ip is not available, split the operation into multiple additions.
|
||||
__ add(r0, r0, Operand(0x12345678));
|
||||
__ blx(lr);
|
||||
|
||||
CodeDesc desc;
|
||||
assm.GetCode(isolate, &desc);
|
||||
Handle<Code> code =
|
||||
isolate->factory()->NewCode(desc, Code::STUB, Handle<Code>());
|
||||
#ifdef DEBUG
|
||||
OFStream os(stdout);
|
||||
code->Print(os);
|
||||
#endif
|
||||
F1 f = FUNCTION_CAST<F1>(code->entry());
|
||||
uint32_t res =
|
||||
reinterpret_cast<int>(CALL_GENERATED_CODE(isolate, f, 0, 0, 0, 0, 0));
|
||||
::printf("f() = 0x%x\n", res);
|
||||
CHECK_EQ(0x12345678, res);
|
||||
}
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
} // namespace test_assembler_arm
|
||||
|
@ -73,6 +73,12 @@ bool DisassembleAndCompare(byte* begin, S... expected_strings) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fail after printing expected disassembly if we expected a different number
|
||||
// of instructions.
|
||||
if (disassembly.size() != expected_disassembly.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return test_passed;
|
||||
}
|
||||
|
||||
@ -1579,5 +1585,35 @@ TEST(LoadStoreExclusive) {
|
||||
VERIFY_RUN();
|
||||
}
|
||||
|
||||
TEST(SplitAddImmediate) {
|
||||
SET_UP();
|
||||
|
||||
// Re-use the destination as a scratch.
|
||||
COMPARE(add(r0, r1, Operand(0x12345678)),
|
||||
"e3050678 movw r0, #22136",
|
||||
"e3410234 movt r0, #4660",
|
||||
"e0810000 add r0, r1, r0");
|
||||
|
||||
// Use ip as a scratch.
|
||||
COMPARE(add(r0, r0, Operand(0x12345678)),
|
||||
"e305c678 movw ip, #22136",
|
||||
"e341c234 movt ip, #4660",
|
||||
"e080000c add r0, r0, ip");
|
||||
|
||||
// If ip is not available, split the operation into multiple additions.
|
||||
{
|
||||
UseScratchRegisterScope temps(&assm);
|
||||
Register reserved = temps.Acquire();
|
||||
USE(reserved);
|
||||
COMPARE(add(r2, r2, Operand(0x12345678)),
|
||||
"e2822f9e add r2, r2, #632",
|
||||
"e2822b15 add r2, r2, #21504",
|
||||
"e282278d add r2, r2, #36962304",
|
||||
"e2822201 add r2, r2, #268435456");
|
||||
}
|
||||
|
||||
VERIFY_RUN();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user