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
parent c2cd2083
......@@ -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<Instruction*>(pc);
ASSERT(instr->IsLdrLiteralX());
......
......@@ -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<byte*>(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<int, FarBranchInfo>::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));
}
......
......@@ -29,6 +29,7 @@
#define V8_A64_ASSEMBLER_A64_H_
#include <list>
#include <map>
#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<int, FarBranchInfo> 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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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());
......
......@@ -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<int>(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<int, FarBranchInfo>::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<Instruction*>(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<int, FarBranchInfo>(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.
......
......@@ -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
......
......@@ -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();
......
......@@ -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'
......
......@@ -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'
......
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