Commit d5b29f43 authored by Pierre Langlois's avatar Pierre Langlois Committed by Commit Bot

[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/685014Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Pierre Langlois <pierre.langlois@arm.com>
Cr-Commit-Position: refs/heads/master@{#48491}
parent a0cde811
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment