| Index: src/a64/code-stubs-a64.h
|
| diff --git a/src/arm/code-stubs-arm.h b/src/a64/code-stubs-a64.h
|
| similarity index 57%
|
| copy from src/arm/code-stubs-arm.h
|
| copy to src/a64/code-stubs-a64.h
|
| index 7a371f169490954dea366b6cde955d4936174989..0709bfc5118e28cb92e66c195d19452411bdfee0 100644
|
| --- a/src/arm/code-stubs-arm.h
|
| +++ b/src/a64/code-stubs-a64.h
|
| @@ -1,4 +1,4 @@
|
| -// Copyright 2012 the V8 project authors. All rights reserved.
|
| +// Copyright 2013 the V8 project authors. All rights reserved.
|
| // Redistribution and use in source and binary forms, with or without
|
| // modification, are permitted provided that the following conditions are
|
| // met:
|
| @@ -25,8 +25,8 @@
|
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
| -#ifndef V8_ARM_CODE_STUBS_ARM_H_
|
| -#define V8_ARM_CODE_STUBS_ARM_H_
|
| +#ifndef V8_A64_CODE_STUBS_A64_H_
|
| +#define V8_A64_CODE_STUBS_A64_H_
|
|
|
| #include "ic-inl.h"
|
|
|
| @@ -40,7 +40,7 @@ void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code);
|
| class StoreBufferOverflowStub: public PlatformCodeStub {
|
| public:
|
| explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
|
| - : save_doubles_(save_fp) {}
|
| + : save_doubles_(save_fp) { }
|
|
|
| void Generate(MacroAssembler* masm);
|
|
|
| @@ -57,20 +57,7 @@ class StoreBufferOverflowStub: public PlatformCodeStub {
|
|
|
| class StringHelper : public AllStatic {
|
| public:
|
| - // Generate code for copying a large number of characters. This function
|
| - // is allowed to spend extra time setting up conditions to make copying
|
| - // faster. Copying of overlapping regions is not supported.
|
| - // Dest register ends at the position after the last character written.
|
| - static void GenerateCopyCharactersLong(MacroAssembler* masm,
|
| - Register dest,
|
| - Register src,
|
| - Register count,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Register scratch3,
|
| - Register scratch4,
|
| - int flags);
|
| -
|
| + // TODO(all): These don't seem to be used any more. Delete them.
|
|
|
| // Generate string hash.
|
| static void GenerateHashInit(MacroAssembler* masm,
|
| @@ -82,101 +69,19 @@ class StringHelper : public AllStatic {
|
| Register character);
|
|
|
| static void GenerateHashGetHash(MacroAssembler* masm,
|
| - Register hash);
|
| + Register hash,
|
| + Register scratch);
|
|
|
| private:
|
| DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
|
| };
|
|
|
|
|
| -class SubStringStub: public PlatformCodeStub {
|
| - public:
|
| - SubStringStub() {}
|
| -
|
| - private:
|
| - Major MajorKey() { return SubString; }
|
| - int MinorKey() { return 0; }
|
| -
|
| - void Generate(MacroAssembler* masm);
|
| -};
|
| -
|
| -
|
| -
|
| -class StringCompareStub: public PlatformCodeStub {
|
| - public:
|
| - StringCompareStub() { }
|
| -
|
| - // Compares two flat ASCII strings and returns result in r0.
|
| - static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
| - Register left,
|
| - Register right,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Register scratch3,
|
| - Register scratch4);
|
| -
|
| - // Compares two flat ASCII strings for equality and returns result
|
| - // in r0.
|
| - static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
| - Register left,
|
| - Register right,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Register scratch3);
|
| -
|
| - private:
|
| - virtual Major MajorKey() { return StringCompare; }
|
| - virtual int MinorKey() { return 0; }
|
| - virtual void Generate(MacroAssembler* masm);
|
| -
|
| - static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
|
| - Register left,
|
| - Register right,
|
| - Register length,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Label* chars_not_equal);
|
| -};
|
| -
|
| -
|
| -// This stub can convert a signed int32 to a heap number (double). It does
|
| -// not work for int32s that are in Smi range! No GC occurs during this stub
|
| -// so you don't have to set up the frame.
|
| -class WriteInt32ToHeapNumberStub : public PlatformCodeStub {
|
| - public:
|
| - WriteInt32ToHeapNumberStub(Register the_int,
|
| - Register the_heap_number,
|
| - Register scratch)
|
| - : the_int_(the_int),
|
| - the_heap_number_(the_heap_number),
|
| - scratch_(scratch) { }
|
| -
|
| - static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
|
| -
|
| - private:
|
| - Register the_int_;
|
| - Register the_heap_number_;
|
| - Register scratch_;
|
| -
|
| - // Minor key encoding in 16 bits.
|
| - class IntRegisterBits: public BitField<int, 0, 4> {};
|
| - class HeapNumberRegisterBits: public BitField<int, 4, 4> {};
|
| - class ScratchRegisterBits: public BitField<int, 8, 4> {};
|
| -
|
| - Major MajorKey() { return WriteInt32ToHeapNumber; }
|
| - int MinorKey() {
|
| - // Encode the parameters in a unique 16 bit value.
|
| - return IntRegisterBits::encode(the_int_.code())
|
| - | HeapNumberRegisterBits::encode(the_heap_number_.code())
|
| - | ScratchRegisterBits::encode(scratch_.code());
|
| - }
|
| -
|
| - void Generate(MacroAssembler* masm);
|
| -};
|
| -
|
| -
|
| class RecordWriteStub: public PlatformCodeStub {
|
| public:
|
| + // Stub to record the write of 'value' at 'address' in 'object'.
|
| + // Typically 'address' = 'object' + <some offset>.
|
| + // See MacroAssembler::RecordWriteField() for example.
|
| RecordWriteStub(Register object,
|
| Register value,
|
| Register address,
|
| @@ -200,126 +105,190 @@ class RecordWriteStub: public PlatformCodeStub {
|
|
|
| virtual bool SometimesSetsUpAFrame() { return false; }
|
|
|
| - static void PatchBranchIntoNop(MacroAssembler* masm, int pos) {
|
| - masm->instr_at_put(pos, (masm->instr_at(pos) & ~B27) | (B24 | B20));
|
| - ASSERT(Assembler::IsTstImmediate(masm->instr_at(pos)));
|
| - }
|
| -
|
| - static void PatchNopIntoBranch(MacroAssembler* masm, int pos) {
|
| - masm->instr_at_put(pos, (masm->instr_at(pos) & ~(B24 | B20)) | B27);
|
| - ASSERT(Assembler::IsBranch(masm->instr_at(pos)));
|
| - }
|
| -
|
| static Mode GetMode(Code* stub) {
|
| - Instr first_instruction = Assembler::instr_at(stub->instruction_start());
|
| - Instr second_instruction = Assembler::instr_at(stub->instruction_start() +
|
| - Assembler::kInstrSize);
|
| + // Find the mode depending on the first two instructions.
|
| + Instruction* instr1 =
|
| + reinterpret_cast<Instruction*>(stub->instruction_start());
|
| + Instruction* instr2 = instr1->following();
|
|
|
| - if (Assembler::IsBranch(first_instruction)) {
|
| + if (instr1->IsUncondBranchImm()) {
|
| + ASSERT(instr2->IsPCRelAddressing() && (instr2->Rd() == xzr.code()));
|
| return INCREMENTAL;
|
| }
|
|
|
| - ASSERT(Assembler::IsTstImmediate(first_instruction));
|
| + ASSERT(instr1->IsPCRelAddressing() && (instr1->Rd() == xzr.code()));
|
|
|
| - if (Assembler::IsBranch(second_instruction)) {
|
| + if (instr2->IsUncondBranchImm()) {
|
| return INCREMENTAL_COMPACTION;
|
| }
|
|
|
| - ASSERT(Assembler::IsTstImmediate(second_instruction));
|
| + ASSERT(instr2->IsPCRelAddressing());
|
|
|
| return STORE_BUFFER_ONLY;
|
| }
|
|
|
| + // We patch the two first instructions of the stub back and forth between an
|
| + // adr and branch when we start and stop incremental heap marking.
|
| + // The branch is
|
| + // b label
|
| + // The adr is
|
| + // adr xzr label
|
| + // so effectively a nop.
|
| static void Patch(Code* stub, Mode mode) {
|
| - MacroAssembler masm(NULL,
|
| - stub->instruction_start(),
|
| - stub->instruction_size());
|
| + // We are going to patch the two first instructions of the stub.
|
| + PatchingAssembler patcher(
|
| + reinterpret_cast<Instruction*>(stub->instruction_start()), 2);
|
| + Instruction* instr1 = patcher.InstructionAt(0);
|
| + Instruction* instr2 = patcher.InstructionAt(kInstructionSize);
|
| + // Instructions must be either 'adr' or 'b'.
|
| + ASSERT(instr1->IsPCRelAddressing() || instr1->IsUncondBranchImm());
|
| + ASSERT(instr2->IsPCRelAddressing() || instr2->IsUncondBranchImm());
|
| + // Retrieve the offsets to the labels.
|
| + int32_t offset_to_incremental_noncompacting = instr1->ImmPCOffset();
|
| + int32_t offset_to_incremental_compacting = instr2->ImmPCOffset();
|
| +
|
| switch (mode) {
|
| case STORE_BUFFER_ONLY:
|
| ASSERT(GetMode(stub) == INCREMENTAL ||
|
| GetMode(stub) == INCREMENTAL_COMPACTION);
|
| - PatchBranchIntoNop(&masm, 0);
|
| - PatchBranchIntoNop(&masm, Assembler::kInstrSize);
|
| + patcher.adr(xzr, offset_to_incremental_noncompacting);
|
| + patcher.adr(xzr, offset_to_incremental_compacting);
|
| break;
|
| case INCREMENTAL:
|
| ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
|
| - PatchNopIntoBranch(&masm, 0);
|
| + patcher.b(offset_to_incremental_noncompacting >> kInstructionSizeLog2);
|
| + patcher.adr(xzr, offset_to_incremental_compacting);
|
| break;
|
| case INCREMENTAL_COMPACTION:
|
| ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
|
| - PatchNopIntoBranch(&masm, Assembler::kInstrSize);
|
| + patcher.adr(xzr, offset_to_incremental_noncompacting);
|
| + patcher.b(offset_to_incremental_compacting >> kInstructionSizeLog2);
|
| break;
|
| }
|
| ASSERT(GetMode(stub) == mode);
|
| - CPU::FlushICache(stub->instruction_start(), 2 * Assembler::kInstrSize);
|
| }
|
|
|
| private:
|
| - // This is a helper class for freeing up 3 scratch registers. The input is
|
| - // two registers that must be preserved and one scratch register provided by
|
| - // the caller.
|
| + // This is a helper class to manage the registers associated with the stub.
|
| + // The 'object' and 'address' registers must be preserved.
|
| class RegisterAllocation {
|
| public:
|
| RegisterAllocation(Register object,
|
| Register address,
|
| - Register scratch0)
|
| + Register scratch)
|
| : object_(object),
|
| address_(address),
|
| - scratch0_(scratch0) {
|
| - ASSERT(!AreAliased(scratch0, object, address, no_reg));
|
| - scratch1_ = GetRegisterThatIsNotOneOf(object_, address_, scratch0_);
|
| + scratch0_(scratch),
|
| + saved_regs_(kCallerSaved) {
|
| + ASSERT(!AreAliased(scratch, object, address));
|
| +
|
| + // We would like to require more scratch registers for this stub,
|
| + // but the number of registers comes down to the ones used in
|
| + // FullCodeGen::SetVar(), which is architecture independent.
|
| + // We allocate 2 extra scratch registers that we'll save on the stack.
|
| + CPURegList pool_available = GetValidRegistersForAllocation();
|
| + CPURegList used_regs(object, address, scratch);
|
| + pool_available.Remove(used_regs);
|
| + scratch1_ = Register(pool_available.PopLowestIndex());
|
| + scratch2_ = Register(pool_available.PopLowestIndex());
|
| +
|
| + // SaveCallerRegisters method needs to save caller saved register, however
|
| + // we don't bother saving ip0 and ip1 because they are used as scratch
|
| + // registers by the MacroAssembler.
|
| + saved_regs_.Remove(ip0);
|
| + saved_regs_.Remove(ip1);
|
| +
|
| + // The scratch registers will be restored by other means so we don't need
|
| + // to save them with the other caller saved registers.
|
| + saved_regs_.Remove(scratch0_);
|
| + saved_regs_.Remove(scratch1_);
|
| + saved_regs_.Remove(scratch2_);
|
| }
|
|
|
| void Save(MacroAssembler* masm) {
|
| - ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_));
|
| // We don't have to save scratch0_ because it was given to us as
|
| // a scratch register.
|
| - masm->push(scratch1_);
|
| + masm->Push(scratch1_, scratch2_);
|
| }
|
|
|
| void Restore(MacroAssembler* masm) {
|
| - masm->pop(scratch1_);
|
| + masm->Pop(scratch2_, scratch1_);
|
| }
|
|
|
| // If we have to call into C then we need to save and restore all caller-
|
| - // saved registers that were not already preserved. The scratch registers
|
| - // will be restored by other means so we don't bother pushing them here.
|
| + // saved registers that were not already preserved.
|
| void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
|
| - masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
|
| + // TODO(all): This can be very expensive, and it is likely that not every
|
| + // register will need to be preserved. Can we improve this?
|
| + masm->PushCPURegList(saved_regs_);
|
| if (mode == kSaveFPRegs) {
|
| - masm->SaveFPRegs(sp, scratch0_);
|
| + masm->PushCPURegList(kCallerSavedFP);
|
| }
|
| }
|
|
|
| - inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
|
| - SaveFPRegsMode mode) {
|
| + void RestoreCallerSaveRegisters(MacroAssembler*masm, SaveFPRegsMode mode) {
|
| + // TODO(all): This can be very expensive, and it is likely that not every
|
| + // register will need to be preserved. Can we improve this?
|
| if (mode == kSaveFPRegs) {
|
| - masm->RestoreFPRegs(sp, scratch0_);
|
| + masm->PopCPURegList(kCallerSavedFP);
|
| }
|
| - masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
|
| + masm->PopCPURegList(saved_regs_);
|
| }
|
|
|
| - inline Register object() { return object_; }
|
| - inline Register address() { return address_; }
|
| - inline Register scratch0() { return scratch0_; }
|
| - inline Register scratch1() { return scratch1_; }
|
| + Register object() { return object_; }
|
| + Register address() { return address_; }
|
| + Register scratch0() { return scratch0_; }
|
| + Register scratch1() { return scratch1_; }
|
| + Register scratch2() { return scratch2_; }
|
|
|
| private:
|
| Register object_;
|
| Register address_;
|
| Register scratch0_;
|
| Register scratch1_;
|
| + Register scratch2_;
|
| + CPURegList saved_regs_;
|
| +
|
| + // TODO(all): We should consider moving this somewhere else.
|
| + static CPURegList GetValidRegistersForAllocation() {
|
| + // The list of valid registers for allocation is defined as all the
|
| + // registers without those with a special meaning.
|
| + //
|
| + // The default list excludes registers x26 to x31 because they are
|
| + // reserved for the following purpose:
|
| + // - x26 root register
|
| + // - x27 context pointer register
|
| + // - x28 jssp
|
| + // - x29 frame pointer
|
| + // - x30 link register(lr)
|
| + // - x31 xzr/stack pointer
|
| + CPURegList list(CPURegister::kRegister, kXRegSize, 0, 25);
|
| +
|
| + // We also remove MacroAssembler's scratch registers.
|
| + list.Remove(ip0);
|
| + list.Remove(ip1);
|
| + list.Remove(x8);
|
| + list.Remove(x9);
|
| +
|
| + return list;
|
| + }
|
|
|
| friend class RecordWriteStub;
|
| };
|
|
|
| + // A list of stub variants which are pregenerated.
|
| + // The variants are stored in the same format as the minor key, so
|
| + // MinorKeyFor() can be used to populate and check this list.
|
| + static const int kAheadOfTime[];
|
| +
|
| + void Generate(MacroAssembler* masm);
|
| + void GenerateIncremental(MacroAssembler* masm, Mode mode);
|
| +
|
| enum OnNoNeedToInformIncrementalMarker {
|
| kReturnOnNoNeedToInformIncrementalMarker,
|
| kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
|
| };
|
|
|
| - void Generate(MacroAssembler* masm);
|
| - void GenerateIncremental(MacroAssembler* masm, Mode mode);
|
| void CheckNeedsToInformIncrementalMarker(
|
| MacroAssembler* masm,
|
| OnNoNeedToInformIncrementalMarker on_no_need,
|
| @@ -329,22 +298,34 @@ class RecordWriteStub: public PlatformCodeStub {
|
| Major MajorKey() { return RecordWrite; }
|
|
|
| int MinorKey() {
|
| - return ObjectBits::encode(object_.code()) |
|
| - ValueBits::encode(value_.code()) |
|
| - AddressBits::encode(address_.code()) |
|
| - RememberedSetActionBits::encode(remembered_set_action_) |
|
| - SaveFPRegsModeBits::encode(save_fp_regs_mode_);
|
| + return MinorKeyFor(object_, value_, address_, remembered_set_action_,
|
| + save_fp_regs_mode_);
|
| + }
|
| +
|
| + static int MinorKeyFor(Register object,
|
| + Register value,
|
| + Register address,
|
| + RememberedSetAction action,
|
| + SaveFPRegsMode fp_mode) {
|
| + ASSERT(object.Is64Bits());
|
| + ASSERT(value.Is64Bits());
|
| + ASSERT(address.Is64Bits());
|
| + return ObjectBits::encode(object.code()) |
|
| + ValueBits::encode(value.code()) |
|
| + AddressBits::encode(address.code()) |
|
| + RememberedSetActionBits::encode(action) |
|
| + SaveFPRegsModeBits::encode(fp_mode);
|
| }
|
|
|
| void Activate(Code* code) {
|
| code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
|
| }
|
|
|
| - class ObjectBits: public BitField<int, 0, 4> {};
|
| - class ValueBits: public BitField<int, 4, 4> {};
|
| - class AddressBits: public BitField<int, 8, 4> {};
|
| - class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {};
|
| - class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {};
|
| + class ObjectBits: public BitField<int, 0, 5> {};
|
| + class ValueBits: public BitField<int, 5, 5> {};
|
| + class AddressBits: public BitField<int, 10, 5> {};
|
| + class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {};
|
| + class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {};
|
|
|
| Register object_;
|
| Register value_;
|
| @@ -356,11 +337,8 @@ class RecordWriteStub: public PlatformCodeStub {
|
| };
|
|
|
|
|
| -// Trampoline stub to call into native code. To call safely into native code
|
| -// in the presence of compacting GC (which can move code objects) we need to
|
| -// keep the code which called into native pinned in the memory. Currently the
|
| -// simplest approach is to generate such stub early enough so it can never be
|
| -// moved by GC
|
| +// Helper to call C++ functions from generated code. The caller must prepare
|
| +// the exit frame before doing the call with GenerateCall.
|
| class DirectCEntryStub: public PlatformCodeStub {
|
| public:
|
| DirectCEntryStub() {}
|
| @@ -396,8 +374,8 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
| Label* done,
|
| Register elements,
|
| Register name,
|
| - Register r0,
|
| - Register r1);
|
| + Register scratch1,
|
| + Register scratch2);
|
|
|
| virtual bool SometimesSetsUpAFrame() { return false; }
|
|
|
| @@ -425,6 +403,55 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
| };
|
|
|
|
|
| +class SubStringStub: public PlatformCodeStub {
|
| + public:
|
| + SubStringStub() {}
|
| +
|
| + private:
|
| + Major MajorKey() { return SubString; }
|
| + int MinorKey() { return 0; }
|
| +
|
| + void Generate(MacroAssembler* masm);
|
| +};
|
| +
|
| +
|
| +class StringCompareStub: public PlatformCodeStub {
|
| + public:
|
| + StringCompareStub() { }
|
| +
|
| + // Compares two flat ASCII strings and returns result in x0.
|
| + static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
| + Register left,
|
| + Register right,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register scratch3,
|
| + Register scratch4);
|
| +
|
| + // Compare two flat ASCII strings for equality and returns result
|
| + // in x0.
|
| + static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
|
| + Register left,
|
| + Register right,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register scratch3);
|
| +
|
| + private:
|
| + virtual Major MajorKey() { return StringCompare; }
|
| + virtual int MinorKey() { return 0; }
|
| + virtual void Generate(MacroAssembler* masm);
|
| +
|
| + static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
|
| + Register left,
|
| + Register right,
|
| + Register length,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Label* chars_not_equal);
|
| +};
|
| +
|
| +
|
| struct PlatformCallInterfaceDescriptor {
|
| explicit PlatformCallInterfaceDescriptor(
|
| TargetAddressStorageMode storage_mode)
|
| @@ -439,4 +466,4 @@ struct PlatformCallInterfaceDescriptor {
|
|
|
| } } // namespace v8::internal
|
|
|
| -#endif // V8_ARM_CODE_STUBS_ARM_H_
|
| +#endif // V8_A64_CODE_STUBS_A64_H_
|
|
|