// 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 INCLUDED_FROM_MACRO_ASSEMBLER_H #error This header must be included via macro-assembler.h #endif #ifndef V8_ARM_MACRO_ASSEMBLER_ARM_H_ #define V8_ARM_MACRO_ASSEMBLER_ARM_H_ #include "src/arm/assembler-arm.h" #include "src/bailout-reason.h" #include "src/contexts.h" #include "src/globals.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- // Static helper functions // Generate a MemOperand for loading a field from an object. inline MemOperand FieldMemOperand(Register object, int offset) { return MemOperand(object, offset - kHeapObjectTag); } enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; enum LinkRegisterStatus { kLRHasNotBeenSaved, kLRHasBeenSaved }; Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2 = no_reg, Register reg3 = no_reg, Register reg4 = no_reg, Register reg5 = no_reg, Register reg6 = no_reg); enum TargetAddressStorageMode { CAN_INLINE_TARGET_ADDRESS, NEVER_INLINE_TARGET_ADDRESS }; class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { public: template <typename... Args> explicit TurboAssembler(Args&&... args) : TurboAssemblerBase(std::forward<Args>(args)...) {} // Activation support. void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg = false); // Returns the pc offset at which the frame ends. int LeaveFrame(StackFrame::Type type); // Push a fixed frame, consisting of lr, fp void PushCommonFrame(Register marker_reg = no_reg); // Generates function and stub prologue code. void StubPrologue(StackFrame::Type type); void Prologue(); // Push a standard frame, consisting of lr, fp, context and JS function void PushStandardFrame(Register function_reg); void InitializeRootRegister(); void Push(Register src) { push(src); } void Push(Handle<HeapObject> handle); void Push(Smi smi); // Push two registers. Pushes leftmost register first (to highest address). void Push(Register src1, Register src2, Condition cond = al) { if (src1.code() > src2.code()) { stm(db_w, sp, src1.bit() | src2.bit(), cond); } else { str(src1, MemOperand(sp, 4, NegPreIndex), cond); str(src2, MemOperand(sp, 4, NegPreIndex), cond); } } // Push three registers. Pushes leftmost register first (to highest address). void Push(Register src1, Register src2, Register src3, Condition cond = al) { if (src1.code() > src2.code()) { if (src2.code() > src3.code()) { stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); } else { stm(db_w, sp, src1.bit() | src2.bit(), cond); str(src3, MemOperand(sp, 4, NegPreIndex), cond); } } else { str(src1, MemOperand(sp, 4, NegPreIndex), cond); Push(src2, src3, cond); } } // Push four registers. Pushes leftmost register first (to highest address). void Push(Register src1, Register src2, Register src3, Register src4, Condition cond = al) { if (src1.code() > src2.code()) { if (src2.code() > src3.code()) { if (src3.code() > src4.code()) { stm(db_w, sp, src1.bit() | src2.bit() | src3.bit() | src4.bit(), cond); } else { stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); str(src4, MemOperand(sp, 4, NegPreIndex), cond); } } else { stm(db_w, sp, src1.bit() | src2.bit(), cond); Push(src3, src4, cond); } } else { str(src1, MemOperand(sp, 4, NegPreIndex), cond); Push(src2, src3, src4, cond); } } // Push five registers. Pushes leftmost register first (to highest address). void Push(Register src1, Register src2, Register src3, Register src4, Register src5, Condition cond = al) { if (src1.code() > src2.code()) { if (src2.code() > src3.code()) { if (src3.code() > src4.code()) { if (src4.code() > src5.code()) { stm(db_w, sp, src1.bit() | src2.bit() | src3.bit() | src4.bit() | src5.bit(), cond); } else { stm(db_w, sp, src1.bit() | src2.bit() | src3.bit() | src4.bit(), cond); str(src5, MemOperand(sp, 4, NegPreIndex), cond); } } else { stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); Push(src4, src5, cond); } } else { stm(db_w, sp, src1.bit() | src2.bit(), cond); Push(src3, src4, src5, cond); } } else { str(src1, MemOperand(sp, 4, NegPreIndex), cond); Push(src2, src3, src4, src5, cond); } } void Pop(Register dst) { pop(dst); } // Pop two registers. Pops rightmost register first (from lower address). void Pop(Register src1, Register src2, Condition cond = al) { DCHECK(src1 != src2); if (src1.code() > src2.code()) { ldm(ia_w, sp, src1.bit() | src2.bit(), cond); } else { ldr(src2, MemOperand(sp, 4, PostIndex), cond); ldr(src1, MemOperand(sp, 4, PostIndex), cond); } } // Pop three registers. Pops rightmost register first (from lower address). void Pop(Register src1, Register src2, Register src3, Condition cond = al) { DCHECK(!AreAliased(src1, src2, src3)); if (src1.code() > src2.code()) { if (src2.code() > src3.code()) { ldm(ia_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); } else { ldr(src3, MemOperand(sp, 4, PostIndex), cond); ldm(ia_w, sp, src1.bit() | src2.bit(), cond); } } else { Pop(src2, src3, cond); ldr(src1, MemOperand(sp, 4, PostIndex), cond); } } // Pop four registers. Pops rightmost register first (from lower address). void Pop(Register src1, Register src2, Register src3, Register src4, Condition cond = al) { DCHECK(!AreAliased(src1, src2, src3, src4)); if (src1.code() > src2.code()) { if (src2.code() > src3.code()) { if (src3.code() > src4.code()) { ldm(ia_w, sp, src1.bit() | src2.bit() | src3.bit() | src4.bit(), cond); } else { ldr(src4, MemOperand(sp, 4, PostIndex), cond); ldm(ia_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); } } else { Pop(src3, src4, cond); ldm(ia_w, sp, src1.bit() | src2.bit(), cond); } } else { Pop(src2, src3, src4, cond); ldr(src1, MemOperand(sp, 4, PostIndex), cond); } } // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, non-register arguments must be stored in // sp[0], sp[4], etc., not pushed. The argument count assumes all arguments // are word sized. If double arguments are used, this function assumes that // all double arguments are stored before core registers; otherwise the // correct alignment of the double values is not guaranteed. // Some compilers/platforms require the stack to be aligned when calling // C++ code. // Needs a scratch register to do some arithmetic. This register will be // trashed. void PrepareCallCFunction(int num_reg_arguments, int num_double_registers = 0, Register scratch = no_reg); // Removes current frame and its arguments from the stack preserving // the arguments and a return address pushed to the stack for the next call. // Both |callee_args_count| and |caller_args_count_reg| do not include // receiver. |callee_args_count| is not modified, |caller_args_count_reg| // is trashed. void PrepareForTailCall(const ParameterCount& callee_args_count, Register caller_args_count_reg, Register scratch0, Register scratch1); // There are two ways of passing double arguments on ARM, depending on // whether soft or hard floating point ABI is used. These functions // abstract parameter passing for the three different ways we call // C functions from generated code. void MovToFloatParameter(DwVfpRegister src); void MovToFloatParameters(DwVfpRegister src1, DwVfpRegister src2); void MovToFloatResult(DwVfpRegister src); // 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); void CallCFunction(ExternalReference function, int num_reg_arguments, int num_double_arguments); void CallCFunction(Register function, int num_reg_arguments, int num_double_arguments); void MovFromFloatParameter(DwVfpRegister dst); void MovFromFloatResult(DwVfpRegister dst); // Calls Abort(msg) if the condition cond is not satisfied. // Use --debug-code to enable. void Assert(Condition cond, AbortReason reason); // Like Assert(), but without condition. // Use --debug-code to enable. void AssertUnreachable(AbortReason reason); // Like Assert(), but always enabled. void Check(Condition cond, AbortReason reason); // Print a message to stdout and abort execution. void Abort(AbortReason msg); void LslPair(Register dst_low, Register dst_high, Register src_low, Register src_high, Register shift); void LslPair(Register dst_low, Register dst_high, Register src_low, Register src_high, uint32_t shift); void LsrPair(Register dst_low, Register dst_high, Register src_low, Register src_high, Register shift); void LsrPair(Register dst_low, Register dst_high, Register src_low, Register src_high, uint32_t shift); void AsrPair(Register dst_low, Register dst_high, Register src_low, Register src_high, Register shift); void AsrPair(Register dst_low, Register dst_high, Register src_low, Register src_high, uint32_t shift); void LoadFromConstantsTable(Register destination, int constant_index) override; void LoadRootRegisterOffset(Register destination, intptr_t offset) override; void LoadRootRelative(Register destination, int32_t offset) override; // Call a runtime routine. This expects {centry} to contain a fitting CEntry // builtin for the target runtime function and uses an indirect call. void CallRuntimeWithCEntry(Runtime::FunctionId fid, Register centry); // Jump, Call, and Ret pseudo instructions implementing inter-working. void Call(Register target, Condition cond = al); void Call(Address target, RelocInfo::Mode rmode, Condition cond = al, TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS, bool check_constant_pool = true); void Call(Handle<Code> code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, Condition cond = al, TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS, bool check_constant_pool = true); void Call(Label* target); void CallBuiltinPointer(Register builtin_pointer) override; void LoadCodeObjectEntry(Register destination, Register code_object) override; void CallCodeObject(Register code_object) override; void JumpCodeObject(Register code_object) override; // Generates an instruction sequence s.t. the return address points to the // instruction following the call. // The return address on the stack is used by frame iteration. void StoreReturnAddressAndCall(Register target); // This should only be used when assembling a deoptimizer call because of // the CheckConstPool invocation, which is only needed for deoptimization. void CallForDeoptimization(Address target, int deopt_id); // Emit code to discard a non-negative number of pointer-sized elements // from the stack, clobbering only the sp register. void Drop(int count, Condition cond = al); void Drop(Register count, Condition cond = al); void Ret(Condition cond = al); void Ret(int drop, Condition cond = al); // Compare single values and move the result to the normal condition flags. void VFPCompareAndSetFlags(const SwVfpRegister src1, const SwVfpRegister src2, const Condition cond = al); void VFPCompareAndSetFlags(const SwVfpRegister src1, const float src2, const Condition cond = al); // Compare double values and move the result to the normal condition flags. void VFPCompareAndSetFlags(const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond = al); void VFPCompareAndSetFlags(const DwVfpRegister src1, const double src2, const Condition cond = al); // If the value is a NaN, canonicalize the value else, do nothing. void VFPCanonicalizeNaN(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond = al); void VFPCanonicalizeNaN(const DwVfpRegister value, const Condition cond = al) { VFPCanonicalizeNaN(value, value, cond); } void VmovHigh(Register dst, DwVfpRegister src); void VmovHigh(DwVfpRegister dst, Register src); void VmovLow(Register dst, DwVfpRegister src); void VmovLow(DwVfpRegister dst, Register src); void CheckPageFlag(Register object, Register scratch, int mask, Condition cc, Label* condition_met); // Check whether d16-d31 are available on the CPU. The result is given by the // Z condition flag: Z==0 if d16-d31 available, Z==1 otherwise. void CheckFor32DRegs(Register scratch); void SaveRegisters(RegList registers); void RestoreRegisters(RegList registers); void CallRecordWriteStub(Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode); void CallRecordWriteStub(Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, Address wasm_target); // Does a runtime check for 16/32 FP registers. Either way, pushes 32 double // values to location, saving [d0..(d15|d31)]. void SaveFPRegs(Register location, Register scratch); // Does a runtime check for 16/32 FP registers. Either way, pops 32 double // values to location, restoring [d0..(d15|d31)]. void RestoreFPRegs(Register location, Register scratch); // Calculate how much stack space (in bytes) are required to store caller // registers excluding those specified in the arguments. int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, Register exclusion2 = no_reg, Register exclusion3 = no_reg) const; // Push caller saved registers on the stack, and return the number of bytes // stack pointer is adjusted. int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, Register exclusion2 = no_reg, Register exclusion3 = no_reg); // Restore caller saved registers from the stack, and return the number of // bytes stack pointer is adjusted. int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, Register exclusion2 = no_reg, Register exclusion3 = no_reg); void Jump(Register target, Condition cond = al); void Jump(Address target, RelocInfo::Mode rmode, Condition cond = al); void Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al); // Perform a floating-point min or max operation with the // (IEEE-754-compatible) semantics of ARM64's fmin/fmax. Some cases, typically // NaNs or +/-0.0, are expected to be rare and are handled in out-of-line // code. The specific behaviour depends on supported instructions. // // These functions assume (and assert) that left!=right. It is permitted // for the result to alias either input register. void FloatMax(SwVfpRegister result, SwVfpRegister left, SwVfpRegister right, Label* out_of_line); void FloatMin(SwVfpRegister result, SwVfpRegister left, SwVfpRegister right, Label* out_of_line); void FloatMax(DwVfpRegister result, DwVfpRegister left, DwVfpRegister right, Label* out_of_line); void FloatMin(DwVfpRegister result, DwVfpRegister left, DwVfpRegister right, Label* out_of_line); // Generate out-of-line cases for the macros above. void FloatMaxOutOfLine(SwVfpRegister result, SwVfpRegister left, SwVfpRegister right); void FloatMinOutOfLine(SwVfpRegister result, SwVfpRegister left, SwVfpRegister right); void FloatMaxOutOfLine(DwVfpRegister result, DwVfpRegister left, DwVfpRegister right); void FloatMinOutOfLine(DwVfpRegister result, DwVfpRegister left, DwVfpRegister right); void ExtractLane(Register dst, QwNeonRegister src, NeonDataType dt, int lane); void ExtractLane(Register dst, DwVfpRegister src, NeonDataType dt, int lane); void ExtractLane(SwVfpRegister dst, QwNeonRegister src, int lane); void ReplaceLane(QwNeonRegister dst, QwNeonRegister src, Register src_lane, NeonDataType dt, int lane); void ReplaceLane(QwNeonRegister dst, QwNeonRegister src, SwVfpRegister src_lane, int lane); // Register move. May do nothing if the registers are identical. void Move(Register dst, Smi smi); void Move(Register dst, Handle<HeapObject> value); void Move(Register dst, ExternalReference reference); void Move(Register dst, Register src, Condition cond = al); void Move(Register dst, const Operand& src, SBit sbit = LeaveCC, Condition cond = al) { if (!src.IsRegister() || src.rm() != dst || sbit != LeaveCC) { mov(dst, src, sbit, cond); } } void Move(SwVfpRegister dst, SwVfpRegister src, Condition cond = al); void Move(DwVfpRegister dst, DwVfpRegister src, Condition cond = al); void Move(QwNeonRegister dst, QwNeonRegister src); // Simulate s-register moves for imaginary s32 - s63 registers. void VmovExtended(Register dst, int src_code); void VmovExtended(int dst_code, Register src); // Move between s-registers and imaginary s-registers. void VmovExtended(int dst_code, int src_code); void VmovExtended(int dst_code, const MemOperand& src); void VmovExtended(const MemOperand& dst, int src_code); // Register swap. Note that the register operands should be distinct. void Swap(Register srcdst0, Register srcdst1); void Swap(DwVfpRegister srcdst0, DwVfpRegister srcdst1); void Swap(QwNeonRegister srcdst0, QwNeonRegister srcdst1); // Get the actual activation frame alignment for target environment. static int ActivationFrameAlignment(); void Bfc(Register dst, Register src, int lsb, int width, Condition cond = al); void SmiUntag(Register reg, SBit s = LeaveCC) { mov(reg, Operand::SmiUntag(reg), s); } void SmiUntag(Register dst, Register src, SBit s = LeaveCC) { mov(dst, Operand::SmiUntag(src), s); } // Load an object from the root table. void LoadRoot(Register destination, RootIndex index) override { LoadRoot(destination, index, al); } void LoadRoot(Register destination, RootIndex index, Condition cond); // Jump if the register contains a smi. void JumpIfSmi(Register value, Label* smi_label); void JumpIfEqual(Register x, int32_t y, Label* dest); void JumpIfLessThan(Register x, int32_t y, Label* dest); // Performs a truncating conversion of a floating point number as used by // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it // succeeds, otherwise falls through if result is saturated. On return // 'result' either holds answer, or is clobbered on fall through. void TryInlineTruncateDoubleToI(Register result, DwVfpRegister input, Label* done); // Performs a truncating conversion of a floating point number as used by // the JS bitwise operations. See ECMA-262 9.5: ToInt32. // Exits with 'result' holding the answer. void TruncateDoubleToI(Isolate* isolate, Zone* zone, Register result, DwVfpRegister double_input, StubCallMode stub_mode); // EABI variant for double arguments in use. bool use_eabi_hardfloat() { #ifdef __arm__ return base::OS::ArmUsingHardFloat(); #elif USE_EABI_HARDFLOAT return true; #else return false; #endif } // Compute the start of the generated instruction stream from the current PC. // This is an alternative to embedding the {CodeObject} handle as a reference. void ComputeCodeStartAddress(Register dst); void ResetSpeculationPoisonRegister(); private: // Compare single values and then load the fpscr flags to a register. void VFPCompareAndLoadFlags(const SwVfpRegister src1, const SwVfpRegister src2, const Register fpscr_flags, const Condition cond = al); void VFPCompareAndLoadFlags(const SwVfpRegister src1, const float src2, const Register fpscr_flags, const Condition cond = al); // Compare double values and then load the fpscr flags to a register. void VFPCompareAndLoadFlags(const DwVfpRegister src1, const DwVfpRegister src2, const Register fpscr_flags, const Condition cond = al); void VFPCompareAndLoadFlags(const DwVfpRegister src1, const double src2, const Register fpscr_flags, const Condition cond = al); void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al); // Implementation helpers for FloatMin and FloatMax. template <typename T> void FloatMaxHelper(T result, T left, T right, Label* out_of_line); template <typename T> void FloatMinHelper(T result, T left, T right, Label* out_of_line); template <typename T> void FloatMaxOutOfLineHelper(T result, T left, T right); template <typename T> void FloatMinOutOfLineHelper(T result, T left, T right); int CalculateStackPassedWords(int num_reg_arguments, int num_double_arguments); void CallCFunctionHelper(Register function, int num_reg_arguments, int num_double_arguments); void CallRecordWriteStub(Register object, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, Handle<Code> code_target, Address wasm_target); }; // MacroAssembler implements a collection of frequently used macros. class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { public: template <typename... Args> explicit MacroAssembler(Args&&... args) : TurboAssembler(std::forward<Args>(args)...) {} void Mls(Register dst, Register src1, Register src2, Register srcA, Condition cond = al); void And(Register dst, Register src1, const Operand& src2, Condition cond = al); void Ubfx(Register dst, Register src, int lsb, int width, Condition cond = al); void Sbfx(Register dst, Register src, int lsb, int width, Condition cond = al); void Load(Register dst, const MemOperand& src, Representation r); void Store(Register src, const MemOperand& dst, Representation r); // --------------------------------------------------------------------------- // GC Support // 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 FieldMemOperand(reg, off). void RecordWriteField( Register object, int offset, Register value, Register scratch, LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, SmiCheck smi_check = INLINE_SMI_CHECK); // For a given |object| notify the garbage collector that the slot |address| // has been written. |value| is the object being stored. The value and // address registers are clobbered by the operation. void RecordWrite( Register object, Register address, Register value, LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, SmiCheck smi_check = INLINE_SMI_CHECK); // Push and pop the registers that can hold pointers, as defined by the // RegList constant kSafepointSavedRegisters. void PushSafepointRegisters(); void PopSafepointRegisters(); // Enter exit frame. // stack_space - extra stack space, used for alignment before call to C. void EnterExitFrame(bool save_doubles, int stack_space = 0, StackFrame::Type frame_type = StackFrame::EXIT); // Leave the current exit frame. Expects the return value in r0. // Expect the number of values, pushed prior to the exit frame, to // remove in a register (or no_reg, if there is nothing to remove). void LeaveExitFrame(bool save_doubles, Register argument_count, bool argument_count_is_length = false); // Load the global proxy from the current context. void LoadGlobalProxy(Register dst); void LoadNativeContextSlot(int index, Register dst); // --------------------------------------------------------------------------- // 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); // On function call, call into the debugger if necessary. void CheckDebugHook(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); void InvokeFunction(Register function, const ParameterCount& expected, const ParameterCount& actual, InvokeFlag flag); // Frame restart support void MaybeDropFrames(); // Exception handling // Push a new stack handler and link into stack handler chain. void PushStackHandler(); // Unlink the stack handler on top of the stack from the stack handler chain. // Must preserve the result register. void PopStackHandler(); // --------------------------------------------------------------------------- // Support functions. // Compare object type for heap object. heap_object contains a non-Smi // whose object type should be compared with the given type. This both // sets the flags and leaves the object type in the type_reg register. // It leaves the map in the map register (unless the type_reg and map register // are the same register). It leaves the heap object in the heap_object // register unless the heap_object register is the same register as one of the // other registers. // Type_reg can be no_reg. In that case a scratch register is used. void CompareObjectType(Register heap_object, Register map, Register type_reg, InstanceType type); // Compare instance type in a map. map contains a valid map object whose // object type should be compared with the given type. This both // sets the flags and leaves the object type in the type_reg register. void CompareInstanceType(Register map, Register type_reg, InstanceType type); // Compare the object in a register to a value from the root list. // Acquires a scratch register. void CompareRoot(Register obj, RootIndex index); void PushRoot(RootIndex index) { UseScratchRegisterScope temps(this); Register scratch = temps.Acquire(); LoadRoot(scratch, index); Push(scratch); } // Compare the object in a register to a value and jump if they are equal. void JumpIfRoot(Register with, RootIndex index, Label* if_equal) { CompareRoot(with, index); b(eq, if_equal); } // Compare the object in a register to a value and jump if they are not equal. void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal) { CompareRoot(with, index); b(ne, if_not_equal); } // Try to convert a double to a signed 32-bit integer. // Z flag set to one and result assigned if the conversion is exact. void TryDoubleToInt32Exact(Register result, DwVfpRegister double_input, LowDwVfpRegister double_scratch); // --------------------------------------------------------------------------- // Runtime calls // Call a runtime routine. void CallRuntime(const Runtime::Function* f, int num_arguments, SaveFPRegsMode save_doubles = kDontSaveFPRegs); // 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: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid); // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& builtin, bool builtin_exit_frame = false); // Generates a trampoline to jump to the off-heap instruction stream. void JumpToInstructionStream(Address entry); // --------------------------------------------------------------------------- // In-place weak references. void LoadWeakValue(Register out, Register in, Label* target_if_cleared); // --------------------------------------------------------------------------- // StatsCounter support void IncrementCounter(StatsCounter* counter, int value, Register scratch1, Register scratch2); void DecrementCounter(StatsCounter* counter, int value, Register scratch1, Register scratch2); // --------------------------------------------------------------------------- // Smi utilities void SmiTag(Register reg, SBit s = LeaveCC); void SmiTag(Register dst, Register src, SBit s = LeaveCC); // Untag the source value into destination and jump if source is a smi. // Souce and destination can be the same register. void UntagAndJumpIfSmi(Register dst, Register src, Label* smi_case); // Test if the register contains a smi (Z == 0 (eq) if true). void SmiTst(Register value); // Jump if either of the registers contain a non-smi. void JumpIfNotSmi(Register value, Label* not_smi_label); // Jump if either of the registers contain a smi. void JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi); // Abort execution if argument is a smi, enabled via --debug-code. void AssertNotSmi(Register object); void AssertSmi(Register object); // Abort execution if argument is not a Constructor, enabled via --debug-code. void AssertConstructor(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 a JSGeneratorObject (or subclass), // enabled via --debug-code. void AssertGeneratorObject(Register object); // Abort execution if argument is not undefined or an AllocationSite, enabled // via --debug-code. void AssertUndefinedOrAllocationSite(Register object, Register scratch); template<typename Field> void DecodeField(Register dst, Register src) { Ubfx(dst, src, Field::kShift, Field::kSize); } template<typename Field> void DecodeField(Register reg) { DecodeField<Field>(reg, reg); } private: // Helper functions for generating invokes. void InvokePrologue(const ParameterCount& expected, const ParameterCount& actual, Label* done, bool* definitely_mismatches, InvokeFlag flag); // Compute memory operands for safepoint stack slots. static int SafepointRegisterStackIndex(int reg_code); // Needs access to SafepointRegisterStackIndex for compiled frame // traversal. friend class StandardFrame; DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler); }; // ----------------------------------------------------------------------------- // Static helper functions. inline MemOperand ContextMemOperand(Register context, int index = 0) { return MemOperand(context, Context::SlotOffset(index)); } inline MemOperand NativeContextMemOperand() { return ContextMemOperand(cp, Context::NATIVE_CONTEXT_INDEX); } #define ACCESS_MASM(masm) masm-> } // namespace internal } // namespace v8 #endif // V8_ARM_MACRO_ASSEMBLER_ARM_H_