diff options
Diffstat (limited to 'deps/v8/src/arm/assembler-arm.cc')
-rw-r--r-- | deps/v8/src/arm/assembler-arm.cc | 275 |
1 files changed, 165 insertions, 110 deletions
diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index 30a8830c9e..47ea0e2066 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -77,6 +77,9 @@ static unsigned CpuFeaturesImpliedByCompiler() { #endif // defined(CAN_USE_ARMV7_INSTRUCTIONS) && defined(__VFP_FP__) // && !defined(__SOFTFP__) #endif // _arm__ + if (answer & (1u << ARMv7)) { + answer |= 1u << UNALIGNED_ACCESSES; + } return answer; } @@ -110,6 +113,14 @@ void CpuFeatures::Probe() { if (FLAG_enable_armv7) { supported_ |= 1u << ARMv7; } + + if (FLAG_enable_sudiv) { + supported_ |= 1u << SUDIV; + } + + if (FLAG_enable_movw_movt) { + supported_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS; + } #else // __arm__ // Probe for additional features not already known to be available. if (!IsSupported(VFP3) && OS::ArmCpuHasFeature(VFP3)) { @@ -125,6 +136,19 @@ void CpuFeatures::Probe() { found_by_runtime_probing_ |= 1u << ARMv7; } + if (!IsSupported(SUDIV) && OS::ArmCpuHasFeature(SUDIV)) { + found_by_runtime_probing_ |= 1u << SUDIV; + } + + if (!IsSupported(UNALIGNED_ACCESSES) && OS::ArmCpuHasFeature(ARMv7)) { + found_by_runtime_probing_ |= 1u << UNALIGNED_ACCESSES; + } + + if (OS::GetCpuImplementer() == QUALCOMM_IMPLEMENTER && + OS::ArmCpuHasFeature(ARMv7)) { + found_by_runtime_probing_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS; + } + supported_ |= found_by_runtime_probing_; #endif @@ -294,46 +318,11 @@ const Instr kLdrStrInstrArgumentMask = 0x0000ffff; const Instr kLdrStrOffsetMask = 0x00000fff; -// Spare buffer. -static const int kMinimalBufferSize = 4*KB; - - -Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) - : AssemblerBase(arg_isolate), +Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size) + : AssemblerBase(isolate, buffer, buffer_size), recorded_ast_id_(TypeFeedbackId::None()), - positions_recorder_(this), - emit_debug_code_(FLAG_debug_code), - predictable_code_size_(false) { - if (buffer == NULL) { - // Do our own buffer management. - if (buffer_size <= kMinimalBufferSize) { - buffer_size = kMinimalBufferSize; - - if (isolate()->assembler_spare_buffer() != NULL) { - buffer = isolate()->assembler_spare_buffer(); - isolate()->set_assembler_spare_buffer(NULL); - } - } - if (buffer == NULL) { - buffer_ = NewArray<byte>(buffer_size); - } else { - buffer_ = static_cast<byte*>(buffer); - } - buffer_size_ = buffer_size; - own_buffer_ = true; - - } else { - // Use externally provided buffer instead. - ASSERT(buffer_size > 0); - buffer_ = static_cast<byte*>(buffer); - buffer_size_ = buffer_size; - own_buffer_ = false; - } - - // Set up buffer pointers. - ASSERT(buffer_ != NULL); - pc_ = buffer_; - reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); + positions_recorder_(this) { + reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_); num_pending_reloc_info_ = 0; next_buffer_check_ = 0; const_pool_blocked_nesting_ = 0; @@ -346,14 +335,6 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) Assembler::~Assembler() { ASSERT(const_pool_blocked_nesting_ == 0); - if (own_buffer_) { - if (isolate()->assembler_spare_buffer() == NULL && - buffer_size_ == kMinimalBufferSize) { - isolate()->set_assembler_spare_buffer(buffer_); - } else { - DeleteArray(buffer_); - } - } } @@ -715,12 +696,6 @@ void Assembler::next(Label* L) { } -static Instr EncodeMovwImmediate(uint32_t immediate) { - ASSERT(immediate < 0x10000); - return ((immediate & 0xf000) << 4) | (immediate & 0xfff); -} - - // Low-level code emission routines depending on the addressing mode. // If this returns true then you have to use the rotate_imm and immed_8 // that it returns, because it may have already changed the instruction @@ -785,7 +760,7 @@ static bool fits_shifter(uint32_t imm32, // if they can be encoded in the ARM's 12 bits of immediate-offset instruction // space. There is no guarantee that the relocated location can be similarly // encoded. -bool Operand::must_use_constant_pool(const Assembler* assembler) const { +bool Operand::must_output_reloc_info(const Assembler* assembler) const { if (rmode_ == RelocInfo::EXTERNAL_REFERENCE) { #ifdef DEBUG if (!Serializer::enabled()) { @@ -801,25 +776,28 @@ bool Operand::must_use_constant_pool(const Assembler* assembler) const { } +static bool use_movw_movt(const Operand& x, const Assembler* assembler) { + if (Assembler::use_immediate_embedded_pointer_loads(assembler)) { + return true; + } + if (x.must_output_reloc_info(assembler)) { + return false; + } + return CpuFeatures::IsSupported(ARMv7); +} + + bool Operand::is_single_instruction(const Assembler* assembler, Instr instr) const { if (rm_.is_valid()) return true; uint32_t dummy1, dummy2; - if (must_use_constant_pool(assembler) || + if (must_output_reloc_info(assembler) || !fits_shifter(imm32_, &dummy1, &dummy2, &instr)) { // The immediate operand cannot be encoded as a shifter operand, or use of // constant pool is required. For a mov instruction not setting the // condition code additional instruction conventions can be used. if ((instr & ~kCondMask) == 13*B21) { // mov, S not set - if (must_use_constant_pool(assembler) || - !CpuFeatures::IsSupported(ARMv7)) { - // mov instruction will be an ldr from constant pool (one instruction). - return true; - } else { - // mov instruction will be a mov or movw followed by movt (two - // instructions). - return false; - } + return !use_movw_movt(*this, assembler); } else { // If this is not a mov or mvn instruction there will always an additional // instructions - either mov or ldr. The mov might actually be two @@ -835,6 +813,29 @@ bool Operand::is_single_instruction(const Assembler* assembler, } +void Assembler::move_32_bit_immediate(Condition cond, + Register rd, + SBit s, + const Operand& x) { + if (rd.code() != pc.code() && s == LeaveCC) { + if (use_movw_movt(x, this)) { + if (x.must_output_reloc_info(this)) { + RecordRelocInfo(x.rmode_, x.imm32_, DONT_USE_CONSTANT_POOL); + // Make sure the movw/movt doesn't get separated. + BlockConstPoolFor(2); + } + emit(cond | 0x30*B20 | rd.code()*B12 | + EncodeMovwImmediate(x.imm32_ & 0xffff)); + movt(rd, static_cast<uint32_t>(x.imm32_) >> 16, cond); + return; + } + } + + RecordRelocInfo(x.rmode_, x.imm32_, USE_CONSTANT_POOL); + ldr(rd, MemOperand(pc, 0), cond); +} + + void Assembler::addrmod1(Instr instr, Register rn, Register rd, @@ -845,7 +846,7 @@ void Assembler::addrmod1(Instr instr, // Immediate. uint32_t rotate_imm; uint32_t immed_8; - if (x.must_use_constant_pool(this) || + if (x.must_output_reloc_info(this) || !fits_shifter(x.imm32_, &rotate_imm, &immed_8, &instr)) { // The immediate operand cannot be encoded as a shifter operand, so load // it first to register ip and change the original instruction to use ip. @@ -854,24 +855,19 @@ void Assembler::addrmod1(Instr instr, CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed Condition cond = Instruction::ConditionField(instr); if ((instr & ~kCondMask) == 13*B21) { // mov, S not set - if (x.must_use_constant_pool(this) || - !CpuFeatures::IsSupported(ARMv7)) { - RecordRelocInfo(x.rmode_, x.imm32_); - ldr(rd, MemOperand(pc, 0), cond); - } else { - // Will probably use movw, will certainly not use constant pool. - mov(rd, Operand(x.imm32_ & 0xffff), LeaveCC, cond); - movt(rd, static_cast<uint32_t>(x.imm32_) >> 16, cond); - } + move_32_bit_immediate(cond, rd, LeaveCC, x); } else { - // If this is not a mov or mvn instruction we may still be able to avoid - // a constant pool entry by using mvn or movw. - if (!x.must_use_constant_pool(this) && - (instr & kMovMvnMask) != kMovMvnPattern) { - mov(ip, x, LeaveCC, cond); - } else { - RecordRelocInfo(x.rmode_, x.imm32_); + if ((instr & kMovMvnMask) == kMovMvnPattern) { + // Moves need to use a constant pool entry. + RecordRelocInfo(x.rmode_, x.imm32_, USE_CONSTANT_POOL); ldr(ip, MemOperand(pc, 0), cond); + } else if (x.must_output_reloc_info(this)) { + // Otherwise, use most efficient form of fetching from constant pool. + move_32_bit_immediate(cond, ip, LeaveCC, x); + } else { + // If this is not a mov or mvn instruction we may still be able to + // avoid a constant pool entry by using mvn or movw. + mov(ip, x, LeaveCC, cond); } addrmod1(instr, rn, rd, Operand(ip)); } @@ -1178,6 +1174,9 @@ void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) { void Assembler::movw(Register reg, uint32_t immediate, Condition cond) { ASSERT(immediate < 0x10000); + // May use movw if supported, but on unsupported platforms will try to use + // equivalent rotated immed_8 value and other tricks before falling back to a + // constant pool load. mov(reg, Operand(immediate), LeaveCC, cond); } @@ -1207,6 +1206,22 @@ void Assembler::mla(Register dst, Register src1, Register src2, Register srcA, } +void Assembler::mls(Register dst, Register src1, Register src2, Register srcA, + Condition cond) { + ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc)); + emit(cond | B22 | B21 | dst.code()*B16 | srcA.code()*B12 | + src2.code()*B8 | B7 | B4 | src1.code()); +} + + +void Assembler::sdiv(Register dst, Register src1, Register src2, + Condition cond) { + ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc)); + emit(cond | B26 | B25| B24 | B20 | dst.code()*B16 | 0xf * B12 | + src2.code()*B8 | B4 | src1.code()); +} + + void Assembler::mul(Register dst, Register src1, Register src2, SBit s, Condition cond) { ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc)); @@ -1391,7 +1406,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src, // Immediate. uint32_t rotate_imm; uint32_t immed_8; - if (src.must_use_constant_pool(this) || + if (src.must_output_reloc_info(this) || !fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) { // Immediate operand cannot be encoded, load it first to register ip. RecordRelocInfo(src.rmode_, src.imm32_); @@ -1826,7 +1841,7 @@ void Assembler::vstr(const SwVfpRegister src, const Condition cond) { ASSERT(!operand.rm().is_valid()); ASSERT(operand.am_ == Offset); - vldr(src, operand.rn(), operand.offset(), cond); + vstr(src, operand.rn(), operand.offset(), cond); } @@ -1975,6 +1990,7 @@ static bool FitsVMOVDoubleImmediate(double d, uint32_t *encoding) { void Assembler::vmov(const DwVfpRegister dst, double imm, + const Register scratch, const Condition cond) { // Dd = immediate // Instruction details available in ARM DDI 0406B, A8-640. @@ -1989,22 +2005,22 @@ void Assembler::vmov(const DwVfpRegister dst, // using vldr from a constant pool. uint32_t lo, hi; DoubleAsTwoUInt32(imm, &lo, &hi); + mov(ip, Operand(lo)); - if (lo == hi) { - // If the lo and hi parts of the double are equal, the literal is easier - // to create. This is the case with 0.0. - mov(ip, Operand(lo)); - vmov(dst, ip, ip); - } else { + if (scratch.is(no_reg)) { // Move the low part of the double into the lower of the corresponsing S // registers of D register dst. - mov(ip, Operand(lo)); vmov(dst.low(), ip, cond); // Move the high part of the double into the higher of the corresponsing S // registers of D register dst. mov(ip, Operand(hi)); vmov(dst.high(), ip, cond); + } else { + // Move the low and high parts of the double to a D register in one + // instruction. + mov(scratch, Operand(hi)); + vmov(dst, ip, scratch, cond); } } } @@ -2333,6 +2349,20 @@ void Assembler::vmul(const DwVfpRegister dst, } +void Assembler::vmla(const DwVfpRegister dst, + const DwVfpRegister src1, + const DwVfpRegister src2, + const Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8-892. + // cond(31-28) | 11100(27-23) | D=?(22) | 00(21-20) | Vn(19-16) | + // Vd(15-12) | 101(11-9) | sz(8)=1 | N=?(7) | op(6)=0 | M=?(5) | 0(4) | + // Vm(3-0) + unsigned x = (cond | 0x1C*B23 | src1.code()*B16 | + dst.code()*B12 | 0x5*B9 | B8 | src2.code()); + emit(x); +} + + void Assembler::vdiv(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, @@ -2408,15 +2438,35 @@ void Assembler::vsqrt(const DwVfpRegister dst, // Pseudo instructions. void Assembler::nop(int type) { - // This is mov rx, rx. - ASSERT(0 <= type && type <= 14); // mov pc, pc is not a nop. + // ARMv6{K/T2} and v7 have an actual NOP instruction but it serializes + // some of the CPU's pipeline and has to issue. Older ARM chips simply used + // MOV Rx, Rx as NOP and it performs better even in newer CPUs. + // We therefore use MOV Rx, Rx, even on newer CPUs, and use Rx to encode + // a type. + ASSERT(0 <= type && type <= 14); // mov pc, pc isn't a nop. emit(al | 13*B21 | type*B12 | type); } +bool Assembler::IsMovT(Instr instr) { + instr &= ~(((kNumberOfConditions - 1) << 28) | // Mask off conditions + ((kNumRegisters-1)*B12) | // mask out register + EncodeMovwImmediate(0xFFFF)); // mask out immediate value + return instr == 0x34*B20; +} + + +bool Assembler::IsMovW(Instr instr) { + instr &= ~(((kNumberOfConditions - 1) << 28) | // Mask off conditions + ((kNumRegisters-1)*B12) | // mask out destination + EncodeMovwImmediate(0xFFFF)); // mask out immediate value + return instr == 0x30*B20; +} + + bool Assembler::IsNop(Instr instr, int type) { + ASSERT(0 <= type && type <= 14); // mov pc, pc isn't a nop. // Check for mov rx, rx where x = type. - ASSERT(0 <= type && type <= 14); // mov pc, pc is not a nop. return instr == (al | 13*B21 | type*B12 | type); } @@ -2532,18 +2582,21 @@ void Assembler::dd(uint32_t data) { } -void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { +void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data, + UseConstantPoolMode mode) { // We do not try to reuse pool constants. RelocInfo rinfo(pc_, rmode, data, NULL); if (((rmode >= RelocInfo::JS_RETURN) && (rmode <= RelocInfo::DEBUG_BREAK_SLOT)) || - (rmode == RelocInfo::CONST_POOL)) { + (rmode == RelocInfo::CONST_POOL) || + mode == DONT_USE_CONSTANT_POOL) { // Adjust code for new modes. ASSERT(RelocInfo::IsDebugBreakSlot(rmode) || RelocInfo::IsJSReturn(rmode) || RelocInfo::IsComment(rmode) || RelocInfo::IsPosition(rmode) - || RelocInfo::IsConstPool(rmode)); + || RelocInfo::IsConstPool(rmode) + || mode == DONT_USE_CONSTANT_POOL); // These modes do not need an entry in the constant pool. } else { ASSERT(num_pending_reloc_info_ < kMaxNumPendingRelocInfo); @@ -2648,9 +2701,9 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { b(&after_pool); } - // Put down constant pool marker "Undefined instruction" as specified by - // A5.6 (ARMv7) Instruction set encoding. - emit(kConstantPoolMarker | num_pending_reloc_info_); + // Put down constant pool marker "Undefined instruction". + emit(kConstantPoolMarker | + EncodeConstantPoolLength(num_pending_reloc_info_)); // Emit constant pool entries. for (int i = 0; i < num_pending_reloc_info_; i++) { @@ -2662,17 +2715,19 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { Instr instr = instr_at(rinfo.pc()); // Instruction to patch must be 'ldr rd, [pc, #offset]' with offset == 0. - ASSERT(IsLdrPcImmediateOffset(instr) && - GetLdrRegisterImmediateOffset(instr) == 0); - - int delta = pc_ - rinfo.pc() - kPcLoadDelta; - // 0 is the smallest delta: - // ldr rd, [pc, #0] - // constant pool marker - // data - ASSERT(is_uint12(delta)); - - instr_at_put(rinfo.pc(), SetLdrRegisterImmediateOffset(instr, delta)); + if (IsLdrPcImmediateOffset(instr) && + GetLdrRegisterImmediateOffset(instr) == 0) { + int delta = pc_ - rinfo.pc() - kPcLoadDelta; + // 0 is the smallest delta: + // ldr rd, [pc, #0] + // constant pool marker + // data + ASSERT(is_uint12(delta)); + + instr_at_put(rinfo.pc(), SetLdrRegisterImmediateOffset(instr, delta)); + } else { + ASSERT(IsMovW(instr)); + } emit(rinfo.data()); } |