// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_X64_MACRO_ASSEMBLER_X64_H_ #define V8_X64_MACRO_ASSEMBLER_X64_H_ #include "src/assembler.h" #include "src/bailout-reason.h" #include "src/base/flags.h" #include "src/frames.h" #include "src/globals.h" #include "src/x64/frames-x64.h" namespace v8 { namespace internal { // Give alias names to registers for calling conventions. const Register kReturnRegister0 = {Register::kCode_rax}; const Register kReturnRegister1 = {Register::kCode_rdx}; const Register kJSFunctionRegister = {Register::kCode_rdi}; const Register kContextRegister = {Register::kCode_rsi}; const Register kInterpreterAccumulatorRegister = {Register::kCode_rax}; const Register kInterpreterRegisterFileRegister = {Register::kCode_r11}; const Register kInterpreterBytecodeOffsetRegister = {Register::kCode_r12}; const Register kInterpreterBytecodeArrayRegister = {Register::kCode_r14}; const Register kInterpreterDispatchTableRegister = {Register::kCode_r15}; const Register kJavaScriptCallArgCountRegister = {Register::kCode_rax}; const Register kJavaScriptCallNewTargetRegister = {Register::kCode_rdx}; const Register kRuntimeCallFunctionRegister = {Register::kCode_rbx}; const Register kRuntimeCallArgCountRegister = {Register::kCode_rax}; // Default scratch register used by MacroAssembler (and other code that needs // a spare register). The register isn't callee save, and not used by the // function calling convention. const Register kScratchRegister = { 10 }; // r10. const Register kRootRegister = { 13 }; // r13 (callee save). // Actual value of root register is offset from the root array's start // to take advantage of negitive 8-bit displacement values. const int kRootRegisterBias = 128; // Convenience for platform-independent signatures. typedef Operand MemOperand; enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; enum PointersToHereCheck { kPointersToHereMaybeInteresting, kPointersToHereAreAlwaysInteresting }; enum class SmiOperationConstraint { kPreserveSourceRegister = 1 << 0, kBailoutOnNoOverflow = 1 << 1, kBailoutOnOverflow = 1 << 2 }; typedef base::Flags SmiOperationConstraints; DEFINE_OPERATORS_FOR_FLAGS(SmiOperationConstraints) #ifdef DEBUG bool AreAliased(Register reg1, Register reg2, Register reg3 = no_reg, Register reg4 = no_reg, Register reg5 = no_reg, Register reg6 = no_reg, Register reg7 = no_reg, Register reg8 = no_reg); #endif // Forward declaration. class JumpTarget; struct SmiIndex { SmiIndex(Register index_register, ScaleFactor scale) : reg(index_register), scale(scale) {} Register reg; ScaleFactor scale; }; // MacroAssembler implements a collection of frequently used macros. class MacroAssembler: public Assembler { public: MacroAssembler(Isolate* isolate, void* buffer, int size, CodeObjectRequired create_code_object); // Prevent the use of the RootArray during the lifetime of this // scope object. class NoRootArrayScope BASE_EMBEDDED { public: explicit NoRootArrayScope(MacroAssembler* assembler) : variable_(&assembler->root_array_available_), old_value_(assembler->root_array_available_) { assembler->root_array_available_ = false; } ~NoRootArrayScope() { *variable_ = old_value_; } private: bool* variable_; bool old_value_; }; // Operand pointing to an external reference. // May emit code to set up the scratch register. The operand is // only guaranteed to be correct as long as the scratch register // isn't changed. // If the operand is used more than once, use a scratch register // that is guaranteed not to be clobbered. Operand ExternalOperand(ExternalReference reference, Register scratch = kScratchRegister); // Loads and stores the value of an external reference. // Special case code for load and store to take advantage of // load_rax/store_rax if possible/necessary. // For other operations, just use: // Operand operand = ExternalOperand(extref); // operation(operand, ..); void Load(Register destination, ExternalReference source); void Store(ExternalReference destination, Register source); // Loads the address of the external reference into the destination // register. void LoadAddress(Register destination, ExternalReference source); // Returns the size of the code generated by LoadAddress. // Used by CallSize(ExternalReference) to find the size of a call. int LoadAddressSize(ExternalReference source); // Pushes the address of the external reference onto the stack. void PushAddress(ExternalReference source); // Operations on roots in the root-array. void LoadRoot(Register destination, Heap::RootListIndex index); void LoadRoot(const Operand& destination, Heap::RootListIndex index) { LoadRoot(kScratchRegister, index); movp(destination, kScratchRegister); } void StoreRoot(Register source, Heap::RootListIndex index); // Load a root value where the index (or part of it) is variable. // The variable_offset register is added to the fixed_offset value // to get the index into the root-array. void LoadRootIndexed(Register destination, Register variable_offset, int fixed_offset); void CompareRoot(Register with, Heap::RootListIndex index); void CompareRoot(const Operand& with, Heap::RootListIndex index); void PushRoot(Heap::RootListIndex index); // Compare the object in a register to a value and jump if they are equal. void JumpIfRoot(Register with, Heap::RootListIndex index, Label* if_equal, Label::Distance if_equal_distance = Label::kFar) { CompareRoot(with, index); j(equal, if_equal, if_equal_distance); } void JumpIfRoot(const Operand& with, Heap::RootListIndex index, Label* if_equal, Label::Distance if_equal_distance = Label::kFar) { CompareRoot(with, index); j(equal, if_equal, if_equal_distance); } // Compare the object in a register to a value and jump if they are not equal. void JumpIfNotRoot(Register with, Heap::RootListIndex index, Label* if_not_equal, Label::Distance if_not_equal_distance = Label::kFar) { CompareRoot(with, index); j(not_equal, if_not_equal, if_not_equal_distance); } void JumpIfNotRoot(const Operand& with, Heap::RootListIndex index, Label* if_not_equal, Label::Distance if_not_equal_distance = Label::kFar) { CompareRoot(with, index); j(not_equal, if_not_equal, if_not_equal_distance); } // These functions do not arrange the registers in any particular order so // they are not useful for calls that can cause a GC. The caller can // exclude up to 3 registers that do not need to be saved and restored. void PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, Register exclusion2 = no_reg, Register exclusion3 = no_reg); void PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, Register exclusion2 = no_reg, Register exclusion3 = no_reg); // --------------------------------------------------------------------------- // GC Support enum RememberedSetFinalAction { kReturnAtEnd, kFallThroughAtEnd }; // Record in the remembered set the fact that we have a pointer to new space // at the address pointed to by the addr register. Only works if addr is not // in new space. void RememberedSetHelper(Register object, // Used for debug code. Register addr, Register scratch, SaveFPRegsMode save_fp, RememberedSetFinalAction and_then); void CheckPageFlag(Register object, Register scratch, int mask, Condition cc, Label* condition_met, Label::Distance condition_met_distance = Label::kFar); // Check if object is in new space. Jumps if the object is not in new space. // The register scratch can be object itself, but scratch will be clobbered. void JumpIfNotInNewSpace(Register object, Register scratch, Label* branch, Label::Distance distance = Label::kFar) { InNewSpace(object, scratch, not_equal, branch, distance); } // Check if object is in new space. Jumps if the object is in new space. // The register scratch can be object itself, but it will be clobbered. void JumpIfInNewSpace(Register object, Register scratch, Label* branch, Label::Distance distance = Label::kFar) { InNewSpace(object, scratch, equal, branch, distance); } // Check if an object has the black incremental marking color. Also uses rcx! void JumpIfBlack(Register object, Register bitmap_scratch, Register mask_scratch, Label* on_black, Label::Distance on_black_distance); // Checks the color of an object. If the object is white we jump to the // incremental marker. void JumpIfWhite(Register value, Register scratch1, Register scratch2, Label* value_is_white, Label::Distance distance); // Notify the garbage collector that we wrote a pointer into an object. // |object| is the object being stored into, |value| is the object being // stored. value and scratch registers are clobbered by the operation. // The offset is the offset from the start of the object, not the offset from // the tagged HeapObject pointer. For use with FieldOperand(reg, off). void RecordWriteField( Register object, int offset, Register value, Register scratch, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, SmiCheck smi_check = INLINE_SMI_CHECK, PointersToHereCheck pointers_to_here_check_for_value = kPointersToHereMaybeInteresting); // As above, but the offset has the tag presubtracted. For use with // Operand(reg, off). void RecordWriteContextSlot( Register context, int offset, Register value, Register scratch, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, SmiCheck smi_check = INLINE_SMI_CHECK, PointersToHereCheck pointers_to_here_check_for_value = kPointersToHereMaybeInteresting) { RecordWriteField(context, offset + kHeapObjectTag, value, scratch, save_fp, remembered_set_action, smi_check, pointers_to_here_check_for_value); } // Notify the garbage collector that we wrote a pointer into a fixed array. // |array| is the array being stored into, |value| is the // object being stored. |index| is the array index represented as a non-smi. // All registers are clobbered by the operation RecordWriteArray // filters out smis so it does not update the write barrier if the // value is a smi. void RecordWriteArray( Register array, Register value, Register index, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, SmiCheck smi_check = INLINE_SMI_CHECK, PointersToHereCheck pointers_to_here_check_for_value = kPointersToHereMaybeInteresting); void RecordWriteForMap( Register object, Register map, Register dst, SaveFPRegsMode save_fp); // For page containing |object| mark region covering |address| // dirty. |object| is the object being stored into, |value| is the // object being stored. The address and value registers are clobbered by the // operation. RecordWrite filters out smis so it does not update // the write barrier if the value is a smi. void RecordWrite( Register object, Register address, Register value, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, SmiCheck smi_check = INLINE_SMI_CHECK, PointersToHereCheck pointers_to_here_check_for_value = kPointersToHereMaybeInteresting); // --------------------------------------------------------------------------- // Debugger Support void DebugBreak(); // Generates function and stub prologue code. void StubPrologue(); void Prologue(bool code_pre_aging); // Enter specific kind of exit frame; either in normal or // debug mode. Expects the number of arguments in register rax and // sets up the number of arguments in register rdi and the pointer // to the first argument in register rsi. // // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack // accessible via StackSpaceOperand. void EnterExitFrame(int arg_stack_space = 0, bool save_doubles = false); // Enter specific kind of exit frame. Allocates arg_stack_space * kPointerSize // memory (not GCed) on the stack accessible via StackSpaceOperand. void EnterApiExitFrame(int arg_stack_space); // Leave the current exit frame. Expects/provides the return value in // register rax:rdx (untouched) and the pointer to the first // argument in register rsi (if pop_arguments == true). void LeaveExitFrame(bool save_doubles = false, bool pop_arguments = true); // Leave the current exit frame. Expects/provides the return value in // register rax (untouched). void LeaveApiExitFrame(bool restore_context); // Push and pop the registers that can hold pointers. void PushSafepointRegisters() { Pushad(); } void PopSafepointRegisters() { Popad(); } // Store the value in register src in the safepoint register stack // slot for register dst. void StoreToSafepointRegisterSlot(Register dst, const Immediate& imm); void StoreToSafepointRegisterSlot(Register dst, Register src); void LoadFromSafepointRegisterSlot(Register dst, Register src); void InitializeRootRegister() { ExternalReference roots_array_start = ExternalReference::roots_array_start(isolate()); Move(kRootRegister, roots_array_start); addp(kRootRegister, Immediate(kRootRegisterBias)); } // --------------------------------------------------------------------------- // JavaScript invokes // Invoke the JavaScript function code by either calling or jumping. void InvokeFunctionCode(Register function, Register new_target, const ParameterCount& expected, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper); void FloodFunctionIfStepping(Register fun, Register new_target, const ParameterCount& expected, const ParameterCount& actual); // Invoke the JavaScript function in the given register. Changes the // current context to the context in the function before invoking. void InvokeFunction(Register function, Register new_target, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper); void InvokeFunction(Register function, Register new_target, const ParameterCount& expected, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper); void InvokeFunction(Handle function, const ParameterCount& expected, const ParameterCount& actual, InvokeFlag flag, const CallWrapper& call_wrapper); // Invoke specified builtin JavaScript function. void InvokeBuiltin(int native_context_index, InvokeFlag flag, const CallWrapper& call_wrapper = NullCallWrapper()); // --------------------------------------------------------------------------- // Smi tagging, untagging and operations on tagged smis. // Support for constant splitting. bool IsUnsafeInt(const int32_t x); void SafeMove(Register dst, Smi* src); void SafePush(Smi* src); // Conversions between tagged smi values and non-tagged integer values. // Tag an integer value. The result must be known to be a valid smi value. // Only uses the low 32 bits of the src register. Sets the N and Z flags // based on the value of the resulting smi. void Integer32ToSmi(Register dst, Register src); // Stores an integer32 value into a memory field that already holds a smi. void Integer32ToSmiField(const Operand& dst, Register src); // Adds constant to src and tags the result as a smi. // Result must be a valid smi. void Integer64PlusConstantToSmi(Register dst, Register src, int constant); // Convert smi to 32-bit integer. I.e., not sign extended into // high 32 bits of destination. void SmiToInteger32(Register dst, Register src); void SmiToInteger32(Register dst, const Operand& src); // Convert smi to 64-bit integer (sign extended if necessary). void SmiToInteger64(Register dst, Register src); void SmiToInteger64(Register dst, const Operand& src); // Multiply a positive smi's integer value by a power of two. // Provides result as 64-bit integer value. void PositiveSmiTimesPowerOfTwoToInteger64(Register dst, Register src, int power); // Divide a positive smi's integer value by a power of two. // Provides result as 32-bit integer value. void PositiveSmiDivPowerOfTwoToInteger32(Register dst, Register src, int power); // Perform the logical or of two smi values and return a smi value. // If either argument is not a smi, jump to on_not_smis and retain // the original values of source registers. The destination register // may be changed if it's not one of the source registers. void SmiOrIfSmis(Register dst, Register src1, Register src2, Label* on_not_smis, Label::Distance near_jump = Label::kFar); // Simple comparison of smis. Both sides must be known smis to use these, // otherwise use Cmp. void SmiCompare(Register smi1, Register smi2); void SmiCompare(Register dst, Smi* src); void SmiCompare(Register dst, const Operand& src); void SmiCompare(const Operand& dst, Register src); void SmiCompare(const Operand& dst, Smi* src); // Compare the int32 in src register to the value of the smi stored at dst. void SmiCompareInteger32(const Operand& dst, Register src); // Sets sign and zero flags depending on value of smi in register. void SmiTest(Register src); // Functions performing a check on a known or potential smi. Returns // a condition that is satisfied if the check is successful. // Is the value a tagged smi. Condition CheckSmi(Register src); Condition CheckSmi(const Operand& src); // Is the value a non-negative tagged smi. Condition CheckNonNegativeSmi(Register src); // Are both values tagged smis. Condition CheckBothSmi(Register first, Register second); // Are both values non-negative tagged smis. Condition CheckBothNonNegativeSmi(Register first, Register second); // Are either value a tagged smi. Condition CheckEitherSmi(Register first, Register second, Register scratch = kScratchRegister); // Checks whether an 32-bit integer value is a valid for conversion // to a smi. Condition CheckInteger32ValidSmiValue(Register src); // Checks whether an 32-bit unsigned integer value is a valid for // conversion to a smi. Condition CheckUInteger32ValidSmiValue(Register src); // Check whether src is a Smi, and set dst to zero if it is a smi, // and to one if it isn't. void CheckSmiToIndicator(Register dst, Register src); void CheckSmiToIndicator(Register dst, const Operand& src); // Test-and-jump functions. Typically combines a check function // above with a conditional jump. // Jump if the value can be represented by a smi. void JumpIfValidSmiValue(Register src, Label* on_valid, Label::Distance near_jump = Label::kFar); // Jump if the value cannot be represented by a smi. void JumpIfNotValidSmiValue(Register src, Label* on_invalid, Label::Distance near_jump = Label::kFar); // Jump if the unsigned integer value can be represented by a smi. void JumpIfUIntValidSmiValue(Register src, Label* on_valid, Label::Distance near_jump = Label::kFar); // Jump if the unsigned integer value cannot be represented by a smi. void JumpIfUIntNotValidSmiValue(Register src, Label* on_invalid, Label::Distance near_jump = Label::kFar); // Jump to label if the value is a tagged smi. void JumpIfSmi(Register src, Label* on_smi, Label::Distance near_jump = Label::kFar); // Jump to label if the value is not a tagged smi. void JumpIfNotSmi(Register src, Label* on_not_smi, Label::Distance near_jump = Label::kFar); // Jump to label if the value is not a non-negative tagged smi. void JumpUnlessNonNegativeSmi(Register src, Label* on_not_smi, Label::Distance near_jump = Label::kFar); // Jump to label if the value, which must be a tagged smi, has value equal // to the constant. void JumpIfSmiEqualsConstant(Register src, Smi* constant, Label* on_equals, Label::Distance near_jump = Label::kFar); // Jump if either or both register are not smi values. void JumpIfNotBothSmi(Register src1, Register src2, Label* on_not_both_smi, Label::Distance near_jump = Label::kFar); // Jump if either or both register are not non-negative smi values. void JumpUnlessBothNonNegativeSmi(Register src1, Register src2, Label* on_not_both_smi, Label::Distance near_jump = Label::kFar); // Operations on tagged smi values. // Smis represent a subset of integers. The subset is always equivalent to // a two's complement interpretation of a fixed number of bits. // Add an integer constant to a tagged smi, giving a tagged smi as result. // No overflow testing on the result is done. void SmiAddConstant(Register dst, Register src, Smi* constant); // Add an integer constant to a tagged smi, giving a tagged smi as result. // No overflow testing on the result is done. void SmiAddConstant(const Operand& dst, Smi* constant); // Add an integer constant to a tagged smi, giving a tagged smi as result, // or jumping to a label if the result cannot be represented by a smi. void SmiAddConstant(Register dst, Register src, Smi* constant, SmiOperationConstraints constraints, Label* bailout_label, Label::Distance near_jump = Label::kFar); // Subtract an integer constant from a tagged smi, giving a tagged smi as // result. No testing on the result is done. Sets the N and Z flags // based on the value of the resulting integer. void SmiSubConstant(Register dst, Register src, Smi* constant); // Subtract an integer constant from a tagged smi, giving a tagged smi as // result, or jumping to a label if the result cannot be represented by a smi. void SmiSubConstant(Register dst, Register src, Smi* constant, SmiOperationConstraints constraints, Label* bailout_label, Label::Distance near_jump = Label::kFar); // Negating a smi can give a negative zero or too large positive value. // NOTICE: This operation jumps on success, not failure! void SmiNeg(Register dst, Register src, Label* on_smi_result, Label::Distance near_jump = Label::kFar); // Adds smi values and return the result as a smi. // If dst is src1, then src1 will be destroyed if the operation is // successful, otherwise kept intact. void SmiAdd(Register dst, Register src1, Register src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); void SmiAdd(Register dst, Register src1, const Operand& src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); void SmiAdd(Register dst, Register src1, Register src2); // Subtracts smi values and return the result as a smi. // If dst is src1, then src1 will be destroyed if the operation is // successful, otherwise kept intact. void SmiSub(Register dst, Register src1, Register src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); void SmiSub(Register dst, Register src1, const Operand& src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); void SmiSub(Register dst, Register src1, Register src2); void SmiSub(Register dst, Register src1, const Operand& src2); // Multiplies smi values and return the result as a smi, // if possible. // If dst is src1, then src1 will be destroyed, even if // the operation is unsuccessful. void SmiMul(Register dst, Register src1, Register src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); // Divides one smi by another and returns the quotient. // Clobbers rax and rdx registers. void SmiDiv(Register dst, Register src1, Register src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); // Divides one smi by another and returns the remainder. // Clobbers rax and rdx registers. void SmiMod(Register dst, Register src1, Register src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); // Bitwise operations. void SmiNot(Register dst, Register src); void SmiAnd(Register dst, Register src1, Register src2); void SmiOr(Register dst, Register src1, Register src2); void SmiXor(Register dst, Register src1, Register src2); void SmiAndConstant(Register dst, Register src1, Smi* constant); void SmiOrConstant(Register dst, Register src1, Smi* constant); void SmiXorConstant(Register dst, Register src1, Smi* constant); void SmiShiftLeftConstant(Register dst, Register src, int shift_value, Label* on_not_smi_result = NULL, Label::Distance near_jump = Label::kFar); void SmiShiftLogicalRightConstant(Register dst, Register src, int shift_value, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); void SmiShiftArithmeticRightConstant(Register dst, Register src, int shift_value); // Shifts a smi value to the left, and returns the result if that is a smi. // Uses and clobbers rcx, so dst may not be rcx. void SmiShiftLeft(Register dst, Register src1, Register src2, Label* on_not_smi_result = NULL, Label::Distance near_jump = Label::kFar); // Shifts a smi value to the right, shifting in zero bits at the top, and // returns the unsigned intepretation of the result if that is a smi. // Uses and clobbers rcx, so dst may not be rcx. void SmiShiftLogicalRight(Register dst, Register src1, Register src2, Label* on_not_smi_result, Label::Distance near_jump = Label::kFar); // Shifts a smi value to the right, sign extending the top, and // returns the signed intepretation of the result. That will always // be a valid smi value, since it's numerically smaller than the // original. // Uses and clobbers rcx, so dst may not be rcx. void SmiShiftArithmeticRight(Register dst, Register src1, Register src2); // Specialized operations // Select the non-smi register of two registers where exactly one is a // smi. If neither are smis, jump to the failure label. void SelectNonSmi(Register dst, Register src1, Register src2, Label* on_not_smis, Label::Distance near_jump = Label::kFar); // Converts, if necessary, a smi to a combination of number and // multiplier to be used as a scaled index. // The src register contains a *positive* smi value. The shift is the // power of two to multiply the index value by (e.g. // to index by smi-value * kPointerSize, pass the smi and kPointerSizeLog2). // The returned index register may be either src or dst, depending // on what is most efficient. If src and dst are different registers, // src is always unchanged. SmiIndex SmiToIndex(Register dst, Register src, int shift); // Converts a positive smi to a negative index. SmiIndex SmiToNegativeIndex(Register dst, Register src, int shift); // Add the value of a smi in memory to an int32 register. // Sets flags as a normal add. void AddSmiField(Register dst, const Operand& src); // Basic Smi operations. void Move(Register dst, Smi* source) { LoadSmiConstant(dst, source); } void Move(const Operand& dst, Smi* source) { Register constant = GetSmiConstant(source); movp(dst, constant); } void Push(Smi* smi); // Save away a raw integer with pointer size on the stack as two integers // masquerading as smis so that the garbage collector skips visiting them. void PushRegisterAsTwoSmis(Register src, Register scratch = kScratchRegister); // Reconstruct a raw integer with pointer size from two integers masquerading // as smis on the top of stack. void PopRegisterAsTwoSmis(Register dst, Register scratch = kScratchRegister); void Test(const Operand& dst, Smi* source); // --------------------------------------------------------------------------- // String macros. // If object is a string, its map is loaded into object_map. void JumpIfNotString(Register object, Register object_map, Label* not_string, Label::Distance near_jump = Label::kFar); void JumpIfNotBothSequentialOneByteStrings( Register first_object, Register second_object, Register scratch1, Register scratch2, Label* on_not_both_flat_one_byte, Label::Distance near_jump = Label::kFar); // Check whether the instance type represents a flat one-byte string. Jump // to the label if not. If the instance type can be scratched specify same // register for both instance type and scratch. void JumpIfInstanceTypeIsNotSequentialOneByte( Register instance_type, Register scratch, Label* on_not_flat_one_byte_string, Label::Distance near_jump = Label::kFar); void JumpIfBothInstanceTypesAreNotSequentialOneByte( Register first_object_instance_type, Register second_object_instance_type, Register scratch1, Register scratch2, Label* on_fail, Label::Distance near_jump = Label::kFar); void EmitSeqStringSetCharCheck(Register string, Register index, Register value, uint32_t encoding_mask); // Checks if the given register or operand is a unique name void JumpIfNotUniqueNameInstanceType(Register reg, Label* not_unique_name, Label::Distance distance = Label::kFar); void JumpIfNotUniqueNameInstanceType(Operand operand, Label* not_unique_name, Label::Distance distance = Label::kFar); // --------------------------------------------------------------------------- // Macro instructions. // Load/store with specific representation. void Load(Register dst, const Operand& src, Representation r); void Store(const Operand& dst, Register src, Representation r); // Load a register with a long value as efficiently as possible. void Set(Register dst, int64_t x); void Set(const Operand& dst, intptr_t x); void Cvtss2sd(XMMRegister dst, XMMRegister src); void Cvtss2sd(XMMRegister dst, const Operand& src); void Cvtsd2ss(XMMRegister dst, XMMRegister src); void Cvtsd2ss(XMMRegister dst, const Operand& src); // cvtsi2sd instruction only writes to the low 64-bit of dst register, which // hinders register renaming and makes dependence chains longer. So we use // xorpd to clear the dst register before cvtsi2sd to solve this issue. void Cvtlsi2sd(XMMRegister dst, Register src); void Cvtlsi2sd(XMMRegister dst, const Operand& src); void Cvtqsi2ss(XMMRegister dst, Register src); void Cvtqsi2ss(XMMRegister dst, const Operand& src); void Cvtqsi2sd(XMMRegister dst, Register src); void Cvtqsi2sd(XMMRegister dst, const Operand& src); void Cvtqui2ss(XMMRegister dst, Register src, Register tmp); void Cvtqui2sd(XMMRegister dst, Register src, Register tmp); void Cvtsd2si(Register dst, XMMRegister src); void Cvttsd2si(Register dst, XMMRegister src); void Cvttsd2si(Register dst, const Operand& src); void Cvttss2siq(Register dst, XMMRegister src); void Cvttss2siq(Register dst, const Operand& src); void Cvttsd2siq(Register dst, XMMRegister src); void Cvttsd2siq(Register dst, const Operand& src); // Move if the registers are not identical. void Move(Register target, Register source); // TestBit and Load SharedFunctionInfo special field. void TestBitSharedFunctionInfoSpecialField(Register base, int offset, int bit_index); void LoadSharedFunctionInfoSpecialField(Register dst, Register base, int offset); // Handle support void Move(Register dst, Handle source); void Move(const Operand& dst, Handle source); void Cmp(Register dst, Handle source); void Cmp(const Operand& dst, Handle source); void Cmp(Register dst, Smi* src); void Cmp(const Operand& dst, Smi* src); void Push(Handle source); // Load a heap object and handle the case of new-space objects by // indirecting via a global cell. void MoveHeapObject(Register result, Handle object); // Load a global cell into a register. void LoadGlobalCell(Register dst, Handle cell); // Compare the given value and the value of weak cell. void CmpWeakValue(Register value, Handle cell, Register scratch); void GetWeakValue(Register value, Handle cell); // Load the value of the weak cell in the value register. Branch to the given // miss label if the weak cell was cleared. void LoadWeakValue(Register value, Handle cell, Label* miss); // Emit code to discard a non-negative number of pointer-sized elements // from the stack, clobbering only the rsp register. void Drop(int stack_elements); // Emit code to discard a positive number of pointer-sized elements // from the stack under the return address which remains on the top, // clobbering the rsp register. void DropUnderReturnAddress(int stack_elements, Register scratch = kScratchRegister); void Call(Label* target) { call(target); } void Push(Register src); void Push(const Operand& src); void PushQuad(const Operand& src); void Push(Immediate value); void PushImm32(int32_t imm32); void Pop(Register dst); void Pop(const Operand& dst); void PopQuad(const Operand& dst); void PushReturnAddressFrom(Register src) { pushq(src); } void PopReturnAddressTo(Register dst) { popq(dst); } void Move(Register dst, ExternalReference ext) { movp(dst, reinterpret_cast(ext.address()), RelocInfo::EXTERNAL_REFERENCE); } // Loads a pointer into a register with a relocation mode. void Move(Register dst, void* ptr, RelocInfo::Mode rmode) { // This method must not be used with heap object references. The stored // address is not GC safe. Use the handle version instead. DCHECK(rmode > RelocInfo::LAST_GCED_ENUM); movp(dst, ptr, rmode); } void Move(Register dst, Handle value, RelocInfo::Mode rmode) { AllowDeferredHandleDereference using_raw_address; DCHECK(!RelocInfo::IsNone(rmode)); DCHECK(value->IsHeapObject()); DCHECK(!isolate()->heap()->InNewSpace(*value)); movp(dst, reinterpret_cast(value.location()), rmode); } void Move(XMMRegister dst, uint32_t src); void Move(XMMRegister dst, uint64_t src); void Move(XMMRegister dst, float src) { Move(dst, bit_cast(src)); } void Move(XMMRegister dst, double src) { Move(dst, bit_cast(src)); } #define AVX_OP2_WITH_TYPE(macro_name, name, src_type) \ void macro_name(XMMRegister dst, src_type src) { \ if (CpuFeatures::IsSupported(AVX)) { \ CpuFeatureScope scope(this, AVX); \ v##name(dst, dst, src); \ } else { \ name(dst, src); \ } \ } #define AVX_OP2_X(macro_name, name) \ AVX_OP2_WITH_TYPE(macro_name, name, XMMRegister) #define AVX_OP2_O(macro_name, name) \ AVX_OP2_WITH_TYPE(macro_name, name, const Operand&) #define AVX_OP2_XO(macro_name, name) \ AVX_OP2_X(macro_name, name) \ AVX_OP2_O(macro_name, name) AVX_OP2_XO(Addsd, addsd) AVX_OP2_XO(Subsd, subsd) AVX_OP2_XO(Mulsd, mulsd) AVX_OP2_XO(Divsd, divsd) AVX_OP2_X(Andpd, andpd) AVX_OP2_X(Orpd, orpd) AVX_OP2_X(Xorpd, xorpd) AVX_OP2_X(Pcmpeqd, pcmpeqd) AVX_OP2_WITH_TYPE(Psllq, psllq, byte) AVX_OP2_WITH_TYPE(Psrlq, psrlq, byte) #undef AVX_OP2_O #undef AVX_OP2_X #undef AVX_OP2_XO #undef AVX_OP2_WITH_TYPE void Movsd(XMMRegister dst, XMMRegister src); void Movsd(XMMRegister dst, const Operand& src); void Movsd(const Operand& dst, XMMRegister src); void Movss(XMMRegister dst, XMMRegister src); void Movss(XMMRegister dst, const Operand& src); void Movss(const Operand& dst, XMMRegister src); void Movd(XMMRegister dst, Register src); void Movd(XMMRegister dst, const Operand& src); void Movd(Register dst, XMMRegister src); void Movq(XMMRegister dst, Register src); void Movq(Register dst, XMMRegister src); void Movaps(XMMRegister dst, XMMRegister src); void Movapd(XMMRegister dst, XMMRegister src); void Movmskpd(Register dst, XMMRegister src); void Roundss(XMMRegister dst, XMMRegister src, RoundingMode mode); void Roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode); void Sqrtsd(XMMRegister dst, XMMRegister src); void Sqrtsd(XMMRegister dst, const Operand& src); void Ucomiss(XMMRegister src1, XMMRegister src2); void Ucomiss(XMMRegister src1, const Operand& src2); void Ucomisd(XMMRegister src1, XMMRegister src2); void Ucomisd(XMMRegister src1, const Operand& src2); // Control Flow void Jump(Address destination, RelocInfo::Mode rmode); void Jump(ExternalReference ext); void Jump(const Operand& op); void Jump(Handle code_object, RelocInfo::Mode rmode); void Call(Address destination, RelocInfo::Mode rmode); void Call(ExternalReference ext); void Call(const Operand& op); void Call(Handle code_object, RelocInfo::Mode rmode, TypeFeedbackId ast_id = TypeFeedbackId::None()); // The size of the code generated for different call instructions. int CallSize(Address destination) { return kCallSequenceLength; } int CallSize(ExternalReference ext); int CallSize(Handle code_object) { // Code calls use 32-bit relative addressing. return kShortCallInstructionLength; } int CallSize(Register target) { // Opcode: REX_opt FF /2 m64 return (target.high_bit() != 0) ? 3 : 2; } int CallSize(const Operand& target) { // Opcode: REX_opt FF /2 m64 return (target.requires_rex() ? 2 : 1) + target.operand_size(); } // Emit call to the code we are currently generating. void CallSelf() { Handle self(reinterpret_cast(CodeObject().location())); Call(self, RelocInfo::CODE_TARGET); } // Non-SSE2 instructions. void Pextrd(Register dst, XMMRegister src, int8_t imm8); void Pinsrd(XMMRegister dst, Register src, int8_t imm8); void Pinsrd(XMMRegister dst, const Operand& src, int8_t imm8); void Lzcntq(Register dst, Register src); void Lzcntq(Register dst, const Operand& src); void Lzcntl(Register dst, Register src); void Lzcntl(Register dst, const Operand& src); void Tzcntq(Register dst, Register src); void Tzcntq(Register dst, const Operand& src); void Tzcntl(Register dst, Register src); void Tzcntl(Register dst, const Operand& src); void Popcntl(Register dst, Register src); void Popcntl(Register dst, const Operand& src); void Popcntq(Register dst, Register src); void Popcntq(Register dst, const Operand& src); // Non-x64 instructions. // Push/pop all general purpose registers. // Does not push rsp/rbp nor any of the assembler's special purpose registers // (kScratchRegister, kRootRegister). void Pushad(); void Popad(); // Sets the stack as after performing Popad, without actually loading the // registers. void Dropad(); // Compare object type for heap object. // Always use unsigned comparisons: above and below, not less and greater. // Incoming register is heap_object and outgoing register is map. // They may be the same register, and may be kScratchRegister. void CmpObjectType(Register heap_object, InstanceType type, Register map); // Compare instance type for map. // Always use unsigned comparisons: above and below, not less and greater. void CmpInstanceType(Register map, InstanceType type); // Check if a map for a JSObject indicates that the object has fast elements. // Jump to the specified label if it does not. void CheckFastElements(Register map, Label* fail, Label::Distance distance = Label::kFar); // Check if a map for a JSObject indicates that the object can have both smi // and HeapObject elements. Jump to the specified label if it does not. void CheckFastObjectElements(Register map, Label* fail, Label::Distance distance = Label::kFar); // Check if a map for a JSObject indicates that the object has fast smi only // elements. Jump to the specified label if it does not. void CheckFastSmiElements(Register map, Label* fail, Label::Distance distance = Label::kFar); // Check to see if maybe_number can be stored as a double in // FastDoubleElements. If it can, store it at the index specified by index in // the FastDoubleElements array elements, otherwise jump to fail. Note that // index must not be smi-tagged. void StoreNumberToDoubleElements(Register maybe_number, Register elements, Register index, XMMRegister xmm_scratch, Label* fail, int elements_offset = 0); // Compare an object's map with the specified map. void CompareMap(Register obj, Handle map); // Check if the map of an object is equal to a specified map and branch to // label if not. Skip the smi check if not required (object is known to be a // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match // against maps that are ElementsKind transition maps of the specified map. void CheckMap(Register obj, Handle map, Label* fail, SmiCheckType smi_check_type); // Check if the map of an object is equal to a specified weak map and branch // to a specified target if equal. Skip the smi check if not required // (object is known to be a heap object) void DispatchWeakMap(Register obj, Register scratch1, Register scratch2, Handle cell, Handle success, SmiCheckType smi_check_type); // Check if the object in register heap_object is a string. Afterwards the // register map contains the object map and the register instance_type // contains the instance_type. The registers map and instance_type can be the // same in which case it contains the instance type afterwards. Either of the // registers map and instance_type can be the same as heap_object. Condition IsObjectStringType(Register heap_object, Register map, Register instance_type); // Check if the object in register heap_object is a name. Afterwards the // register map contains the object map and the register instance_type // contains the instance_type. The registers map and instance_type can be the // same in which case it contains the instance type afterwards. Either of the // registers map and instance_type can be the same as heap_object. Condition IsObjectNameType(Register heap_object, Register map, Register instance_type); // FCmp compares and pops the two values on top of the FPU stack. // The flag results are similar to integer cmp, but requires unsigned // jcc instructions (je, ja, jae, jb, jbe, je, and jz). void FCmp(); void ClampUint8(Register reg); void ClampDoubleToUint8(XMMRegister input_reg, XMMRegister temp_xmm_reg, Register result_reg); void SlowTruncateToI(Register result_reg, Register input_reg, int offset = HeapNumber::kValueOffset - kHeapObjectTag); void TruncateHeapNumberToI(Register result_reg, Register input_reg); void TruncateDoubleToI(Register result_reg, XMMRegister input_reg); void DoubleToI(Register result_reg, XMMRegister input_reg, XMMRegister scratch, MinusZeroMode minus_zero_mode, Label* lost_precision, Label* is_nan, Label* minus_zero, Label::Distance dst = Label::kFar); void LoadUint32(XMMRegister dst, Register src); void LoadInstanceDescriptors(Register map, Register descriptors); void EnumLength(Register dst, Register map); void NumberOfOwnDescriptors(Register dst, Register map); void LoadAccessor(Register dst, Register holder, int accessor_index, AccessorComponent accessor); template void DecodeField(Register reg) { static const int shift = Field::kShift; static const int mask = Field::kMask >> Field::kShift; if (shift != 0) { shrp(reg, Immediate(shift)); } andp(reg, Immediate(mask)); } template void DecodeFieldToSmi(Register reg) { if (SmiValuesAre32Bits()) { andp(reg, Immediate(Field::kMask)); shlp(reg, Immediate(kSmiShift - Field::kShift)); } else { static const int shift = Field::kShift; static const int mask = (Field::kMask >> Field::kShift) << kSmiTagSize; DCHECK(SmiValuesAre31Bits()); DCHECK(kSmiShift == kSmiTagSize); DCHECK((mask & 0x80000000u) == 0); if (shift < kSmiShift) { shlp(reg, Immediate(kSmiShift - shift)); } else if (shift > kSmiShift) { sarp(reg, Immediate(shift - kSmiShift)); } andp(reg, Immediate(mask)); } } // Abort execution if argument is not a number, enabled via --debug-code. void AssertNumber(Register object); // Abort execution if argument is a smi, enabled via --debug-code. void AssertNotSmi(Register object); // Abort execution if argument is not a smi, enabled via --debug-code. void AssertSmi(Register object); void AssertSmi(const Operand& object); // Abort execution if a 64 bit register containing a 32 bit payload does not // have zeros in the top 32 bits, enabled via --debug-code. void AssertZeroExtended(Register reg); // Abort execution if argument is not a string, enabled via --debug-code. void AssertString(Register object); // Abort execution if argument is not a name, enabled via --debug-code. void AssertName(Register object); // Abort execution if argument is not a JSFunction, enabled via --debug-code. void AssertFunction(Register object); // Abort execution if argument is not a JSBoundFunction, // enabled via --debug-code. void AssertBoundFunction(Register object); // Abort execution if argument is not undefined or an AllocationSite, enabled // via --debug-code. void AssertUndefinedOrAllocationSite(Register object); // Abort execution if argument is not the root value with the given index, // enabled via --debug-code. void AssertRootValue(Register src, Heap::RootListIndex root_value_index, BailoutReason reason); // --------------------------------------------------------------------------- // Exception handling // Push a new stack handler and link it into stack handler chain. void PushStackHandler(); // Unlink the stack handler on top of the stack from the stack handler chain. void PopStackHandler(); // --------------------------------------------------------------------------- // Inline caching support // Generate code for checking access rights - used for security checks // on access to global objects across environments. The holder register // is left untouched, but the scratch register and kScratchRegister, // which must be different, are clobbered. void CheckAccessGlobalProxy(Register holder_reg, Register scratch, Label* miss); void GetNumberHash(Register r0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, Register key, Register r0, Register r1, Register r2, Register result); // --------------------------------------------------------------------------- // Allocation support // Allocate an object in new space or old space. If the given space // is exhausted control continues at the gc_required label. The allocated // object is returned in result and end of the new object is returned in // result_end. The register scratch can be passed as no_reg in which case // an additional object reference will be added to the reloc info. The // returned pointers in result and result_end have not yet been tagged as // heap objects. If result_contains_top_on_entry is true the content of // result is known to be the allocation top on entry (could be result_end // from a previous call). If result_contains_top_on_entry is true scratch // should be no_reg as it is never used. void Allocate(int object_size, Register result, Register result_end, Register scratch, Label* gc_required, AllocationFlags flags); void Allocate(int header_size, ScaleFactor element_size, Register element_count, Register result, Register result_end, Register scratch, Label* gc_required, AllocationFlags flags); void Allocate(Register object_size, Register result, Register result_end, Register scratch, Label* gc_required, AllocationFlags flags); // Allocate a heap number in new space with undefined value. Returns // tagged pointer in result register, or jumps to gc_required if new // space is full. void AllocateHeapNumber(Register result, Register scratch, Label* gc_required, MutableMode mode = IMMUTABLE); // Allocate a sequential string. All the header fields of the string object // are initialized. void AllocateTwoByteString(Register result, Register length, Register scratch1, Register scratch2, Register scratch3, Label* gc_required); void AllocateOneByteString(Register result, Register length, Register scratch1, Register scratch2, Register scratch3, Label* gc_required); // Allocate a raw cons string object. Only the map field of the result is // initialized. void AllocateTwoByteConsString(Register result, Register scratch1, Register scratch2, Label* gc_required); void AllocateOneByteConsString(Register result, Register scratch1, Register scratch2, Label* gc_required); // Allocate a raw sliced string object. Only the map field of the result is // initialized. void AllocateTwoByteSlicedString(Register result, Register scratch1, Register scratch2, Label* gc_required); void AllocateOneByteSlicedString(Register result, Register scratch1, Register scratch2, Label* gc_required); // Allocate and initialize a JSValue wrapper with the specified {constructor} // and {value}. void AllocateJSValue(Register result, Register constructor, Register value, Register scratch, Label* gc_required); // --------------------------------------------------------------------------- // Support functions. // Check if result is zero and op is negative. void NegativeZeroTest(Register result, Register op, Label* then_label); // Check if result is zero and op is negative in code using jump targets. void NegativeZeroTest(CodeGenerator* cgen, Register result, Register op, JumpTarget* then_target); // Check if result is zero and any of op1 and op2 are negative. // Register scratch is destroyed, and it must be different from op2. void NegativeZeroTest(Register result, Register op1, Register op2, Register scratch, Label* then_label); // Machine code version of Map::GetConstructor(). // |temp| holds |result|'s map when done. void GetMapConstructor(Register result, Register map, Register temp); // Try to get function prototype of a function and puts the value in // the result register. Checks that the function really is a // function and jumps to the miss label if the fast checks fail. The // function register will be untouched; the other register may be // clobbered. void TryGetFunctionPrototype(Register function, Register result, Label* miss); // Picks out an array index from the hash field. // Register use: // hash - holds the index's hash. Clobbered. // index - holds the overwritten index on exit. void IndexFromHash(Register hash, Register index); // Find the function context up the context chain. void LoadContext(Register dst, int context_chain_length); // Load the global object from the current context. void LoadGlobalObject(Register dst) { LoadNativeContextSlot(Context::EXTENSION_INDEX, dst); } // Load the global proxy from the current context. void LoadGlobalProxy(Register dst) { LoadNativeContextSlot(Context::GLOBAL_PROXY_INDEX, dst); } // Conditionally load the cached Array transitioned map of type // transitioned_kind from the native context if the map in register // map_in_out is the cached Array map in the native context of // expected_kind. void LoadTransitionedArrayMapConditional( ElementsKind expected_kind, ElementsKind transitioned_kind, Register map_in_out, Register scratch, Label* no_map_match); // Load the native context slot with the current index. void LoadNativeContextSlot(int index, Register dst); // Load the initial map from the global function. The registers // function and map can be the same. void LoadGlobalFunctionInitialMap(Register function, Register map); // --------------------------------------------------------------------------- // Runtime calls // Call a code stub. void CallStub(CodeStub* stub, TypeFeedbackId ast_id = TypeFeedbackId::None()); // Tail call a code stub (jump). void TailCallStub(CodeStub* stub); // Return from a code stub after popping its arguments. void StubReturn(int argc); // Call a runtime routine. void CallRuntime(const Runtime::Function* f, int num_arguments, SaveFPRegsMode save_doubles = kDontSaveFPRegs); // Call a runtime function and save the value of XMM registers. void CallRuntimeSaveDoubles(Runtime::FunctionId fid) { const Runtime::Function* function = Runtime::FunctionForId(fid); CallRuntime(function, function->nargs, kSaveFPRegs); } // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId fid, SaveFPRegsMode save_doubles = kDontSaveFPRegs) { const Runtime::Function* function = Runtime::FunctionForId(fid); CallRuntime(function, function->nargs, save_doubles); } // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId fid, int num_arguments, SaveFPRegsMode save_doubles = kDontSaveFPRegs) { CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles); } // Convenience function: call an external reference. void CallExternalReference(const ExternalReference& ext, int num_arguments); // Convenience function: tail call a runtime routine (jump) void TailCallRuntime(Runtime::FunctionId fid); // Jump to a runtime routines void JumpToExternalReference(const ExternalReference& ext); // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in rsp[0], rsp[8], // etc., not pushed. The argument count assumes all arguments are word sized. // The number of slots reserved for arguments depends on platform. On Windows // stack slots are reserved for the arguments passed in registers. On other // platforms stack slots are only reserved for the arguments actually passed // on the stack. void PrepareCallCFunction(int num_arguments); // Calls a C function and cleans up the space for arguments allocated // by PrepareCallCFunction. The called function is not allowed to trigger a // garbage collection, since that might move the code and invalidate the // return address (unless this is somehow accounted for by the called // function). void CallCFunction(ExternalReference function, int num_arguments); void CallCFunction(Register function, int num_arguments); // Calculate the number of stack slots to reserve for arguments when calling a // C function. int ArgumentStackSlotsForCFunctionCall(int num_arguments); // --------------------------------------------------------------------------- // Utilities void Ret(); // Return and drop arguments from stack, where the number of arguments // may be bigger than 2^16 - 1. Requires a scratch register. void Ret(int bytes_dropped, Register scratch); Handle CodeObject() { DCHECK(!code_object_.is_null()); return code_object_; } // Copy length bytes from source to destination. // Uses scratch register internally (if you have a low-eight register // free, do use it, otherwise kScratchRegister will be used). // The min_length is a minimum limit on the value that length will have. // The algorithm has some special cases that might be omitted if the string // is known to always be long. void CopyBytes(Register destination, Register source, Register length, int min_length = 0, Register scratch = kScratchRegister); // Initialize fields with filler values. Fields starting at |current_address| // not including |end_address| are overwritten with the value in |filler|. At // the end the loop, |current_address| takes the value of |end_address|. void InitializeFieldsWithFiller(Register current_address, Register end_address, Register filler); // Emit code for a truncating division by a constant. The dividend register is // unchanged, the result is in rdx, and rax gets clobbered. void TruncatingDiv(Register dividend, int32_t divisor); // --------------------------------------------------------------------------- // StatsCounter support void SetCounter(StatsCounter* counter, int value); void IncrementCounter(StatsCounter* counter, int value); void DecrementCounter(StatsCounter* counter, int value); // --------------------------------------------------------------------------- // Debugging // Calls Abort(msg) if the condition cc is not satisfied. // Use --debug_code to enable. void Assert(Condition cc, BailoutReason reason); void AssertFastElements(Register elements); // Like Assert(), but always enabled. void Check(Condition cc, BailoutReason reason); // Print a message to stdout and abort execution. void Abort(BailoutReason msg); // Check that the stack is aligned. void CheckStackAlignment(); // Verify restrictions about code generated in stubs. void set_generating_stub(bool value) { generating_stub_ = value; } bool generating_stub() { return generating_stub_; } void set_has_frame(bool value) { has_frame_ = value; } bool has_frame() { return has_frame_; } inline bool AllowThisStubCall(CodeStub* stub); static int SafepointRegisterStackIndex(Register reg) { return SafepointRegisterStackIndex(reg.code()); } // Load the type feedback vector from a JavaScript frame. void EmitLoadTypeFeedbackVector(Register vector); // Activation support. void EnterFrame(StackFrame::Type type); void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg); void LeaveFrame(StackFrame::Type type); // Expects object in rax and returns map with validated enum cache // in rax. Assumes that any other register can be used as a scratch. void CheckEnumCache(Register null_value, Label* call_runtime); // AllocationMemento support. Arrays may have an associated // AllocationMemento object that can be checked for in order to pretransition // to another type. // On entry, receiver_reg should point to the array object. // scratch_reg gets clobbered. // If allocation info is present, condition flags are set to equal. void TestJSArrayForAllocationMemento(Register receiver_reg, Register scratch_reg, Label* no_memento_found); void JumpIfJSArrayHasAllocationMemento(Register receiver_reg, Register scratch_reg, Label* memento_found) { Label no_memento_found; TestJSArrayForAllocationMemento(receiver_reg, scratch_reg, &no_memento_found); j(equal, memento_found); bind(&no_memento_found); } // Jumps to found label if a prototype map has dictionary elements. void JumpIfDictionaryInPrototypeChain(Register object, Register scratch0, Register scratch1, Label* found); private: // Order general registers are pushed by Pushad. // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r12, r14, r15. static const int kSafepointPushRegisterIndices[Register::kNumRegisters]; static const int kNumSafepointSavedRegisters = 12; static const int kSmiShift = kSmiTagSize + kSmiShiftSize; bool generating_stub_; bool has_frame_; bool root_array_available_; // Returns a register holding the smi value. The register MUST NOT be // modified. It may be the "smi 1 constant" register. Register GetSmiConstant(Smi* value); int64_t RootRegisterDelta(ExternalReference other); // Moves the smi value to the destination register. void LoadSmiConstant(Register dst, Smi* value); // This handle will be patched with the code object on installation. Handle code_object_; // Helper functions for generating invokes. void InvokePrologue(const ParameterCount& expected, const ParameterCount& actual, Label* done, bool* definitely_mismatches, InvokeFlag flag, Label::Distance near_jump, const CallWrapper& call_wrapper); void EnterExitFramePrologue(bool save_rax); // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack // accessible via StackSpaceOperand. void EnterExitFrameEpilogue(int arg_stack_space, bool save_doubles); void LeaveExitFrameEpilogue(bool restore_context); // Allocation support helpers. // Loads the top of new-space into the result register. // Otherwise the address of the new-space top is loaded into scratch (if // scratch is valid), and the new-space top is loaded into result. void LoadAllocationTopHelper(Register result, Register scratch, AllocationFlags flags); void MakeSureDoubleAlignedHelper(Register result, Register scratch, Label* gc_required, AllocationFlags flags); // Update allocation top with value in result_end register. // If scratch is valid, it contains the address of the allocation top. void UpdateAllocationTopHelper(Register result_end, Register scratch, AllocationFlags flags); // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. void InNewSpace(Register object, Register scratch, Condition cc, Label* branch, Label::Distance distance = Label::kFar); // Helper for finding the mark bits for an address. Afterwards, the // bitmap register points at the word with the mark bits and the mask // the position of the first bit. Uses rcx as scratch and leaves addr_reg // unchanged. inline void GetMarkBits(Register addr_reg, Register bitmap_reg, Register mask_reg); // Compute memory operands for safepoint stack slots. Operand SafepointRegisterSlot(Register reg); static int SafepointRegisterStackIndex(int reg_code) { return kNumSafepointRegisters - kSafepointPushRegisterIndices[reg_code] - 1; } // Needs access to SafepointRegisterStackIndex for compiled frame // traversal. friend class StandardFrame; }; // The code patcher is used to patch (typically) small parts of code e.g. for // debugging and other types of instrumentation. When using the code patcher // the exact number of bytes specified must be emitted. Is not legal to emit // relocation information. If any of these constraints are violated it causes // an assertion. class CodePatcher { public: CodePatcher(Isolate* isolate, byte* address, int size); ~CodePatcher(); // Macro assembler to emit code. MacroAssembler* masm() { return &masm_; } private: byte* address_; // The address of the code being patched. int size_; // Number of bytes of the expected patch size. MacroAssembler masm_; // Macro assembler used to generate the code. }; // ----------------------------------------------------------------------------- // Static helper functions. // Generate an Operand for loading a field from an object. inline Operand FieldOperand(Register object, int offset) { return Operand(object, offset - kHeapObjectTag); } // Generate an Operand for loading an indexed field from an object. inline Operand FieldOperand(Register object, Register index, ScaleFactor scale, int offset) { return Operand(object, index, scale, offset - kHeapObjectTag); } inline Operand ContextOperand(Register context, int index) { return Operand(context, Context::SlotOffset(index)); } inline Operand ContextOperand(Register context, Register index) { return Operand(context, index, times_pointer_size, Context::SlotOffset(0)); } inline Operand NativeContextOperand() { return ContextOperand(rsi, Context::NATIVE_CONTEXT_INDEX); } // Provides access to exit frame stack space (not GCed). inline Operand StackSpaceOperand(int index) { #ifdef _WIN64 const int kShaddowSpace = 4; return Operand(rsp, (index + kShaddowSpace) * kPointerSize); #else return Operand(rsp, index * kPointerSize); #endif } inline Operand StackOperandForReturnAddress(int32_t disp) { return Operand(rsp, disp); } #ifdef GENERATED_CODE_COVERAGE extern void LogGeneratedCodeCoverage(const char* file_line); #define CODE_COVERAGE_STRINGIFY(x) #x #define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) #define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__) #define ACCESS_MASM(masm) { \ Address x64_coverage_function = FUNCTION_ADDR(LogGeneratedCodeCoverage); \ masm->pushfq(); \ masm->Pushad(); \ masm->Push(Immediate(reinterpret_cast(&__FILE_LINE__))); \ masm->Call(x64_coverage_function, RelocInfo::EXTERNAL_REFERENCE); \ masm->Pop(rax); \ masm->Popad(); \ masm->popfq(); \ } \ masm-> #else #define ACCESS_MASM(masm) masm-> #endif } // namespace internal } // namespace v8 #endif // V8_X64_MACRO_ASSEMBLER_X64_H_