diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc index 9799950728..eccb4d22bd 100644 --- a/src/arm/assembler-arm.cc +++ b/src/arm/assembler-arm.cc @@ -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 diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index c2de64467f..64f26b6079 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc @@ -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); } diff --git a/test/cctest/test-assembler-arm.cc b/test/cctest/test-assembler-arm.cc index 5f405548f0..324e4530f4 100644 --- a/test/cctest/test-assembler-arm.cc +++ b/test/cctest/test-assembler-arm.cc @@ -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 = + isolate->factory()->NewCode(desc, Code::STUB, Handle()); +#ifdef DEBUG + OFStream os(stdout); + code->Print(os); +#endif + F1 f = FUNCTION_CAST(code->entry()); + uint32_t res = + reinterpret_cast(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 = + isolate->factory()->NewCode(desc, Code::STUB, Handle()); +#ifdef DEBUG + OFStream os(stdout); + code->Print(os); +#endif + F1 f = FUNCTION_CAST(code->entry()); + uint32_t res = + reinterpret_cast(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 = + isolate->factory()->NewCode(desc, Code::STUB, Handle()); +#ifdef DEBUG + OFStream os(stdout); + code->Print(os); +#endif + F1 f = FUNCTION_CAST(code->entry()); + uint32_t res = + reinterpret_cast(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 diff --git a/test/cctest/test-disasm-arm.cc b/test/cctest/test-disasm-arm.cc index c8c9daa0e2..253daefa6c 100644 --- a/test/cctest/test-disasm-arm.cc +++ b/test/cctest/test-disasm-arm.cc @@ -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