From: alexandre.rames@arm.com Date: Tue, 18 Feb 2014 13:15:32 +0000 (+0000) Subject: A64: Let the MacroAssembler resolve branches to distant targets. X-Git-Tag: upstream/4.7.83~10650 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=62116e2c125a31fc4b2fd55c172f045d804921d6;p=platform%2Fupstream%2Fv8.git A64: Let the MacroAssembler resolve branches to distant targets. Code generation would fail when assembling a branch to a label that is bound outside the immediate range of the instruction. A64 is sensitive to this, as the various branching instructions have different ranges, going down to +-32KB for TBZ/TBNZ. The MacroAssembler is augmented to handle branches to targets that may exceed the immediate range of instructions. When branching backward to a label exceeding the instruction range, the MacroAssembler can simply tweak the generated code to use an unconditional branch with a longer range. For example instead of B(cond, &label); the MacroAssembler can generate: b(InvertCondition(cond), &done); b(&label); bind(&done); Since the target is not known when the branch is emitted, forward branches uses a different mechanism. The MacroAssembler keeps track of forward branches to unbound labels. When the code generation approaches the end of the range of a branch, a veneer is generated for the branch. BUG=v8:3148 LOG=Y R=ulan@chromium.org Review URL: https://codereview.chromium.org/169893002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19444 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/a64/assembler-a64-inl.h b/src/a64/assembler-a64-inl.h index 09e23ea..e68dee0 100644 --- a/src/a64/assembler-a64-inl.h +++ b/src/a64/assembler-a64-inl.h @@ -536,6 +536,16 @@ Operand MemOperand::OffsetAsOperand() const { } +void Assembler::Unreachable() { +#ifdef USE_SIMULATOR + debug("UNREACHABLE", __LINE__, BREAK); +#else + // Crash by branching to 0. lr now points near the fault. + Emit(BLR | Rn(xzr)); +#endif +} + + Address Assembler::target_pointer_address_at(Address pc) { Instruction* instr = reinterpret_cast(pc); ASSERT(instr->IsLdrLiteralX()); diff --git a/src/a64/assembler-a64.cc b/src/a64/assembler-a64.cc index d125074..43b1391 100644 --- a/src/a64/assembler-a64.cc +++ b/src/a64/assembler-a64.cc @@ -283,6 +283,7 @@ bool Operand::NeedsRelocation() const { Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size) : AssemblerBase(isolate, buffer, buffer_size), recorded_ast_id_(TypeFeedbackId::None()), + unresolved_branches_(), positions_recorder_(this) { const_pool_blocked_nesting_ = 0; Reset(); @@ -341,13 +342,13 @@ void Assembler::CheckLabelLinkChain(Label const * label) { #ifdef DEBUG if (label->is_linked()) { int linkoffset = label->pos(); - bool start_of_chain = false; - while (!start_of_chain) { + bool end_of_chain = false; + while (!end_of_chain) { Instruction * link = InstructionAt(linkoffset); int linkpcoffset = link->ImmPCOffset(); int prevlinkoffset = linkoffset + linkpcoffset; - start_of_chain = (linkoffset == prevlinkoffset); + end_of_chain = (linkoffset == prevlinkoffset); linkoffset = linkoffset + linkpcoffset; } } @@ -355,6 +356,86 @@ void Assembler::CheckLabelLinkChain(Label const * label) { } +void Assembler::RemoveBranchFromLabelLinkChain(Instruction* branch, + Label* label, + Instruction* label_veneer) { + ASSERT(label->is_linked()); + + CheckLabelLinkChain(label); + + Instruction* link = InstructionAt(label->pos()); + Instruction* prev_link = link; + Instruction* next_link; + bool end_of_chain = false; + + while (link != branch && !end_of_chain) { + next_link = link->ImmPCOffsetTarget(); + end_of_chain = (link == next_link); + prev_link = link; + link = next_link; + } + + ASSERT(branch == link); + next_link = branch->ImmPCOffsetTarget(); + + if (branch == prev_link) { + // The branch is the first instruction in the chain. + if (branch == next_link) { + // It is also the last instruction in the chain, so it is the only branch + // currently referring to this label. + label->Unuse(); + } else { + label->link_to(reinterpret_cast(next_link) - buffer_); + } + + } else if (branch == next_link) { + // The branch is the last (but not also the first) instruction in the chain. + prev_link->SetImmPCOffsetTarget(prev_link); + + } else { + // The branch is in the middle of the chain. + if (prev_link->IsTargetInImmPCOffsetRange(next_link)) { + prev_link->SetImmPCOffsetTarget(next_link); + } else if (label_veneer != NULL) { + // Use the veneer for all previous links in the chain. + prev_link->SetImmPCOffsetTarget(prev_link); + + end_of_chain = false; + link = next_link; + while (!end_of_chain) { + next_link = link->ImmPCOffsetTarget(); + end_of_chain = (link == next_link); + link->SetImmPCOffsetTarget(label_veneer); + link = next_link; + } + } else { + // The assert below will fire. + // Some other work could be attempted to fix up the chain, but it would be + // rather complicated. If we crash here, we may want to consider using an + // other mechanism than a chain of branches. + // + // Note that this situation currently should not happen, as we always call + // this function with a veneer to the target label. + // However this could happen with a MacroAssembler in the following state: + // [previous code] + // B(label); + // [20KB code] + // Tbz(label); // First tbz. Pointing to unconditional branch. + // [20KB code] + // Tbz(label); // Second tbz. Pointing to the first tbz. + // [more code] + // and this function is called to remove the first tbz from the label link + // chain. Since tbz has a range of +-32KB, the second tbz cannot point to + // the unconditional branch. + CHECK(prev_link->IsTargetInImmPCOffsetRange(next_link)); + UNREACHABLE(); + } + } + + CheckLabelLinkChain(label); +} + + void Assembler::bind(Label* label) { // Bind label to the address at pc_. All instructions (most likely branches) // that are linked to this label will be updated to point to the newly-bound @@ -404,6 +485,8 @@ void Assembler::bind(Label* label) { ASSERT(label->is_bound()); ASSERT(!label->is_linked()); + + DeleteUnresolvedBranchInfoForLabel(label); } @@ -450,6 +533,20 @@ int Assembler::LinkAndGetByteOffsetTo(Label* label) { } +void Assembler::DeleteUnresolvedBranchInfoForLabel(Label* label) { + // Branches to this label will be resolved when the label is bound below. + std::multimap::iterator it_tmp, it; + it = unresolved_branches_.begin(); + while (it != unresolved_branches_.end()) { + it_tmp = it++; + if (it_tmp->second.label_ == label) { + CHECK(it_tmp->first >= pc_offset()); + unresolved_branches_.erase(it_tmp); + } + } +} + + void Assembler::StartBlockConstPool() { if (const_pool_blocked_nesting_++ == 0) { // Prevent constant pool checks happening by setting the next check to @@ -520,8 +617,7 @@ void Assembler::ConstantPoolGuard() { instr->preceding()->Rt() == xzr.code()); #endif - // Crash by branching to 0. lr now points near the fault. - // TODO(all): update the simulator to trap this pattern. + // We must generate only one instruction. Emit(BLR | Rn(xzr)); } diff --git a/src/a64/assembler-a64.h b/src/a64/assembler-a64.h index 1ac773d..a2c93df 100644 --- a/src/a64/assembler-a64.h +++ b/src/a64/assembler-a64.h @@ -29,6 +29,7 @@ #define V8_A64_ASSEMBLER_A64_H_ #include +#include #include "globals.h" #include "utils.h" @@ -720,6 +721,8 @@ class Assembler : public AssemblerBase { // of m. m must be a power of 2 (>= 4). void Align(int m); + inline void Unreachable(); + // Label -------------------------------------------------------------------- // Bind a label to the current pc. Note that labels can only be bound once, // and if labels are linked to other instructions, they _must_ be bound @@ -1792,6 +1795,12 @@ class Assembler : public AssemblerBase { static inline LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( const CPURegister& rt, const CPURegister& rt2); + // Remove the specified branch from the unbound label link chain. + // If available, a veneer for this label can be used for other branches in the + // chain if the link chain cannot be fixed up without this branch. + void RemoveBranchFromLabelLinkChain(Instruction* branch, + Label* label, + Instruction* label_veneer = NULL); private: // Instruction helpers. @@ -1978,6 +1987,39 @@ class Assembler : public AssemblerBase { // stream. static const int kGap = 128; + public: + class FarBranchInfo { + public: + FarBranchInfo(int offset, Label* label) + : pc_offset_(offset), label_(label) {} + // Offset of the branch in the code generation buffer. + int pc_offset_; + // The label branched to. + Label* label_; + }; + + protected: + // Information about unresolved (forward) branches. + // The Assembler is only allowed to delete out-of-date information from here + // after a label is bound. The MacroAssembler uses this information to + // generate veneers. + // + // The second member gives information about the unresolved branch. The first + // member of the pair is the maximum offset that the branch can reach in the + // buffer. The map is sorted according to this reachable offset, allowing to + // easily check when veneers need to be emitted. + // Note that the maximum reachable offset (first member of the pairs) should + // always be positive but has the same type as the return value for + // pc_offset() for convenience. + std::multimap unresolved_branches_; + + private: + // If a veneer is emitted for a branch instruction, that instruction must be + // removed from the associated label's link chain so that the assembler does + // not later attempt (likely unsuccessfully) to patch it to branch directly to + // the label. + void DeleteUnresolvedBranchInfoForLabel(Label* label); + private: // TODO(jbramley): VIXL uses next_literal_pool_check_ and // literal_pool_monitor_ to determine when to consider emitting a literal diff --git a/src/a64/instructions-a64.cc b/src/a64/instructions-a64.cc index ebd6c26..4496d56 100644 --- a/src/a64/instructions-a64.cc +++ b/src/a64/instructions-a64.cc @@ -230,6 +230,18 @@ Instruction* Instruction::ImmPCOffsetTarget() { } +bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type, + int32_t offset) { + return is_intn(offset, ImmBranchRangeBitwidth(branch_type)); +} + + +bool Instruction::IsTargetInImmPCOffsetRange(Instruction* target) { + int offset = target - this; + return IsValidImmPCOffset(BranchType(), offset); +} + + void Instruction::SetImmPCOffsetTarget(Instruction* target) { if (IsPCRelAddressing()) { SetPCRelImmTarget(target); diff --git a/src/a64/instructions-a64.h b/src/a64/instructions-a64.h index 83d22e8..472d4bf 100644 --- a/src/a64/instructions-a64.h +++ b/src/a64/instructions-a64.h @@ -280,6 +280,29 @@ class Instruction { } } + static int ImmBranchRangeBitwidth(ImmBranchType branch_type) { + switch (branch_type) { + case UncondBranchType: + return ImmUncondBranch_width; + case CondBranchType: + return ImmCondBranch_width; + case CompareBranchType: + return ImmCmpBranch_width; + case TestBranchType: + return ImmTestBranch_width; + default: + UNREACHABLE(); + return 0; + } + } + + // The range of the branch instruction, expressed as 'instr +- range'. + static int32_t ImmBranchRange(ImmBranchType branch_type) { + return + (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 - + kInstructionSize; + } + int ImmBranch() const { switch (BranchType()) { case CondBranchType: return ImmCondBranch(); @@ -329,6 +352,8 @@ class Instruction { // PC-relative addressing instruction. Instruction* ImmPCOffsetTarget(); + static bool IsValidImmPCOffset(ImmBranchType branch_type, int32_t offset); + bool IsTargetInImmPCOffsetRange(Instruction* target); // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or // a PC-relative addressing instruction. void SetImmPCOffsetTarget(Instruction* target); diff --git a/src/a64/macro-assembler-a64-inl.h b/src/a64/macro-assembler-a64-inl.h index 53ce57f..0c62a8b 100644 --- a/src/a64/macro-assembler-a64-inl.h +++ b/src/a64/macro-assembler-a64-inl.h @@ -346,6 +346,7 @@ void MacroAssembler::Asr(const Register& rd, void MacroAssembler::B(Label* label) { b(label); + CheckVeneers(false); } @@ -355,13 +356,6 @@ void MacroAssembler::B(Condition cond, Label* label) { } -void MacroAssembler::B(Label* label, Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT((cond != al) && (cond != nv)); - b(label, cond); -} - - void MacroAssembler::Bfi(const Register& rd, const Register& rn, unsigned lsb, @@ -414,18 +408,6 @@ void MacroAssembler::Brk(int code) { } -void MacroAssembler::Cbnz(const Register& rt, Label* label) { - ASSERT(allow_macro_instructions_); - cbnz(rt, label); -} - - -void MacroAssembler::Cbz(const Register& rt, Label* label) { - ASSERT(allow_macro_instructions_); - cbz(rt, label); -} - - void MacroAssembler::Cinc(const Register& rd, const Register& rn, Condition cond) { @@ -1032,6 +1014,7 @@ void MacroAssembler::Ret(const Register& xn) { ASSERT(allow_macro_instructions_); ASSERT(!xn.IsZero()); ret(xn); + CheckVeneers(false); } @@ -1186,18 +1169,6 @@ void MacroAssembler::Sxtw(const Register& rd, const Register& rn) { } -void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) { - ASSERT(allow_macro_instructions_); - tbnz(rt, bit_pos, label); -} - - -void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) { - ASSERT(allow_macro_instructions_); - tbz(rt, bit_pos, label); -} - - void MacroAssembler::Ubfiz(const Register& rd, const Register& rn, unsigned lsb, @@ -1255,12 +1226,6 @@ void MacroAssembler::Umsubl(const Register& rd, } -void MacroAssembler::Unreachable() { - ASSERT(allow_macro_instructions_); - hlt(kImmExceptionIsUnreachable); -} - - void MacroAssembler::Uxtb(const Register& rd, const Register& rn) { ASSERT(allow_macro_instructions_); ASSERT(!rd.IsZero()); diff --git a/src/a64/macro-assembler-a64.cc b/src/a64/macro-assembler-a64.cc index d6bd6b6..e6fef6d 100644 --- a/src/a64/macro-assembler-a64.cc +++ b/src/a64/macro-assembler-a64.cc @@ -558,6 +558,204 @@ void MacroAssembler::Store(const Register& rt, } +bool MacroAssembler::ShouldEmitVeneer(int max_reachable_pc, int margin) { + // Account for the branch around the veneers and the guard. + int protection_offset = 2 * kInstructionSize; + return pc_offset() > max_reachable_pc - margin - protection_offset - + static_cast(unresolved_branches_.size() * kMaxVeneerCodeSize); +} + + +void MacroAssembler::EmitVeneers(bool need_protection) { + RecordComment("[ Veneers"); + + Label end; + if (need_protection) { + B(&end); + } + + EmitVeneersGuard(); + + { + InstructionAccurateScope scope(this); + Label size_check; + + std::multimap::iterator it, it_to_delete; + + it = unresolved_branches_.begin(); + while (it != unresolved_branches_.end()) { + if (ShouldEmitVeneer(it->first)) { + Instruction* branch = InstructionAt(it->second.pc_offset_); + Label* label = it->second.label_; + +#ifdef DEBUG + __ bind(&size_check); +#endif + // Patch the branch to point to the current position, and emit a branch + // to the label. + Instruction* veneer = reinterpret_cast(pc_); + RemoveBranchFromLabelLinkChain(branch, label, veneer); + branch->SetImmPCOffsetTarget(veneer); + b(label); +#ifdef DEBUG + ASSERT(SizeOfCodeGeneratedSince(&size_check) <= kMaxVeneerCodeSize); + size_check.Unuse(); +#endif + + it_to_delete = it++; + unresolved_branches_.erase(it_to_delete); + } else { + ++it; + } + } + } + + Bind(&end); + + RecordComment("]"); +} + + +void MacroAssembler::EmitVeneersGuard() { + if (emit_debug_code()) { + Unreachable(); + } +} + + +void MacroAssembler::CheckVeneers(bool need_protection) { + if (unresolved_branches_.empty()) { + return; + } + + CHECK(pc_offset() < unresolved_branches_first_limit()); + int margin = kVeneerDistanceMargin; + if (!need_protection) { + // Prefer emitting veneers protected by an existing instruction. + // The 4 divisor is a finger in the air guess. With a default margin of 2KB, + // that leaves 512B = 128 instructions of extra margin to avoid requiring a + // protective branch. + margin += margin / 4; + } + if (ShouldEmitVeneer(unresolved_branches_first_limit(), margin)) { + EmitVeneers(need_protection); + } +} + + +bool MacroAssembler::NeedExtraInstructionsOrRegisterBranch( + Label *label, ImmBranchType b_type) { + bool need_longer_range = false; + // There are two situations in which we care about the offset being out of + // range: + // - The label is bound but too far away. + // - The label is not bound but linked, and the previous branch + // instruction in the chain is too far away. + if (label->is_bound() || label->is_linked()) { + need_longer_range = + !Instruction::IsValidImmPCOffset(b_type, label->pos() - pc_offset()); + } + if (!need_longer_range && !label->is_bound()) { + int max_reachable_pc = pc_offset() + Instruction::ImmBranchRange(b_type); + unresolved_branches_.insert( + std::pair(max_reachable_pc, + FarBranchInfo(pc_offset(), label))); + } + return need_longer_range; +} + + +void MacroAssembler::B(Label* label, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT((cond != al) && (cond != nv)); + + Label done; + bool need_extra_instructions = + NeedExtraInstructionsOrRegisterBranch(label, CondBranchType); + + if (need_extra_instructions) { + b(&done, InvertCondition(cond)); + b(label); + } else { + b(label, cond); + } + CheckVeneers(!need_extra_instructions); + bind(&done); +} + + +void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) { + ASSERT(allow_macro_instructions_); + + Label done; + bool need_extra_instructions = + NeedExtraInstructionsOrRegisterBranch(label, TestBranchType); + + if (need_extra_instructions) { + tbz(rt, bit_pos, &done); + b(label); + } else { + tbnz(rt, bit_pos, label); + } + CheckVeneers(!need_extra_instructions); + bind(&done); +} + + +void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) { + ASSERT(allow_macro_instructions_); + + Label done; + bool need_extra_instructions = + NeedExtraInstructionsOrRegisterBranch(label, TestBranchType); + + if (need_extra_instructions) { + tbnz(rt, bit_pos, &done); + b(label); + } else { + tbz(rt, bit_pos, label); + } + CheckVeneers(!need_extra_instructions); + bind(&done); +} + + +void MacroAssembler::Cbnz(const Register& rt, Label* label) { + ASSERT(allow_macro_instructions_); + + Label done; + bool need_extra_instructions = + NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType); + + if (need_extra_instructions) { + cbz(rt, &done); + b(label); + } else { + cbnz(rt, label); + } + CheckVeneers(!need_extra_instructions); + bind(&done); +} + + +void MacroAssembler::Cbz(const Register& rt, Label* label) { + ASSERT(allow_macro_instructions_); + + Label done; + bool need_extra_instructions = + NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType); + + if (need_extra_instructions) { + cbnz(rt, &done); + b(label); + } else { + cbz(rt, label); + } + CheckVeneers(!need_extra_instructions); + bind(&done); +} + + // Pseudo-instructions. diff --git a/src/a64/macro-assembler-a64.h b/src/a64/macro-assembler-a64.h index 8bd24d3..7b8dd3f 100644 --- a/src/a64/macro-assembler-a64.h +++ b/src/a64/macro-assembler-a64.h @@ -212,7 +212,7 @@ class MacroAssembler : public Assembler { inline void Asr(const Register& rd, const Register& rn, const Register& rm); inline void B(Label* label); inline void B(Condition cond, Label* label); - inline void B(Label* label, Condition cond); + void B(Label* label, Condition cond); inline void Bfi(const Register& rd, const Register& rn, unsigned lsb, @@ -226,8 +226,8 @@ class MacroAssembler : public Assembler { inline void Blr(const Register& xn); inline void Br(const Register& xn); inline void Brk(int code); - inline void Cbnz(const Register& rt, Label* label); - inline void Cbz(const Register& rt, Label* label); + void Cbnz(const Register& rt, Label* label); + void Cbz(const Register& rt, Label* label); inline void Cinc(const Register& rd, const Register& rn, Condition cond); inline void Cinv(const Register& rd, const Register& rn, Condition cond); inline void Cls(const Register& rd, const Register& rn); @@ -400,8 +400,8 @@ class MacroAssembler : public Assembler { inline void Sxtb(const Register& rd, const Register& rn); inline void Sxth(const Register& rd, const Register& rn); inline void Sxtw(const Register& rd, const Register& rn); - inline void Tbnz(const Register& rt, unsigned bit_pos, Label* label); - inline void Tbz(const Register& rt, unsigned bit_pos, Label* label); + void Tbnz(const Register& rt, unsigned bit_pos, Label* label); + void Tbz(const Register& rt, unsigned bit_pos, Label* label); inline void Ubfiz(const Register& rd, const Register& rn, unsigned lsb, @@ -422,7 +422,6 @@ class MacroAssembler : public Assembler { const Register& rn, const Register& rm, const Register& ra); - inline void Unreachable(); inline void Uxtb(const Register& rd, const Register& rn); inline void Uxth(const Register& rd, const Register& rn); inline void Uxtw(const Register& rd, const Register& rn); @@ -2074,6 +2073,58 @@ class MacroAssembler : public Assembler { Heap::RootListIndex map_index, Register scratch1, Register scratch2); + + public: + // Far branches resolving. + // + // The various classes of branch instructions with immediate offsets have + // different ranges. While the Assembler will fail to assemble a branch + // exceeding its range, the MacroAssembler offers a mechanism to resolve + // branches to too distant targets, either by tweaking the generated code to + // use branch instructions with wider ranges or generating veneers. + // + // Currently branches to distant targets are resolved using unconditional + // branch isntructions with a range of +-128MB. If that becomes too little + // (!), the mechanism can be extended to generate special veneers for really + // far targets. + + // Returns true if we should emit a veneer as soon as possible for a branch + // which can at most reach to specified pc. + bool ShouldEmitVeneer(int max_reachable_pc, + int margin = kVeneerDistanceMargin); + + // The maximum code size generated for a veneer. Currently one branch + // instruction. This is for code size checking purposes, and can be extended + // in the future for example if we decide to add nops between the veneers. + static const int kMaxVeneerCodeSize = 1 * kInstructionSize; + + // Emits veneers for branches that are approaching their maximum range. + // If need_protection is true, the veneers are protected by a branch jumping + // over the code. + void EmitVeneers(bool need_protection); + void EmitVeneersGuard(); + // Checks wether veneers need to be emitted at this point. + void CheckVeneers(bool need_protection); + + // Helps resolve branching to labels potentially out of range. + // If the label is not bound, it registers the information necessary to later + // be able to emit a veneer for this branch if necessary. + // If the label is bound, it returns true if the label (or the previous link + // in the label chain) is out of range. In that case the caller is responsible + // for generating appropriate code. + // Otherwise it returns false. + // This function also checks wether veneers need to be emitted. + bool NeedExtraInstructionsOrRegisterBranch(Label *label, + ImmBranchType branch_type); + + private: + // We generate a veneer for a branch if we reach within this distance of the + // limit of the range. + static const int kVeneerDistanceMargin = 2 * KB; + int unresolved_branches_first_limit() const { + ASSERT(!unresolved_branches_.empty()); + return unresolved_branches_.begin()->first; + } }; @@ -2083,20 +2134,13 @@ class MacroAssembler : public Assembler { // emitted is what you specified when creating the scope. class InstructionAccurateScope BASE_EMBEDDED { public: - explicit InstructionAccurateScope(MacroAssembler* masm) - : masm_(masm), size_(0) { - masm_->StartBlockConstPool(); -#ifdef DEBUG - previous_allow_macro_instructions_ = masm_->allow_macro_instructions(); - masm_->set_allow_macro_instructions(false); -#endif - } - - InstructionAccurateScope(MacroAssembler* masm, size_t count) + InstructionAccurateScope(MacroAssembler* masm, size_t count = 0) : masm_(masm), size_(count * kInstructionSize) { masm_->StartBlockConstPool(); #ifdef DEBUG - masm_->bind(&start_); + if (count != 0) { + masm_->bind(&start_); + } previous_allow_macro_instructions_ = masm_->allow_macro_instructions(); masm_->set_allow_macro_instructions(false); #endif diff --git a/test/cctest/test-assembler-a64.cc b/test/cctest/test-assembler-a64.cc index 547de68..656f369 100644 --- a/test/cctest/test-assembler-a64.cc +++ b/test/cctest/test-assembler-a64.cc @@ -1947,6 +1947,335 @@ TEST(test_branch) { } +TEST(far_branch_backward) { + INIT_V8(); + + // Test that the MacroAssembler correctly resolves backward branches to labels + // that are outside the immediate range of branch instructions. + int max_range = + std::max(Instruction::ImmBranchRange(TestBranchType), + std::max(Instruction::ImmBranchRange(CompareBranchType), + Instruction::ImmBranchRange(CondBranchType))); + + SETUP_SIZE(max_range + 1000 * kInstructionSize); + + START(); + + Label done, fail; + Label test_tbz, test_cbz, test_bcond; + Label success_tbz, success_cbz, success_bcond; + + __ Mov(x0, 0); + __ Mov(x1, 1); + __ Mov(x10, 0); + + __ B(&test_tbz); + __ Bind(&success_tbz); + __ Orr(x0, x0, 1 << 0); + __ B(&test_cbz); + __ Bind(&success_cbz); + __ Orr(x0, x0, 1 << 1); + __ B(&test_bcond); + __ Bind(&success_bcond); + __ Orr(x0, x0, 1 << 2); + + __ B(&done); + + // Generate enough code to overflow the immediate range of the three types of + // branches below. + 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(); + } + } + __ B(&fail); + + __ Bind(&test_tbz); + __ Tbz(x10, 7, &success_tbz); + __ Bind(&test_cbz); + __ Cbz(x10, &success_cbz); + __ Bind(&test_bcond); + __ Cmp(x10, 0); + __ B(eq, &success_bcond); + + // For each out-of-range branch instructions, at least two instructions should + // have been generated. + CHECK_GE(7 * kInstructionSize, __ SizeOfCodeGeneratedSince(&test_tbz)); + + __ Bind(&fail); + __ Mov(x1, 0); + __ Bind(&done); + + END(); + + RUN(); + + ASSERT_EQUAL_64(0x7, x0); + ASSERT_EQUAL_64(0x1, x1); + + TEARDOWN(); +} + + +TEST(far_branch_simple_veneer) { + INIT_V8(); + + // Test that the MacroAssembler correctly emits veneers for forward branches + // to labels that are outside the immediate range of branch instructions. + int max_range = + std::max(Instruction::ImmBranchRange(TestBranchType), + std::max(Instruction::ImmBranchRange(CompareBranchType), + Instruction::ImmBranchRange(CondBranchType))); + + SETUP_SIZE(max_range + 1000 * kInstructionSize); + + START(); + + Label done, fail; + Label test_tbz, test_cbz, test_bcond; + Label success_tbz, success_cbz, success_bcond; + + __ Mov(x0, 0); + __ Mov(x1, 1); + __ Mov(x10, 0); + + __ Bind(&test_tbz); + __ Tbz(x10, 7, &success_tbz); + __ Bind(&test_cbz); + __ Cbz(x10, &success_cbz); + __ Bind(&test_bcond); + __ Cmp(x10, 0); + __ B(eq, &success_bcond); + + // Generate enough code to overflow the immediate range of the three types of + // branches below. + 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). + // Also, the branches give the MacroAssembler the opportunity to emit the + // veneers. + __ B(&fail); + } else { + __ Nop(); + } + } + __ B(&fail); + + __ Bind(&success_tbz); + __ Orr(x0, x0, 1 << 0); + __ B(&test_cbz); + __ Bind(&success_cbz); + __ Orr(x0, x0, 1 << 1); + __ B(&test_bcond); + __ Bind(&success_bcond); + __ Orr(x0, x0, 1 << 2); + + __ B(&done); + __ Bind(&fail); + __ Mov(x1, 0); + __ Bind(&done); + + END(); + + RUN(); + + ASSERT_EQUAL_64(0x7, x0); + ASSERT_EQUAL_64(0x1, x1); + + TEARDOWN(); +} + + +TEST(far_branch_veneer_link_chain) { + INIT_V8(); + + // Test that the MacroAssembler correctly emits veneers for forward branches + // that target out-of-range labels and are part of multiple instructions + // jumping to that label. + // + // We test the three situations with the different types of instruction: + // (1)- When the branch is at the start of the chain with tbz. + // (2)- When the branch is in the middle of the chain with cbz. + // (3)- When the branch is at the end of the chain with bcond. + int max_range = + std::max(Instruction::ImmBranchRange(TestBranchType), + std::max(Instruction::ImmBranchRange(CompareBranchType), + Instruction::ImmBranchRange(CondBranchType))); + + SETUP_SIZE(max_range + 1000 * kInstructionSize); + + START(); + + Label skip, fail, done; + Label test_tbz, test_cbz, test_bcond; + Label success_tbz, success_cbz, success_bcond; + + __ Mov(x0, 0); + __ Mov(x1, 1); + __ Mov(x10, 0); + + __ B(&skip); + // Branches at the start of the chain for situations (2) and (3). + __ B(&success_cbz); + __ B(&success_bcond); + __ Nop(); + __ B(&success_bcond); + __ B(&success_cbz); + __ Bind(&skip); + + __ Bind(&test_tbz); + __ Tbz(x10, 7, &success_tbz); + __ Bind(&test_cbz); + __ Cbz(x10, &success_cbz); + __ Bind(&test_bcond); + __ Cmp(x10, 0); + __ B(eq, &success_bcond); + + skip.Unuse(); + __ B(&skip); + // Branches at the end of the chain for situations (1) and (2). + __ B(&success_cbz); + __ B(&success_tbz); + __ Nop(); + __ B(&success_tbz); + __ B(&success_cbz); + __ Bind(&skip); + + // Generate enough code to overflow the immediate range of the three types of + // branches below. + 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). + // Also, the branches give the MacroAssembler the opportunity to emit the + // veneers. + __ B(&fail); + } else { + __ Nop(); + } + } + __ B(&fail); + + __ Bind(&success_tbz); + __ Orr(x0, x0, 1 << 0); + __ B(&test_cbz); + __ Bind(&success_cbz); + __ Orr(x0, x0, 1 << 1); + __ B(&test_bcond); + __ Bind(&success_bcond); + __ Orr(x0, x0, 1 << 2); + + __ B(&done); + __ Bind(&fail); + __ Mov(x1, 0); + __ Bind(&done); + + END(); + + RUN(); + + ASSERT_EQUAL_64(0x7, x0); + ASSERT_EQUAL_64(0x1, x1); + + TEARDOWN(); +} + + +TEST(far_branch_veneer_broken_link_chain) { + INIT_V8(); + + // Check that the MacroAssembler correctly handles the situation when removing + // a branch from the link chain of a label and the two links on each side of + // the removed branch cannot be linked together (out of range). + // + // We test with tbz because it has a small range. + int max_range = Instruction::ImmBranchRange(TestBranchType); + int inter_range = max_range / 2 + max_range / 10; + + SETUP_SIZE(3 * inter_range + 1000 * kInstructionSize); + + START(); + + Label skip, fail, done; + Label test_1, test_2, test_3; + Label far_target; + + __ Mov(x0, 0); // Indicates the origin of the branch. + __ Mov(x1, 1); + __ Mov(x10, 0); + + // First instruction in the label chain. + __ Bind(&test_1); + __ Mov(x0, 1); + __ B(&far_target); + + for (unsigned i = 0; i < inter_range / kInstructionSize; ++i) { + if (i % 100 == 0) { + // Do not allow generating veneers. They should not be needed. + __ b(&fail); + } else { + __ Nop(); + } + } + + // Will need a veneer to point to reach the target. + __ Bind(&test_2); + __ Mov(x0, 2); + __ Tbz(x10, 7, &far_target); + + for (unsigned i = 0; i < inter_range / kInstructionSize; ++i) { + if (i % 100 == 0) { + // Do not allow generating veneers. They should not be needed. + __ b(&fail); + } else { + __ Nop(); + } + } + + // Does not need a veneer to reach the target, but the initial branch + // instruction is out of range. + __ Bind(&test_3); + __ Mov(x0, 3); + __ Tbz(x10, 7, &far_target); + + for (unsigned i = 0; i < inter_range / kInstructionSize; ++i) { + if (i % 100 == 0) { + // Allow generating veneers. + __ B(&fail); + } else { + __ Nop(); + } + } + + __ B(&fail); + + __ Bind(&far_target); + __ Cmp(x0, 1); + __ B(eq, &test_2); + __ Cmp(x0, 2); + __ B(eq, &test_3); + + __ B(&done); + __ Bind(&fail); + __ Mov(x1, 0); + __ Bind(&done); + + END(); + + RUN(); + + ASSERT_EQUAL_64(0x3, x0); + ASSERT_EQUAL_64(0x1, x1); + + TEARDOWN(); +} + + TEST(ldr_str_offset) { INIT_V8(); SETUP(); diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index 23cedc6..4dcf7ee 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -176,10 +176,6 @@ # BUG(v8:3147). It works on other architectures by accident. 'regress/regress-conditional-position': [FAIL], - # BUG(v8:3148): Invalid branch instruction emitted. - 'debug-references': [PASS, ['mode == debug', SKIP]], - 'mirror-array': [PASS, ['mode == debug', SKIP]], - # BUG(v8:3156): Fails on gc stress bots. 'compiler/concurrent-invalidate-transition-map': [PASS, ['gc_stress == True', FAIL]], }], # 'arch == a64' diff --git a/test/mozilla/mozilla.status b/test/mozilla/mozilla.status index 5b3b135..197e651 100644 --- a/test/mozilla/mozilla.status +++ b/test/mozilla/mozilla.status @@ -843,12 +843,6 @@ ['arch == a64', { - # BUG(v8:3148): Invalid branch instruction emitted. - 'ecma/Date/15.9.5.26-1': [SKIP], - 'js1_5/extensions/regress-396326': [SKIP], - 'js1_5/Regress/regress-80981': [SKIP], - 'ecma/Date/15.9.5.28-1': [PASS, ['mode == debug', SKIP]], - # BUG(v8:3152): Runs out of stack in debug mode. 'js1_5/extensions/regress-355497': [FAIL_OK, ['mode == debug', SKIP]], }], # 'arch == a64'