A64: Use a scope utility to allocate scratch registers.
This replaces Tmp0() and Tmp1() with a more flexible scratch register pool. A scope-based utility can temporarily acquire registers from this pool as needed. We no longer have to worry about whether to use Tmp0(), Tmp1() or something else; the scope can just get the next available scratch register. BUG= R=jochen@chromium.org, rmcilroy@chromium.org Review URL: https://codereview.chromium.org/164793003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19768 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
e19d2bd423
commit
19a04c3a69
@ -179,9 +179,9 @@ inline void CPURegList::Combine(const CPURegList& other) {
|
||||
|
||||
inline void CPURegList::Remove(const CPURegList& other) {
|
||||
ASSERT(IsValid());
|
||||
ASSERT(other.type() == type_);
|
||||
ASSERT(other.RegisterSizeInBits() == size_);
|
||||
list_ &= ~other.list();
|
||||
if (other.type() == type_) {
|
||||
list_ &= ~other.list();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -192,10 +192,14 @@ inline void CPURegList::Combine(const CPURegister& other) {
|
||||
}
|
||||
|
||||
|
||||
inline void CPURegList::Remove(const CPURegister& other) {
|
||||
ASSERT(other.type() == type_);
|
||||
ASSERT(other.SizeInBits() == size_);
|
||||
Remove(other.code());
|
||||
inline void CPURegList::Remove(const CPURegister& other1,
|
||||
const CPURegister& other2,
|
||||
const CPURegister& other3,
|
||||
const CPURegister& other4) {
|
||||
if (!other1.IsNone() && (other1.type() == type_)) Remove(other1.code());
|
||||
if (!other2.IsNone() && (other2.type() == type_)) Remove(other2.code());
|
||||
if (!other3.IsNone() && (other3.type() == type_)) Remove(other3.code());
|
||||
if (!other4.IsNone() && (other4.type() == type_)) Remove(other4.code());
|
||||
}
|
||||
|
||||
|
||||
|
@ -461,6 +461,11 @@ class CPURegList {
|
||||
return list_;
|
||||
}
|
||||
|
||||
inline void set_list(RegList new_list) {
|
||||
ASSERT(IsValid());
|
||||
list_ = new_list;
|
||||
}
|
||||
|
||||
// Combine another CPURegList into this one. Registers that already exist in
|
||||
// this list are left unchanged. The type and size of the registers in the
|
||||
// 'other' list must match those in this list.
|
||||
@ -471,9 +476,12 @@ class CPURegList {
|
||||
// in the 'other' list must match those in this list.
|
||||
void Remove(const CPURegList& other);
|
||||
|
||||
// Variants of Combine and Remove which take a single register.
|
||||
// Variants of Combine and Remove which take CPURegisters.
|
||||
void Combine(const CPURegister& other);
|
||||
void Remove(const CPURegister& other);
|
||||
void Remove(const CPURegister& other1,
|
||||
const CPURegister& other2 = NoCPUReg,
|
||||
const CPURegister& other3 = NoCPUReg,
|
||||
const CPURegister& other4 = NoCPUReg);
|
||||
|
||||
// Variants of Combine and Remove which take a single register by its code;
|
||||
// the type and size of the register is inferred from this list.
|
||||
@ -503,9 +511,17 @@ class CPURegList {
|
||||
return list_ == 0;
|
||||
}
|
||||
|
||||
bool IncludesAliasOf(const CPURegister& other) const {
|
||||
bool IncludesAliasOf(const CPURegister& other1,
|
||||
const CPURegister& other2 = NoCPUReg,
|
||||
const CPURegister& other3 = NoCPUReg,
|
||||
const CPURegister& other4 = NoCPUReg) const {
|
||||
ASSERT(IsValid());
|
||||
return (type_ == other.type()) && (other.Bit() & list_);
|
||||
RegList list = 0;
|
||||
if (!other1.IsNone() && (other1.type() == type_)) list |= other1.Bit();
|
||||
if (!other2.IsNone() && (other2.type() == type_)) list |= other2.Bit();
|
||||
if (!other3.IsNone() && (other3.type() == type_)) list |= other3.Bit();
|
||||
if (!other4.IsNone() && (other4.type() == type_)) list |= other4.Bit();
|
||||
return (list_ & list) != 0;
|
||||
}
|
||||
|
||||
int Count() const {
|
||||
|
@ -1222,7 +1222,8 @@ void MathPowStub::Generate(MacroAssembler* masm) {
|
||||
// A64 simulator does not currently simulate FPCR (where the rounding
|
||||
// mode is set), so test the operation with some debug code.
|
||||
if (masm->emit_debug_code()) {
|
||||
Register temp = masm->Tmp1();
|
||||
UseScratchRegisterScope temps(masm);
|
||||
Register temp = temps.AcquireX();
|
||||
// d5 zero_double The value +0.0 as a double.
|
||||
__ Fneg(scratch0_double, zero_double);
|
||||
// Verify that we correctly generated +0.0 and -0.0.
|
||||
@ -1500,7 +1501,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
|
||||
if (__ emit_debug_code()) {
|
||||
// Verify that the slot below fp[kSPOffset]-8 points to the return location
|
||||
// (currently in x12).
|
||||
Register temp = masm->Tmp1();
|
||||
UseScratchRegisterScope temps(masm);
|
||||
Register temp = temps.AcquireX();
|
||||
__ Ldr(temp, MemOperand(fp, ExitFrameConstants::kSPOffset));
|
||||
__ Ldr(temp, MemOperand(temp, -static_cast<int64_t>(kXRegSizeInBytes)));
|
||||
__ Cmp(temp, x12);
|
||||
@ -4720,9 +4722,10 @@ void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
|
||||
masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode);
|
||||
InformIncrementalMarker(masm);
|
||||
regs_.Restore(masm); // Restore the extra scratch registers we used.
|
||||
|
||||
__ RememberedSetHelper(object_,
|
||||
address_,
|
||||
value_,
|
||||
value_, // scratch1
|
||||
save_fp_regs_mode_,
|
||||
MacroAssembler::kReturnAtEnd);
|
||||
|
||||
@ -4783,7 +4786,7 @@ void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
|
||||
if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
|
||||
__ RememberedSetHelper(object_,
|
||||
address_,
|
||||
value_,
|
||||
value_, // scratch1
|
||||
save_fp_regs_mode_,
|
||||
MacroAssembler::kReturnAtEnd);
|
||||
} else {
|
||||
@ -4826,7 +4829,7 @@ void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
|
||||
if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
|
||||
__ RememberedSetHelper(object_,
|
||||
address_,
|
||||
value_,
|
||||
value_, // scratch1
|
||||
save_fp_regs_mode_,
|
||||
MacroAssembler::kReturnAtEnd);
|
||||
} else {
|
||||
@ -4859,7 +4862,7 @@ void RecordWriteStub::Generate(MacroAssembler* masm) {
|
||||
if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
|
||||
__ RememberedSetHelper(object_,
|
||||
address_,
|
||||
value_,
|
||||
value_, // scratch1
|
||||
save_fp_regs_mode_,
|
||||
MacroAssembler::kReturnAtEnd);
|
||||
}
|
||||
@ -5090,12 +5093,11 @@ void NameDictionaryLookupStub::GeneratePositiveLookup(
|
||||
__ Add(scratch2, scratch2, Operand(scratch2, LSL, 1));
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
UseScratchRegisterScope temps(masm);
|
||||
Register scratch3 = temps.AcquireX();
|
||||
__ Add(scratch2, elements, Operand(scratch2, LSL, kPointerSizeLog2));
|
||||
// TODO(jbramley): We need another scratch here, but some callers can't
|
||||
// provide a scratch3 so we have to use Tmp1(). We should find a clean way
|
||||
// to make it unavailable to the MacroAssembler for a short time.
|
||||
__ Ldr(__ Tmp1(), FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ Cmp(name, __ Tmp1());
|
||||
__ Ldr(scratch3, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ Cmp(name, scratch3);
|
||||
__ B(eq, done);
|
||||
}
|
||||
|
||||
|
@ -175,8 +175,7 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
ASSERT((object_regs & non_object_regs) == 0);
|
||||
ASSERT((scratch.Bit() & object_regs) == 0);
|
||||
ASSERT((scratch.Bit() & non_object_regs) == 0);
|
||||
ASSERT((ip0.Bit() & (object_regs | non_object_regs)) == 0);
|
||||
ASSERT((ip1.Bit() & (object_regs | non_object_regs)) == 0);
|
||||
ASSERT((masm->TmpList()->list() & (object_regs | non_object_regs)) == 0);
|
||||
STATIC_ASSERT(kSmiValueSize == 32);
|
||||
|
||||
CPURegList non_object_list =
|
||||
|
@ -340,6 +340,9 @@ const int Deoptimizer::table_entry_size_ = 2 * kInstructionSize;
|
||||
|
||||
|
||||
void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
|
||||
UseScratchRegisterScope temps(masm());
|
||||
Register entry_id = temps.AcquireX();
|
||||
|
||||
// Create a sequence of deoptimization entries.
|
||||
// Note that registers are still live when jumping to an entry.
|
||||
Label done;
|
||||
@ -354,15 +357,13 @@ void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
|
||||
for (int i = 0; i < count(); i++) {
|
||||
int start = masm()->pc_offset();
|
||||
USE(start);
|
||||
__ movz(masm()->Tmp0(), i);
|
||||
__ movz(entry_id, i);
|
||||
__ b(&done);
|
||||
ASSERT(masm()->pc_offset() - start == table_entry_size_);
|
||||
}
|
||||
}
|
||||
__ Bind(&done);
|
||||
// TODO(all): We need to add some kind of assertion to verify that Tmp0()
|
||||
// is not clobbered by Push.
|
||||
__ Push(masm()->Tmp0());
|
||||
__ Push(entry_id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -86,10 +86,10 @@ class JumpPatchSite BASE_EMBEDDED {
|
||||
}
|
||||
|
||||
void EmitJumpIfEitherNotSmi(Register reg1, Register reg2, Label* target) {
|
||||
// We need to use ip0, so don't allow access to the MacroAssembler.
|
||||
InstructionAccurateScope scope(masm_);
|
||||
__ orr(ip0, reg1, reg2);
|
||||
EmitJumpIfNotSmi(ip0, target);
|
||||
UseScratchRegisterScope temps(masm_);
|
||||
Register temp = temps.AcquireX();
|
||||
__ Orr(temp, reg1, reg2);
|
||||
EmitJumpIfNotSmi(temp, target);
|
||||
}
|
||||
|
||||
void EmitPatchInfo() {
|
||||
|
@ -1074,6 +1074,7 @@ static void KeyedStoreGenerateGenericHelper(
|
||||
FAST_DOUBLE_ELEMENTS,
|
||||
receiver_map,
|
||||
x10,
|
||||
x11,
|
||||
slow);
|
||||
ASSERT(receiver_map.Is(x3)); // Transition code expects map in x3.
|
||||
AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
|
||||
@ -1088,6 +1089,7 @@ static void KeyedStoreGenerateGenericHelper(
|
||||
FAST_ELEMENTS,
|
||||
receiver_map,
|
||||
x10,
|
||||
x11,
|
||||
slow);
|
||||
ASSERT(receiver_map.Is(x3)); // Transition code expects map in x3.
|
||||
mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
|
||||
@ -1104,6 +1106,7 @@ static void KeyedStoreGenerateGenericHelper(
|
||||
FAST_ELEMENTS,
|
||||
receiver_map,
|
||||
x10,
|
||||
x11,
|
||||
slow);
|
||||
ASSERT(receiver_map.Is(x3)); // Transition code expects map in x3.
|
||||
mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
|
||||
|
@ -845,7 +845,13 @@ bool LCodeGen::GenerateDeoptJumpTable() {
|
||||
}
|
||||
if (deopt_jump_table_[i].needs_frame) {
|
||||
ASSERT(!info()->saves_caller_doubles());
|
||||
__ Mov(__ Tmp0(), Operand(ExternalReference::ForDeoptEntry(entry)));
|
||||
|
||||
UseScratchRegisterScope temps(masm());
|
||||
Register stub_deopt_entry = temps.AcquireX();
|
||||
Register stub_marker = temps.AcquireX();
|
||||
|
||||
__ Mov(stub_deopt_entry,
|
||||
Operand(ExternalReference::ForDeoptEntry(entry)));
|
||||
if (needs_frame.is_bound()) {
|
||||
__ B(&needs_frame);
|
||||
} else {
|
||||
@ -853,12 +859,11 @@ bool LCodeGen::GenerateDeoptJumpTable() {
|
||||
// This variant of deopt can only be used with stubs. Since we don't
|
||||
// have a function pointer to install in the stack frame that we're
|
||||
// building, install a special marker there instead.
|
||||
// TODO(jochen): Revisit the use of TmpX().
|
||||
ASSERT(info()->IsStub());
|
||||
__ Mov(__ Tmp1(), Operand(Smi::FromInt(StackFrame::STUB)));
|
||||
__ Push(lr, fp, cp, __ Tmp1());
|
||||
__ Mov(stub_marker, Operand(Smi::FromInt(StackFrame::STUB)));
|
||||
__ Push(lr, fp, cp, stub_marker);
|
||||
__ Add(fp, __ StackPointer(), 2 * kPointerSize);
|
||||
__ Call(__ Tmp0());
|
||||
__ Call(stub_deopt_entry);
|
||||
}
|
||||
} else {
|
||||
if (info()->saves_caller_doubles()) {
|
||||
|
@ -588,7 +588,8 @@ void MacroAssembler::Fcmp(const FPRegister& fn, const FPRegister& fm) {
|
||||
void MacroAssembler::Fcmp(const FPRegister& fn, double value) {
|
||||
ASSERT(allow_macro_instructions_);
|
||||
if (value != 0.0) {
|
||||
FPRegister tmp = AppropriateTempFor(fn);
|
||||
UseScratchRegisterScope temps(this);
|
||||
FPRegister tmp = temps.AcquireSameSizeAs(fn);
|
||||
Fmov(tmp, value);
|
||||
fcmp(fn, tmp);
|
||||
} else {
|
||||
@ -742,16 +743,19 @@ void MacroAssembler::Fmov(FPRegister fd, double imm) {
|
||||
// These cases can be handled by the Assembler.
|
||||
fmov(fd, imm);
|
||||
} else {
|
||||
UseScratchRegisterScope temps(this);
|
||||
// TODO(all): The Assembler would try to relocate the immediate with
|
||||
// Assembler::ldr(const FPRegister& ft, double imm) but it is not
|
||||
// implemented yet.
|
||||
if (fd.SizeInBits() == kDRegSize) {
|
||||
Mov(Tmp0(), double_to_rawbits(imm));
|
||||
Fmov(fd, Tmp0());
|
||||
Register tmp = temps.AcquireX();
|
||||
Mov(tmp, double_to_rawbits(imm));
|
||||
Fmov(fd, tmp);
|
||||
} else {
|
||||
ASSERT(fd.SizeInBits() == kSRegSize);
|
||||
Mov(WTmp0(), float_to_rawbits(static_cast<float>(imm)));
|
||||
Fmov(fd, WTmp0());
|
||||
Register tmp = temps.AcquireW();
|
||||
Mov(tmp, float_to_rawbits(static_cast<float>(imm)));
|
||||
Fmov(fd, tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1351,9 +1355,11 @@ void MacroAssembler::JumpIfBothSmi(Register value1,
|
||||
Label* both_smi_label,
|
||||
Label* not_smi_label) {
|
||||
STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
|
||||
UseScratchRegisterScope temps(this);
|
||||
Register tmp = temps.AcquireX();
|
||||
// Check if both tag bits are clear.
|
||||
Orr(Tmp0(), value1, value2);
|
||||
JumpIfSmi(Tmp0(), both_smi_label, not_smi_label);
|
||||
Orr(tmp, value1, value2);
|
||||
JumpIfSmi(tmp, both_smi_label, not_smi_label);
|
||||
}
|
||||
|
||||
|
||||
@ -1362,9 +1368,11 @@ void MacroAssembler::JumpIfEitherSmi(Register value1,
|
||||
Label* either_smi_label,
|
||||
Label* not_smi_label) {
|
||||
STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
|
||||
UseScratchRegisterScope temps(this);
|
||||
Register tmp = temps.AcquireX();
|
||||
// Check if either tag bit is clear.
|
||||
And(Tmp0(), value1, value2);
|
||||
JumpIfSmi(Tmp0(), either_smi_label, not_smi_label);
|
||||
And(tmp, value1, value2);
|
||||
JumpIfSmi(tmp, either_smi_label, not_smi_label);
|
||||
}
|
||||
|
||||
|
||||
@ -1437,8 +1445,10 @@ void MacroAssembler::IsObjectJSStringType(Register object,
|
||||
|
||||
|
||||
void MacroAssembler::Push(Handle<Object> handle) {
|
||||
Mov(Tmp0(), Operand(handle));
|
||||
Push(Tmp0());
|
||||
UseScratchRegisterScope temps(this);
|
||||
Register tmp = temps.AcquireX();
|
||||
Mov(tmp, Operand(handle));
|
||||
Push(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -520,7 +520,6 @@ class MacroAssembler : public Assembler {
|
||||
//
|
||||
// Other than the registers passed into Pop, the stack pointer and (possibly)
|
||||
// the system stack pointer, these methods do not modify any other registers.
|
||||
// Scratch registers such as Tmp0() and Tmp1() are preserved.
|
||||
void Push(const CPURegister& src0, const CPURegister& src1 = NoReg,
|
||||
const CPURegister& src2 = NoReg, const CPURegister& src3 = NoReg);
|
||||
void Pop(const CPURegister& dst0, const CPURegister& dst1 = NoReg,
|
||||
@ -746,7 +745,7 @@ class MacroAssembler : public Assembler {
|
||||
|
||||
// Set the current stack pointer, but don't generate any code.
|
||||
inline void SetStackPointer(const Register& stack_pointer) {
|
||||
ASSERT(!AreAliased(stack_pointer, Tmp0(), Tmp1()));
|
||||
ASSERT(!TmpList()->IncludesAliasOf(stack_pointer));
|
||||
sp_ = stack_pointer;
|
||||
}
|
||||
|
||||
@ -940,13 +939,13 @@ class MacroAssembler : public Assembler {
|
||||
|
||||
// Copy fields from 'src' to 'dst', where both are tagged objects.
|
||||
// The 'temps' list is a list of X registers which can be used for scratch
|
||||
// values. The temps list must include at least one register, and it must not
|
||||
// contain Tmp0() or Tmp1().
|
||||
// values. The temps list must include at least one register.
|
||||
//
|
||||
// Currently, CopyFields cannot make use of more than three registers from
|
||||
// the 'temps' list.
|
||||
//
|
||||
// As with several MacroAssembler methods, Tmp0() and Tmp1() will be used.
|
||||
// CopyFields expects to be able to take at least two registers from
|
||||
// MacroAssembler::TmpList().
|
||||
void CopyFields(Register dst, Register src, CPURegList temps, unsigned count);
|
||||
|
||||
// Copies a number of bytes from src to dst. All passed registers are
|
||||
@ -1449,7 +1448,6 @@ class MacroAssembler : public Assembler {
|
||||
void LoadElementsKind(Register result, Register object);
|
||||
|
||||
// Compare the object in a register to a value from the root list.
|
||||
// Uses the Tmp0() register as scratch.
|
||||
void CompareRoot(const Register& obj, Heap::RootListIndex index);
|
||||
|
||||
// Compare the object in a register to a value and jump if they are equal.
|
||||
@ -1556,7 +1554,8 @@ class MacroAssembler : public Assembler {
|
||||
// on access to global objects across environments. The holder register
|
||||
// is left untouched, whereas both scratch registers are clobbered.
|
||||
void CheckAccessGlobalProxy(Register holder_reg,
|
||||
Register scratch,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* miss);
|
||||
|
||||
// Hash the interger value in 'key' register.
|
||||
@ -1588,8 +1587,6 @@ class MacroAssembler : public Assembler {
|
||||
// Frames.
|
||||
|
||||
// Activation support.
|
||||
// Note that Tmp0() and Tmp1() are used as a scratch registers. This is safe
|
||||
// because these methods are not used in Crankshaft.
|
||||
void EnterFrame(StackFrame::Type type);
|
||||
void LeaveFrame(StackFrame::Type type);
|
||||
|
||||
@ -1678,7 +1675,7 @@ class MacroAssembler : public Assembler {
|
||||
void LoadContext(Register dst, int context_chain_length);
|
||||
|
||||
// Emit code for a flooring division by a constant. The dividend register is
|
||||
// unchanged and Tmp0() gets clobbered. Dividend and result must be different.
|
||||
// unchanged. Dividend and result must be different.
|
||||
void FlooringDiv(Register result, Register dividend, int32_t divisor);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -1704,7 +1701,7 @@ class MacroAssembler : public Assembler {
|
||||
// in new space.
|
||||
void RememberedSetHelper(Register object, // Used for debug code.
|
||||
Register addr,
|
||||
Register scratch,
|
||||
Register scratch1,
|
||||
SaveFPRegsMode save_fp,
|
||||
RememberedSetFinalAction and_then);
|
||||
|
||||
@ -1889,7 +1886,8 @@ class MacroAssembler : public Assembler {
|
||||
ElementsKind expected_kind,
|
||||
ElementsKind transitioned_kind,
|
||||
Register map_in_out,
|
||||
Register scratch,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* no_map_match);
|
||||
|
||||
void LoadGlobalFunction(int index, Register function);
|
||||
@ -1900,72 +1898,8 @@ class MacroAssembler : public Assembler {
|
||||
Register map,
|
||||
Register scratch);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Set the registers used internally by the MacroAssembler as scratch
|
||||
// registers. These registers are used to implement behaviours which are not
|
||||
// directly supported by A64, and where an intermediate result is required.
|
||||
//
|
||||
// Both tmp0 and tmp1 may be set to any X register except for xzr, sp,
|
||||
// and StackPointer(). Also, they must not be the same register (though they
|
||||
// may both be NoReg).
|
||||
//
|
||||
// It is valid to set either or both of these registers to NoReg if you don't
|
||||
// want the MacroAssembler to use any scratch registers. In a debug build, the
|
||||
// Assembler will assert that any registers it uses are valid. Be aware that
|
||||
// this check is not present in release builds. If this is a problem, use the
|
||||
// Assembler directly.
|
||||
void SetScratchRegisters(const Register& tmp0, const Register& tmp1) {
|
||||
// V8 assumes the macro assembler uses ip0 and ip1 as temp registers.
|
||||
ASSERT(tmp0.IsNone() || tmp0.Is(ip0));
|
||||
ASSERT(tmp1.IsNone() || tmp1.Is(ip1));
|
||||
|
||||
ASSERT(!AreAliased(xzr, csp, tmp0, tmp1));
|
||||
ASSERT(!AreAliased(StackPointer(), tmp0, tmp1));
|
||||
tmp0_ = tmp0;
|
||||
tmp1_ = tmp1;
|
||||
}
|
||||
|
||||
const Register& Tmp0() const {
|
||||
return tmp0_;
|
||||
}
|
||||
|
||||
const Register& Tmp1() const {
|
||||
return tmp1_;
|
||||
}
|
||||
|
||||
const Register WTmp0() const {
|
||||
return Register::Create(tmp0_.code(), kWRegSize);
|
||||
}
|
||||
|
||||
const Register WTmp1() const {
|
||||
return Register::Create(tmp1_.code(), kWRegSize);
|
||||
}
|
||||
|
||||
void SetFPScratchRegister(const FPRegister& fptmp0) {
|
||||
fptmp0_ = fptmp0;
|
||||
}
|
||||
|
||||
const FPRegister& FPTmp0() const {
|
||||
return fptmp0_;
|
||||
}
|
||||
|
||||
const Register AppropriateTempFor(
|
||||
const Register& target,
|
||||
const CPURegister& forbidden = NoCPUReg) const {
|
||||
Register candidate = forbidden.Is(Tmp0()) ? Tmp1() : Tmp0();
|
||||
ASSERT(!candidate.Is(target));
|
||||
return Register::Create(candidate.code(), target.SizeInBits());
|
||||
}
|
||||
|
||||
const FPRegister AppropriateTempFor(
|
||||
const FPRegister& target,
|
||||
const CPURegister& forbidden = NoCPUReg) const {
|
||||
USE(forbidden);
|
||||
FPRegister candidate = FPTmp0();
|
||||
ASSERT(!candidate.Is(forbidden));
|
||||
ASSERT(!candidate.Is(target));
|
||||
return FPRegister::Create(candidate.code(), target.SizeInBits());
|
||||
}
|
||||
CPURegList* TmpList() { return &tmp_list_; }
|
||||
CPURegList* FPTmpList() { return &fptmp_list_; }
|
||||
|
||||
// Like printf, but print at run-time from generated code.
|
||||
//
|
||||
@ -1978,7 +1912,7 @@ class MacroAssembler : public Assembler {
|
||||
// size.
|
||||
//
|
||||
// The following registers cannot be printed:
|
||||
// Tmp0(), Tmp1(), StackPointer(), csp.
|
||||
// StackPointer(), csp.
|
||||
//
|
||||
// This function automatically preserves caller-saved registers so that
|
||||
// calling code can use Printf at any point without having to worry about
|
||||
@ -2063,11 +1997,14 @@ class MacroAssembler : public Assembler {
|
||||
// These each implement CopyFields in a different way.
|
||||
void CopyFieldsLoopPairsHelper(Register dst, Register src, unsigned count,
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3);
|
||||
Register scratch3, Register scratch4,
|
||||
Register scratch5);
|
||||
void CopyFieldsUnrolledPairsHelper(Register dst, Register src, unsigned count,
|
||||
Register scratch1, Register scratch2);
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3, Register scratch4);
|
||||
void CopyFieldsUnrolledHelper(Register dst, Register src, unsigned count,
|
||||
Register scratch1);
|
||||
Register scratch1, Register scratch2,
|
||||
Register scratch3);
|
||||
|
||||
// The actual Push and Pop implementations. These don't generate any code
|
||||
// other than that required for the push or pop. This allows
|
||||
@ -2148,10 +2085,9 @@ class MacroAssembler : public Assembler {
|
||||
// The register to use as a stack pointer for stack operations.
|
||||
Register sp_;
|
||||
|
||||
// Scratch registers used internally by the MacroAssembler.
|
||||
Register tmp0_;
|
||||
Register tmp1_;
|
||||
FPRegister fptmp0_;
|
||||
// Scratch registers available for use by the MacroAssembler.
|
||||
CPURegList tmp_list_;
|
||||
CPURegList fptmp_list_;
|
||||
|
||||
void InitializeNewString(Register string,
|
||||
Register length,
|
||||
@ -2232,6 +2168,49 @@ class InstructionAccurateScope BASE_EMBEDDED {
|
||||
};
|
||||
|
||||
|
||||
// This scope utility allows scratch registers to be managed safely. The
|
||||
// MacroAssembler's TmpList() (and FPTmpList()) is used as a pool of scratch
|
||||
// registers. These registers can be allocated on demand, and will be returned
|
||||
// at the end of the scope.
|
||||
//
|
||||
// When the scope ends, the MacroAssembler's lists will be restored to their
|
||||
// original state, even if the lists were modified by some other means.
|
||||
class UseScratchRegisterScope {
|
||||
public:
|
||||
explicit UseScratchRegisterScope(MacroAssembler* masm)
|
||||
: available_(masm->TmpList()),
|
||||
availablefp_(masm->FPTmpList()),
|
||||
old_available_(available_->list()),
|
||||
old_availablefp_(availablefp_->list()) {
|
||||
ASSERT(available_->type() == CPURegister::kRegister);
|
||||
ASSERT(availablefp_->type() == CPURegister::kFPRegister);
|
||||
}
|
||||
|
||||
~UseScratchRegisterScope();
|
||||
|
||||
// Take a register from the appropriate temps list. It will be returned
|
||||
// automatically when the scope ends.
|
||||
Register AcquireW() { return AcquireNextAvailable(available_).W(); }
|
||||
Register AcquireX() { return AcquireNextAvailable(available_).X(); }
|
||||
FPRegister AcquireS() { return AcquireNextAvailable(availablefp_).S(); }
|
||||
FPRegister AcquireD() { return AcquireNextAvailable(availablefp_).D(); }
|
||||
|
||||
Register AcquireSameSizeAs(const Register& reg);
|
||||
FPRegister AcquireSameSizeAs(const FPRegister& reg);
|
||||
|
||||
private:
|
||||
static CPURegister AcquireNextAvailable(CPURegList* available);
|
||||
|
||||
// Available scratch registers.
|
||||
CPURegList* available_; // kRegister
|
||||
CPURegList* availablefp_; // kFPRegister
|
||||
|
||||
// The state of the available lists at the start of this scope.
|
||||
RegList old_available_; // kRegister
|
||||
RegList old_availablefp_; // kFPRegister
|
||||
};
|
||||
|
||||
|
||||
inline MemOperand ContextMemOperand(Register context, int index) {
|
||||
return MemOperand(context, Context::SlotOffset(index));
|
||||
}
|
||||
|
@ -903,7 +903,8 @@ Register StubCompiler::CheckPrototypes(Handle<HeapType> type,
|
||||
// the map check so that we know that the object is actually a global
|
||||
// object.
|
||||
if (current_map->IsJSGlobalProxyMap()) {
|
||||
__ CheckAccessGlobalProxy(reg, scratch2, miss);
|
||||
UseScratchRegisterScope temps(masm());
|
||||
__ CheckAccessGlobalProxy(reg, scratch2, temps.AcquireX(), miss);
|
||||
} else if (current_map->IsJSGlobalObjectMap()) {
|
||||
GenerateCheckPropertyCell(
|
||||
masm(), Handle<JSGlobalObject>::cast(current), name,
|
||||
@ -940,7 +941,7 @@ Register StubCompiler::CheckPrototypes(Handle<HeapType> type,
|
||||
ASSERT(current_map->IsJSGlobalProxyMap() ||
|
||||
!current_map->is_access_check_needed());
|
||||
if (current_map->IsJSGlobalProxyMap()) {
|
||||
__ CheckAccessGlobalProxy(reg, scratch1, miss);
|
||||
__ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss);
|
||||
}
|
||||
|
||||
// Return the register containing the holder.
|
||||
|
@ -5614,51 +5614,58 @@ TEST(fcmp) {
|
||||
|
||||
// Some of these tests require a floating-point scratch register assigned to
|
||||
// the macro assembler, but most do not.
|
||||
__ SetFPScratchRegister(NoFPReg);
|
||||
{
|
||||
// We're going to mess around with the available scratch registers in this
|
||||
// test. A UseScratchRegisterScope will make sure that they are restored to
|
||||
// the default values once we're finished.
|
||||
UseScratchRegisterScope temps(&masm);
|
||||
masm.FPTmpList()->set_list(0);
|
||||
|
||||
__ Fmov(s8, 0.0);
|
||||
__ Fmov(s9, 0.5);
|
||||
__ Mov(w18, 0x7f800001); // Single precision NaN.
|
||||
__ Fmov(s18, w18);
|
||||
__ Fmov(s8, 0.0);
|
||||
__ Fmov(s9, 0.5);
|
||||
__ Mov(w18, 0x7f800001); // Single precision NaN.
|
||||
__ Fmov(s18, w18);
|
||||
|
||||
__ Fcmp(s8, s8);
|
||||
__ Mrs(x0, NZCV);
|
||||
__ Fcmp(s8, s9);
|
||||
__ Mrs(x1, NZCV);
|
||||
__ Fcmp(s9, s8);
|
||||
__ Mrs(x2, NZCV);
|
||||
__ Fcmp(s8, s18);
|
||||
__ Mrs(x3, NZCV);
|
||||
__ Fcmp(s18, s18);
|
||||
__ Mrs(x4, NZCV);
|
||||
__ Fcmp(s8, 0.0);
|
||||
__ Mrs(x5, NZCV);
|
||||
__ SetFPScratchRegister(d0);
|
||||
__ Fcmp(s8, 255.0);
|
||||
__ SetFPScratchRegister(NoFPReg);
|
||||
__ Mrs(x6, NZCV);
|
||||
__ Fcmp(s8, s8);
|
||||
__ Mrs(x0, NZCV);
|
||||
__ Fcmp(s8, s9);
|
||||
__ Mrs(x1, NZCV);
|
||||
__ Fcmp(s9, s8);
|
||||
__ Mrs(x2, NZCV);
|
||||
__ Fcmp(s8, s18);
|
||||
__ Mrs(x3, NZCV);
|
||||
__ Fcmp(s18, s18);
|
||||
__ Mrs(x4, NZCV);
|
||||
__ Fcmp(s8, 0.0);
|
||||
__ Mrs(x5, NZCV);
|
||||
masm.FPTmpList()->set_list(d0.Bit());
|
||||
__ Fcmp(s8, 255.0);
|
||||
masm.FPTmpList()->set_list(0);
|
||||
__ Mrs(x6, NZCV);
|
||||
|
||||
__ Fmov(d19, 0.0);
|
||||
__ Fmov(d20, 0.5);
|
||||
__ Mov(x21, 0x7ff0000000000001UL); // Double precision NaN.
|
||||
__ Fmov(d21, x21);
|
||||
__ Fmov(d19, 0.0);
|
||||
__ Fmov(d20, 0.5);
|
||||
__ Mov(x21, 0x7ff0000000000001UL); // Double precision NaN.
|
||||
__ Fmov(d21, x21);
|
||||
|
||||
__ Fcmp(d19, d19);
|
||||
__ Mrs(x10, NZCV);
|
||||
__ Fcmp(d19, d20);
|
||||
__ Mrs(x11, NZCV);
|
||||
__ Fcmp(d20, d19);
|
||||
__ Mrs(x12, NZCV);
|
||||
__ Fcmp(d19, d21);
|
||||
__ Mrs(x13, NZCV);
|
||||
__ Fcmp(d21, d21);
|
||||
__ Mrs(x14, NZCV);
|
||||
__ Fcmp(d19, 0.0);
|
||||
__ Mrs(x15, NZCV);
|
||||
masm.FPTmpList()->set_list(d0.Bit());
|
||||
__ Fcmp(d19, 12.3456);
|
||||
masm.FPTmpList()->set_list(0);
|
||||
__ Mrs(x16, NZCV);
|
||||
}
|
||||
|
||||
__ Fcmp(d19, d19);
|
||||
__ Mrs(x10, NZCV);
|
||||
__ Fcmp(d19, d20);
|
||||
__ Mrs(x11, NZCV);
|
||||
__ Fcmp(d20, d19);
|
||||
__ Mrs(x12, NZCV);
|
||||
__ Fcmp(d19, d21);
|
||||
__ Mrs(x13, NZCV);
|
||||
__ Fcmp(d21, d21);
|
||||
__ Mrs(x14, NZCV);
|
||||
__ Fcmp(d19, 0.0);
|
||||
__ Mrs(x15, NZCV);
|
||||
__ SetFPScratchRegister(d0);
|
||||
__ Fcmp(d19, 12.3456);
|
||||
__ SetFPScratchRegister(NoFPReg);
|
||||
__ Mrs(x16, NZCV);
|
||||
END();
|
||||
|
||||
RUN();
|
||||
|
@ -323,11 +323,10 @@ void RegisterDump::Dump(MacroAssembler* masm) {
|
||||
ASSERT(__ StackPointer().Is(csp));
|
||||
|
||||
// Ensure that we don't unintentionally clobber any registers.
|
||||
Register old_tmp0 = __ Tmp0();
|
||||
Register old_tmp1 = __ Tmp1();
|
||||
FPRegister old_fptmp0 = __ FPTmp0();
|
||||
__ SetScratchRegisters(NoReg, NoReg);
|
||||
__ SetFPScratchRegister(NoFPReg);
|
||||
RegList old_tmp_list = masm->TmpList()->list();
|
||||
RegList old_fptmp_list = masm->FPTmpList()->list();
|
||||
masm->TmpList()->set_list(0);
|
||||
masm->FPTmpList()->set_list(0);
|
||||
|
||||
// Preserve some temporary registers.
|
||||
Register dump_base = x0;
|
||||
@ -419,8 +418,8 @@ void RegisterDump::Dump(MacroAssembler* masm) {
|
||||
__ Ldr(dump2, MemOperand(dump2, dump2.code() * kXRegSizeInBytes));
|
||||
|
||||
// Restore the MacroAssembler's scratch registers.
|
||||
__ SetScratchRegisters(old_tmp0, old_tmp1);
|
||||
__ SetFPScratchRegister(old_fptmp0);
|
||||
masm->TmpList()->set_list(old_tmp_list);
|
||||
masm->FPTmpList()->set_list(old_fptmp_list);
|
||||
|
||||
completed_ = true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user