Revision: 20545
Author: [email protected]
Date: Mon Apr 7 12:33:03 2014 UTC
Log: 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.
[email protected]
Review URL: https://codereview.chromium.org/222433002
http://code.google.com/p/v8/source/detail?r=20545
Modified:
/branches/bleeding_edge/src/arm64/assembler-arm64.cc
/branches/bleeding_edge/src/arm64/assembler-arm64.h
/branches/bleeding_edge/src/arm64/instructions-arm64.cc
/branches/bleeding_edge/src/arm64/instructions-arm64.h
/branches/bleeding_edge/src/arm64/macro-assembler-arm64-inl.h
/branches/bleeding_edge/src/arm64/macro-assembler-arm64.cc
/branches/bleeding_edge/src/arm64/macro-assembler-arm64.h
/branches/bleeding_edge/src/arm64/regexp-macro-assembler-arm64.cc
/branches/bleeding_edge/test/cctest/test-assembler-arm64.cc
=======================================
--- /branches/bleeding_edge/src/arm64/assembler-arm64.cc Fri Apr 4
12:54:12 2014 UTC
+++ /branches/bleeding_edge/src/arm64/assembler-arm64.cc Mon Apr 7
12:33:03 2014 UTC
@@ -2815,6 +2815,76 @@
// No out-of-line constant pool support.
UNREACHABLE();
}
+
+
+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
=======================================
--- /branches/bleeding_edge/src/arm64/assembler-arm64.h Thu Apr 3 14:30:44
2014 UTC
+++ /branches/bleeding_edge/src/arm64/assembler-arm64.h Mon Apr 7 12:33:03
2014 UTC
@@ -1501,8 +1501,9 @@
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 @@
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);
};
=======================================
--- /branches/bleeding_edge/src/arm64/instructions-arm64.cc Fri Mar 21
09:28:26 2014 UTC
+++ /branches/bleeding_edge/src/arm64/instructions-arm64.cc Mon Apr 7
12:33:03 2014 UTC
@@ -254,11 +254,18 @@
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);
+ }
}
=======================================
--- /branches/bleeding_edge/src/arm64/instructions-arm64.h Fri Mar 21
09:28:26 2014 UTC
+++ /branches/bleeding_edge/src/arm64/instructions-arm64.h Mon Apr 7
12:33:03 2014 UTC
@@ -160,9 +160,10 @@
// 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();
@@ -202,6 +203,10 @@
bool IsPCRelAddressing() const {
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
}
+
+ bool IsAdr() const {
+ return Mask(PCRelAddressingMask) == ADR;
+ }
bool IsLogicalImmediate() const {
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
@@ -210,6 +215,10 @@
bool IsAddSubImmediate() const {
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
}
+
+ bool IsAddSubShifted() const {
+ return Mask(AddSubShiftedFMask) == AddSubShiftedFixed;
+ }
bool IsAddSubExtended() const {
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
@@ -387,6 +396,10 @@
}
+ static const int ImmPCRelRangeBitwidth = 21;
+ static bool IsValidPCRelOffset(int offset) {
+ return is_int21(offset);
+ }
void SetPCRelImmTarget(Instruction* target);
void SetBranchImmTarget(Instruction* target);
};
=======================================
--- /branches/bleeding_edge/src/arm64/macro-assembler-arm64-inl.h Thu Apr
3 14:30:44 2014 UTC
+++ /branches/bleeding_edge/src/arm64/macro-assembler-arm64-inl.h Mon Apr
7 12:33:03 2014 UTC
@@ -319,13 +319,6 @@
#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) {
=======================================
--- /branches/bleeding_edge/src/arm64/macro-assembler-arm64.cc Mon Mar 31
14:06:42 2014 UTC
+++ /branches/bleeding_edge/src/arm64/macro-assembler-arm64.cc Mon Apr 7
12:33:03 2014 UTC
@@ -597,6 +597,43 @@
}
return need_longer_range;
}
+
+
+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) {
=======================================
--- /branches/bleeding_edge/src/arm64/macro-assembler-arm64.h Fri Apr 4
13:41:12 2014 UTC
+++ /branches/bleeding_edge/src/arm64/macro-assembler-arm64.h Mon Apr 7
12:33:03 2014 UTC
@@ -255,8 +255,16 @@
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);
=======================================
--- /branches/bleeding_edge/src/arm64/regexp-macro-assembler-arm64.cc Mon
Mar 24 10:41:33 2014 UTC
+++ /branches/bleeding_edge/src/arm64/regexp-macro-assembler-arm64.cc Mon
Apr 7 12:33:03 2014 UTC
@@ -1128,7 +1128,7 @@
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);
=======================================
--- /branches/bleeding_edge/test/cctest/test-assembler-arm64.cc Thu Apr 3
14:30:44 2014 UTC
+++ /branches/bleeding_edge/test/cctest/test-assembler-arm64.cc Mon Apr 7
12:33:03 2014 UTC
@@ -1684,6 +1684,71 @@
TEARDOWN();
}
+
+
+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) {
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.