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
This commit is contained in:
parent
c2cd2083a3
commit
62116e2c12
@ -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) {
|
Address Assembler::target_pointer_address_at(Address pc) {
|
||||||
Instruction* instr = reinterpret_cast<Instruction*>(pc);
|
Instruction* instr = reinterpret_cast<Instruction*>(pc);
|
||||||
ASSERT(instr->IsLdrLiteralX());
|
ASSERT(instr->IsLdrLiteralX());
|
||||||
|
@ -283,6 +283,7 @@ bool Operand::NeedsRelocation() const {
|
|||||||
Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
|
Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
|
||||||
: AssemblerBase(isolate, buffer, buffer_size),
|
: AssemblerBase(isolate, buffer, buffer_size),
|
||||||
recorded_ast_id_(TypeFeedbackId::None()),
|
recorded_ast_id_(TypeFeedbackId::None()),
|
||||||
|
unresolved_branches_(),
|
||||||
positions_recorder_(this) {
|
positions_recorder_(this) {
|
||||||
const_pool_blocked_nesting_ = 0;
|
const_pool_blocked_nesting_ = 0;
|
||||||
Reset();
|
Reset();
|
||||||
@ -341,13 +342,13 @@ void Assembler::CheckLabelLinkChain(Label const * label) {
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (label->is_linked()) {
|
if (label->is_linked()) {
|
||||||
int linkoffset = label->pos();
|
int linkoffset = label->pos();
|
||||||
bool start_of_chain = false;
|
bool end_of_chain = false;
|
||||||
while (!start_of_chain) {
|
while (!end_of_chain) {
|
||||||
Instruction * link = InstructionAt(linkoffset);
|
Instruction * link = InstructionAt(linkoffset);
|
||||||
int linkpcoffset = link->ImmPCOffset();
|
int linkpcoffset = link->ImmPCOffset();
|
||||||
int prevlinkoffset = linkoffset + linkpcoffset;
|
int prevlinkoffset = linkoffset + linkpcoffset;
|
||||||
|
|
||||||
start_of_chain = (linkoffset == prevlinkoffset);
|
end_of_chain = (linkoffset == prevlinkoffset);
|
||||||
linkoffset = linkoffset + linkpcoffset;
|
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) {
|
void Assembler::bind(Label* label) {
|
||||||
// Bind label to the address at pc_. All instructions (most likely branches)
|
// 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
|
// 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_bound());
|
||||||
ASSERT(!label->is_linked());
|
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() {
|
void Assembler::StartBlockConstPool() {
|
||||||
if (const_pool_blocked_nesting_++ == 0) {
|
if (const_pool_blocked_nesting_++ == 0) {
|
||||||
// Prevent constant pool checks happening by setting the next check to
|
// Prevent constant pool checks happening by setting the next check to
|
||||||
@ -520,8 +617,7 @@ void Assembler::ConstantPoolGuard() {
|
|||||||
instr->preceding()->Rt() == xzr.code());
|
instr->preceding()->Rt() == xzr.code());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Crash by branching to 0. lr now points near the fault.
|
// We must generate only one instruction.
|
||||||
// TODO(all): update the simulator to trap this pattern.
|
|
||||||
Emit(BLR | Rn(xzr));
|
Emit(BLR | Rn(xzr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#define V8_A64_ASSEMBLER_A64_H_
|
#define V8_A64_ASSEMBLER_A64_H_
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@ -720,6 +721,8 @@ class Assembler : public AssemblerBase {
|
|||||||
// of m. m must be a power of 2 (>= 4).
|
// of m. m must be a power of 2 (>= 4).
|
||||||
void Align(int m);
|
void Align(int m);
|
||||||
|
|
||||||
|
inline void Unreachable();
|
||||||
|
|
||||||
// Label --------------------------------------------------------------------
|
// Label --------------------------------------------------------------------
|
||||||
// Bind a label to the current pc. Note that labels can only be bound once,
|
// 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
|
// and if labels are linked to other instructions, they _must_ be bound
|
||||||
@ -1792,6 +1795,12 @@ class Assembler : public AssemblerBase {
|
|||||||
static inline LoadStorePairNonTemporalOp StorePairNonTemporalOpFor(
|
static inline LoadStorePairNonTemporalOp StorePairNonTemporalOpFor(
|
||||||
const CPURegister& rt, const CPURegister& rt2);
|
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:
|
private:
|
||||||
// Instruction helpers.
|
// Instruction helpers.
|
||||||
@ -1978,6 +1987,39 @@ class Assembler : public AssemblerBase {
|
|||||||
// stream.
|
// stream.
|
||||||
static const int kGap = 128;
|
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:
|
private:
|
||||||
// TODO(jbramley): VIXL uses next_literal_pool_check_ and
|
// TODO(jbramley): VIXL uses next_literal_pool_check_ and
|
||||||
// literal_pool_monitor_ to determine when to consider emitting a literal
|
// 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) {
|
void Instruction::SetImmPCOffsetTarget(Instruction* target) {
|
||||||
if (IsPCRelAddressing()) {
|
if (IsPCRelAddressing()) {
|
||||||
SetPCRelImmTarget(target);
|
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 {
|
int ImmBranch() const {
|
||||||
switch (BranchType()) {
|
switch (BranchType()) {
|
||||||
case CondBranchType: return ImmCondBranch();
|
case CondBranchType: return ImmCondBranch();
|
||||||
@ -329,6 +352,8 @@ class Instruction {
|
|||||||
// PC-relative addressing instruction.
|
// PC-relative addressing instruction.
|
||||||
Instruction* ImmPCOffsetTarget();
|
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
|
// Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
|
||||||
// a PC-relative addressing instruction.
|
// a PC-relative addressing instruction.
|
||||||
void SetImmPCOffsetTarget(Instruction* target);
|
void SetImmPCOffsetTarget(Instruction* target);
|
||||||
|
@ -346,6 +346,7 @@ void MacroAssembler::Asr(const Register& rd,
|
|||||||
|
|
||||||
void MacroAssembler::B(Label* label) {
|
void MacroAssembler::B(Label* label) {
|
||||||
b(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,
|
void MacroAssembler::Bfi(const Register& rd,
|
||||||
const Register& rn,
|
const Register& rn,
|
||||||
unsigned lsb,
|
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,
|
void MacroAssembler::Cinc(const Register& rd,
|
||||||
const Register& rn,
|
const Register& rn,
|
||||||
Condition cond) {
|
Condition cond) {
|
||||||
@ -1032,6 +1014,7 @@ void MacroAssembler::Ret(const Register& xn) {
|
|||||||
ASSERT(allow_macro_instructions_);
|
ASSERT(allow_macro_instructions_);
|
||||||
ASSERT(!xn.IsZero());
|
ASSERT(!xn.IsZero());
|
||||||
ret(xn);
|
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,
|
void MacroAssembler::Ubfiz(const Register& rd,
|
||||||
const Register& rn,
|
const Register& rn,
|
||||||
unsigned lsb,
|
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) {
|
void MacroAssembler::Uxtb(const Register& rd, const Register& rn) {
|
||||||
ASSERT(allow_macro_instructions_);
|
ASSERT(allow_macro_instructions_);
|
||||||
ASSERT(!rd.IsZero());
|
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.
|
// Pseudo-instructions.
|
||||||
|
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ class MacroAssembler : public Assembler {
|
|||||||
inline void Asr(const Register& rd, const Register& rn, const Register& rm);
|
inline void Asr(const Register& rd, const Register& rn, const Register& rm);
|
||||||
inline void B(Label* label);
|
inline void B(Label* label);
|
||||||
inline void B(Condition cond, 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,
|
inline void Bfi(const Register& rd,
|
||||||
const Register& rn,
|
const Register& rn,
|
||||||
unsigned lsb,
|
unsigned lsb,
|
||||||
@ -226,8 +226,8 @@ class MacroAssembler : public Assembler {
|
|||||||
inline void Blr(const Register& xn);
|
inline void Blr(const Register& xn);
|
||||||
inline void Br(const Register& xn);
|
inline void Br(const Register& xn);
|
||||||
inline void Brk(int code);
|
inline void Brk(int code);
|
||||||
inline void Cbnz(const Register& rt, Label* label);
|
void Cbnz(const Register& rt, Label* label);
|
||||||
inline void Cbz(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 Cinc(const Register& rd, const Register& rn, Condition cond);
|
||||||
inline void Cinv(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);
|
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 Sxtb(const Register& rd, const Register& rn);
|
||||||
inline void Sxth(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 Sxtw(const Register& rd, const Register& rn);
|
||||||
inline void Tbnz(const Register& rt, unsigned bit_pos, Label* label);
|
void Tbnz(const Register& rt, unsigned bit_pos, Label* label);
|
||||||
inline void Tbz(const Register& rt, unsigned bit_pos, Label* label);
|
void Tbz(const Register& rt, unsigned bit_pos, Label* label);
|
||||||
inline void Ubfiz(const Register& rd,
|
inline void Ubfiz(const Register& rd,
|
||||||
const Register& rn,
|
const Register& rn,
|
||||||
unsigned lsb,
|
unsigned lsb,
|
||||||
@ -422,7 +422,6 @@ class MacroAssembler : public Assembler {
|
|||||||
const Register& rn,
|
const Register& rn,
|
||||||
const Register& rm,
|
const Register& rm,
|
||||||
const Register& ra);
|
const Register& ra);
|
||||||
inline void Unreachable();
|
|
||||||
inline void Uxtb(const Register& rd, const Register& rn);
|
inline void Uxtb(const Register& rd, const Register& rn);
|
||||||
inline void Uxth(const Register& rd, const Register& rn);
|
inline void Uxth(const Register& rd, const Register& rn);
|
||||||
inline void Uxtw(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,
|
Heap::RootListIndex map_index,
|
||||||
Register scratch1,
|
Register scratch1,
|
||||||
Register scratch2);
|
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.
|
// emitted is what you specified when creating the scope.
|
||||||
class InstructionAccurateScope BASE_EMBEDDED {
|
class InstructionAccurateScope BASE_EMBEDDED {
|
||||||
public:
|
public:
|
||||||
explicit InstructionAccurateScope(MacroAssembler* masm)
|
InstructionAccurateScope(MacroAssembler* masm, size_t count = 0)
|
||||||
: 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)
|
|
||||||
: masm_(masm), size_(count * kInstructionSize) {
|
: masm_(masm), size_(count * kInstructionSize) {
|
||||||
masm_->StartBlockConstPool();
|
masm_->StartBlockConstPool();
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
masm_->bind(&start_);
|
if (count != 0) {
|
||||||
|
masm_->bind(&start_);
|
||||||
|
}
|
||||||
previous_allow_macro_instructions_ = masm_->allow_macro_instructions();
|
previous_allow_macro_instructions_ = masm_->allow_macro_instructions();
|
||||||
masm_->set_allow_macro_instructions(false);
|
masm_->set_allow_macro_instructions(false);
|
||||||
#endif
|
#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) {
|
TEST(ldr_str_offset) {
|
||||||
INIT_V8();
|
INIT_V8();
|
||||||
SETUP();
|
SETUP();
|
||||||
|
@ -176,10 +176,6 @@
|
|||||||
# BUG(v8:3147). It works on other architectures by accident.
|
# BUG(v8:3147). It works on other architectures by accident.
|
||||||
'regress/regress-conditional-position': [FAIL],
|
'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.
|
# BUG(v8:3156): Fails on gc stress bots.
|
||||||
'compiler/concurrent-invalidate-transition-map': [PASS, ['gc_stress == True', FAIL]],
|
'compiler/concurrent-invalidate-transition-map': [PASS, ['gc_stress == True', FAIL]],
|
||||||
}], # 'arch == a64'
|
}], # 'arch == a64'
|
||||||
|
@ -843,12 +843,6 @@
|
|||||||
|
|
||||||
|
|
||||||
['arch == a64', {
|
['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.
|
# BUG(v8:3152): Runs out of stack in debug mode.
|
||||||
'js1_5/extensions/regress-355497': [FAIL_OK, ['mode == debug', SKIP]],
|
'js1_5/extensions/regress-355497': [FAIL_OK, ['mode == debug', SKIP]],
|
||||||
}], # 'arch == a64'
|
}], # 'arch == a64'
|
||||||
|
Loading…
Reference in New Issue
Block a user