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
parent 341ec1b5
......@@ -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);
ASSERT(IsAdr());
Instr imm = Assembler::ImmPCRelAddress(DistanceTo(target));
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
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();
......
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