ARM64: Introduce a version of ADR handling distant targets.
This fixes an out-of-range label error for an ADR instruction in the mozilla/data/js1_5/Regress/regress-280769-2.js test. R=ulan@chromium.org Review URL: https://codereview.chromium.org/222433002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20545 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
341ec1b50e
commit
622ddd3495
@ -2817,6 +2817,76 @@ void Assembler::PopulateConstantPool(ConstantPoolArray* constant_pool) {
|
||||
}
|
||||
|
||||
|
||||
void PatchingAssembler::MovInt64(const Register& rd, int64_t imm) {
|
||||
Label start;
|
||||
bind(&start);
|
||||
|
||||
ASSERT(rd.Is64Bits());
|
||||
ASSERT(!rd.IsSP());
|
||||
|
||||
for (unsigned i = 0; i < (rd.SizeInBits() / 16); i++) {
|
||||
uint64_t imm16 = (imm >> (16 * i)) & 0xffffL;
|
||||
movk(rd, imm16, 16 * i);
|
||||
}
|
||||
|
||||
ASSERT(SizeOfCodeGeneratedSince(&start) ==
|
||||
kMovInt64NInstrs * kInstructionSize);
|
||||
}
|
||||
|
||||
|
||||
void PatchingAssembler::PatchAdrFar(Instruction* target) {
|
||||
// The code at the current instruction should be:
|
||||
// adr rd, 0
|
||||
// nop (adr_far)
|
||||
// nop (adr_far)
|
||||
// nop (adr_far)
|
||||
// movz scratch, 0
|
||||
// add rd, rd, scratch
|
||||
|
||||
// Verify the expected code.
|
||||
Instruction* expected_adr = InstructionAt(0);
|
||||
CHECK(expected_adr->IsAdr() && (expected_adr->ImmPCRel() == 0));
|
||||
int rd_code = expected_adr->Rd();
|
||||
for (int i = 0; i < kAdrFarPatchableNNops; ++i) {
|
||||
CHECK(InstructionAt((i + 1) * kInstructionSize)->IsNop(ADR_FAR_NOP));
|
||||
}
|
||||
Instruction* expected_movz =
|
||||
InstructionAt((kAdrFarPatchableNInstrs - 2) * kInstructionSize);
|
||||
CHECK(expected_movz->IsMovz() &&
|
||||
(expected_movz->ImmMoveWide() == 0) &&
|
||||
(expected_movz->ShiftMoveWide() == 0));
|
||||
int scratch_code = expected_movz->Rd();
|
||||
Instruction* expected_add =
|
||||
InstructionAt((kAdrFarPatchableNInstrs - 1) * kInstructionSize);
|
||||
CHECK(expected_add->IsAddSubShifted() &&
|
||||
(expected_add->Mask(AddSubOpMask) == ADD) &&
|
||||
expected_add->SixtyFourBits() &&
|
||||
(expected_add->Rd() == rd_code) && (expected_add->Rn() == rd_code) &&
|
||||
(expected_add->Rm() == scratch_code) &&
|
||||
(static_cast<Shift>(expected_add->ShiftDP()) == LSL) &&
|
||||
(expected_add->ImmDPShift() == 0));
|
||||
|
||||
// Patch to load the correct address.
|
||||
Label start;
|
||||
bind(&start);
|
||||
Register rd = Register::XRegFromCode(rd_code);
|
||||
// If the target is in range, we only patch the adr. Otherwise we patch the
|
||||
// nops with fixup instructions.
|
||||
int target_offset = expected_adr->DistanceTo(target);
|
||||
if (Instruction::IsValidPCRelOffset(target_offset)) {
|
||||
adr(rd, target_offset);
|
||||
for (int i = 0; i < kAdrFarPatchableNInstrs - 2; ++i) {
|
||||
nop(ADR_FAR_NOP);
|
||||
}
|
||||
} else {
|
||||
Register scratch = Register::XRegFromCode(scratch_code);
|
||||
adr(rd, 0);
|
||||
MovInt64(scratch, target_offset);
|
||||
add(rd, rd, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_TARGET_ARCH_ARM64
|
||||
|
@ -1501,8 +1501,9 @@ class Assembler : public AssemblerBase {
|
||||
enum NopMarkerTypes {
|
||||
DEBUG_BREAK_NOP,
|
||||
INTERRUPT_CODE_NOP,
|
||||
ADR_FAR_NOP,
|
||||
FIRST_NOP_MARKER = DEBUG_BREAK_NOP,
|
||||
LAST_NOP_MARKER = INTERRUPT_CODE_NOP
|
||||
LAST_NOP_MARKER = ADR_FAR_NOP
|
||||
};
|
||||
|
||||
void nop(NopMarkerTypes n) {
|
||||
@ -2223,6 +2224,14 @@ class PatchingAssembler : public Assembler {
|
||||
size_t length = buffer_size_ - kGap;
|
||||
CPU::FlushICache(buffer_, length);
|
||||
}
|
||||
|
||||
static const int kMovInt64NInstrs = 4;
|
||||
void MovInt64(const Register& rd, int64_t imm);
|
||||
|
||||
// See definition of PatchAdrFar() for details.
|
||||
static const int kAdrFarPatchableNNops = kMovInt64NInstrs - 1;
|
||||
static const int kAdrFarPatchableNInstrs = kAdrFarPatchableNNops + 3;
|
||||
void PatchAdrFar(Instruction* target);
|
||||
};
|
||||
|
||||
|
||||
|
@ -254,11 +254,18 @@ void Instruction::SetImmPCOffsetTarget(Instruction* target) {
|
||||
|
||||
void Instruction::SetPCRelImmTarget(Instruction* target) {
|
||||
// ADRP is not supported, so 'this' must point to an ADR instruction.
|
||||
ASSERT(Mask(PCRelAddressingMask) == ADR);
|
||||
|
||||
Instr imm = Assembler::ImmPCRelAddress(DistanceTo(target));
|
||||
ASSERT(IsAdr());
|
||||
|
||||
int target_offset = DistanceTo(target);
|
||||
Instr imm;
|
||||
if (Instruction::IsValidPCRelOffset(target_offset)) {
|
||||
imm = Assembler::ImmPCRelAddress(target_offset);
|
||||
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
|
||||
} else {
|
||||
PatchingAssembler patcher(this,
|
||||
PatchingAssembler::kAdrFarPatchableNInstrs);
|
||||
patcher.PatchAdrFar(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -160,9 +160,10 @@ class Instruction {
|
||||
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
|
||||
// formed from ImmPCRelLo and ImmPCRelHi.
|
||||
int ImmPCRel() const {
|
||||
ASSERT(IsPCRelAddressing());
|
||||
int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
|
||||
int const width = ImmPCRelLo_width + ImmPCRelHi_width;
|
||||
return signed_bitextract_32(width-1, 0, offset);
|
||||
return signed_bitextract_32(width - 1, 0, offset);
|
||||
}
|
||||
|
||||
uint64_t ImmLogical();
|
||||
@ -203,6 +204,10 @@ class Instruction {
|
||||
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
|
||||
}
|
||||
|
||||
bool IsAdr() const {
|
||||
return Mask(PCRelAddressingMask) == ADR;
|
||||
}
|
||||
|
||||
bool IsLogicalImmediate() const {
|
||||
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
|
||||
}
|
||||
@ -211,6 +216,10 @@ class Instruction {
|
||||
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
|
||||
}
|
||||
|
||||
bool IsAddSubShifted() const {
|
||||
return Mask(AddSubShiftedFMask) == AddSubShiftedFixed;
|
||||
}
|
||||
|
||||
bool IsAddSubExtended() const {
|
||||
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
|
||||
}
|
||||
@ -387,6 +396,10 @@ class Instruction {
|
||||
}
|
||||
|
||||
|
||||
static const int ImmPCRelRangeBitwidth = 21;
|
||||
static bool IsValidPCRelOffset(int offset) {
|
||||
return is_int21(offset);
|
||||
}
|
||||
void SetPCRelImmTarget(Instruction* target);
|
||||
void SetBranchImmTarget(Instruction* target);
|
||||
};
|
||||
|
@ -319,13 +319,6 @@ LS_MACRO_LIST(DEFINE_FUNCTION)
|
||||
#undef DEFINE_FUNCTION
|
||||
|
||||
|
||||
void MacroAssembler::Adr(const Register& rd, Label* label) {
|
||||
ASSERT(allow_macro_instructions_);
|
||||
ASSERT(!rd.IsZero());
|
||||
adr(rd, label);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Asr(const Register& rd,
|
||||
const Register& rn,
|
||||
unsigned shift) {
|
||||
|
@ -599,6 +599,43 @@ bool MacroAssembler::NeedExtraInstructionsOrRegisterBranch(
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Adr(const Register& rd, Label* label, AdrHint hint) {
|
||||
ASSERT(allow_macro_instructions_);
|
||||
ASSERT(!rd.IsZero());
|
||||
|
||||
if (hint == kAdrNear) {
|
||||
adr(rd, label);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(hint == kAdrFar);
|
||||
UseScratchRegisterScope temps(this);
|
||||
Register scratch = temps.AcquireX();
|
||||
ASSERT(!AreAliased(rd, scratch));
|
||||
|
||||
if (label->is_bound()) {
|
||||
int label_offset = label->pos() - pc_offset();
|
||||
if (Instruction::IsValidPCRelOffset(label_offset)) {
|
||||
adr(rd, label);
|
||||
} else {
|
||||
ASSERT(label_offset <= 0);
|
||||
int min_adr_offset = -(1 << (Instruction::ImmPCRelRangeBitwidth - 1));
|
||||
adr(rd, min_adr_offset);
|
||||
Add(rd, rd, label_offset - min_adr_offset);
|
||||
}
|
||||
} else {
|
||||
InstructionAccurateScope scope(
|
||||
this, PatchingAssembler::kAdrFarPatchableNInstrs);
|
||||
adr(rd, label);
|
||||
for (int i = 0; i < PatchingAssembler::kAdrFarPatchableNNops; ++i) {
|
||||
nop(ADR_FAR_NOP);
|
||||
}
|
||||
movz(scratch, 0);
|
||||
add(rd, rd, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::B(Label* label, BranchType type, Register reg, int bit) {
|
||||
ASSERT((reg.Is(NoReg) || type >= kBranchTypeFirstUsingReg) &&
|
||||
(bit == -1 || type >= kBranchTypeFirstUsingBit));
|
||||
|
@ -255,8 +255,16 @@ class MacroAssembler : public Assembler {
|
||||
void Load(const Register& rt, const MemOperand& addr, Representation r);
|
||||
void Store(const Register& rt, const MemOperand& addr, Representation r);
|
||||
|
||||
enum AdrHint {
|
||||
// The target must be within the immediate range of adr.
|
||||
kAdrNear,
|
||||
// The target may be outside of the immediate range of adr. Additional
|
||||
// instructions may be emitted.
|
||||
kAdrFar
|
||||
};
|
||||
void Adr(const Register& rd, Label* label, AdrHint = kAdrNear);
|
||||
|
||||
// Remaining instructions are simple pass-through calls to the assembler.
|
||||
inline void Adr(const Register& rd, Label* label);
|
||||
inline void Asr(const Register& rd, const Register& rn, unsigned shift);
|
||||
inline void Asr(const Register& rd, const Register& rn, const Register& rm);
|
||||
|
||||
|
@ -1128,7 +1128,7 @@ void RegExpMacroAssemblerARM64::PushBacktrack(Label* label) {
|
||||
int target = label->pos();
|
||||
__ Mov(w10, target + Code::kHeaderSize - kHeapObjectTag);
|
||||
} else {
|
||||
__ Adr(x10, label);
|
||||
__ Adr(x10, label, MacroAssembler::kAdrFar);
|
||||
__ Sub(x10, x10, code_pointer());
|
||||
if (masm_->emit_debug_code()) {
|
||||
__ Cmp(x10, kWRegMask);
|
||||
|
@ -1686,6 +1686,71 @@ TEST(adr) {
|
||||
}
|
||||
|
||||
|
||||
TEST(adr_far) {
|
||||
INIT_V8();
|
||||
|
||||
int max_range = 1 << (Instruction::ImmPCRelRangeBitwidth - 1);
|
||||
SETUP_SIZE(max_range + 1000 * kInstructionSize);
|
||||
|
||||
Label done, fail;
|
||||
Label test_near, near_forward, near_backward;
|
||||
Label test_far, far_forward, far_backward;
|
||||
|
||||
START();
|
||||
__ Mov(x0, 0x0);
|
||||
|
||||
__ Bind(&test_near);
|
||||
__ Adr(x10, &near_forward, MacroAssembler::kAdrFar);
|
||||
__ Br(x10);
|
||||
__ B(&fail);
|
||||
__ Bind(&near_backward);
|
||||
__ Orr(x0, x0, 1 << 1);
|
||||
__ B(&test_far);
|
||||
|
||||
__ Bind(&near_forward);
|
||||
__ Orr(x0, x0, 1 << 0);
|
||||
__ Adr(x10, &near_backward, MacroAssembler::kAdrFar);
|
||||
__ Br(x10);
|
||||
|
||||
__ Bind(&test_far);
|
||||
__ Adr(x10, &far_forward, MacroAssembler::kAdrFar);
|
||||
__ Br(x10);
|
||||
__ B(&fail);
|
||||
__ Bind(&far_backward);
|
||||
__ Orr(x0, x0, 1 << 3);
|
||||
__ B(&done);
|
||||
|
||||
for (unsigned i = 0; i < max_range / kInstructionSize + 1; ++i) {
|
||||
if (i % 100 == 0) {
|
||||
// If we do land in this code, we do not want to execute so many nops
|
||||
// before reaching the end of test (especially if tracing is activated).
|
||||
__ b(&fail);
|
||||
} else {
|
||||
__ nop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__ Bind(&far_forward);
|
||||
__ Orr(x0, x0, 1 << 2);
|
||||
__ Adr(x10, &far_backward, MacroAssembler::kAdrFar);
|
||||
__ Br(x10);
|
||||
|
||||
__ B(&done);
|
||||
__ Bind(&fail);
|
||||
__ Orr(x0, x0, 1 << 4);
|
||||
__ Bind(&done);
|
||||
|
||||
END();
|
||||
|
||||
RUN();
|
||||
|
||||
ASSERT_EQUAL_64(0xf, x0);
|
||||
|
||||
TEARDOWN();
|
||||
}
|
||||
|
||||
|
||||
TEST(branch_cond) {
|
||||
INIT_V8();
|
||||
SETUP();
|
||||
|
Loading…
Reference in New Issue
Block a user