v8/src/ia32/macro-assembler-ia32.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

665 lines
26 KiB
C
Raw Normal View History

// 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_IA32_MACRO_ASSEMBLER_IA32_H_
#define V8_IA32_MACRO_ASSEMBLER_IA32_H_
#include "src/assembler.h"
#include "src/bailout-reason.h"
#include "src/globals.h"
#include "src/ia32/assembler-ia32.h"
namespace v8 {
namespace internal {
// Give alias names to registers for calling conventions.
constexpr Register kReturnRegister0 = eax;
constexpr Register kReturnRegister1 = edx;
constexpr Register kReturnRegister2 = edi;
constexpr Register kJSFunctionRegister = edi;
constexpr Register kContextRegister = esi;
constexpr Register kAllocateSizeRegister = edx;
constexpr Register kInterpreterAccumulatorRegister = eax;
constexpr Register kInterpreterBytecodeOffsetRegister = ecx;
constexpr Register kInterpreterBytecodeArrayRegister = edi;
constexpr Register kInterpreterDispatchTableRegister = esi;
constexpr Register kJavaScriptCallArgCountRegister = eax;
constexpr Register kJavaScriptCallNewTargetRegister = edx;
constexpr Register kRuntimeCallFunctionRegister = ebx;
constexpr Register kRuntimeCallArgCountRegister = eax;
// Convenience for platform-independent signatures. We do not normally
// distinguish memory operands from other operands on ia32.
typedef Operand MemOperand;
enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
enum RegisterValueType { REGISTER_VALUE_IS_SMI, REGISTER_VALUE_IS_INT32 };
#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
class TurboAssembler : public Assembler {
public:
TurboAssembler(Isolate* isolate, void* buffer, int buffer_size,
CodeObjectRequired create_code_object);
void set_has_frame(bool value) { has_frame_ = value; }
bool has_frame() const { return has_frame_; }
Isolate* isolate() const { return isolate_; }
Handle<HeapObject> CodeObject() {
DCHECK(!code_object_.is_null());
return code_object_;
}
void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
Label* condition_met,
Label::Distance condition_met_distance = Label::kFar);
// Activation support.
void EnterFrame(StackFrame::Type type);
void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) {
// Out-of-line constant pool not implemented on ia32.
UNREACHABLE();
}
void LeaveFrame(StackFrame::Type type);
// Print a message to stdout and abort execution.
void Abort(BailoutReason reason);
// Calls Abort(msg) if the condition cc is not satisfied.
// Use --debug_code to enable.
void Assert(Condition cc, BailoutReason reason);
// Like Assert(), but without condition.
// Use --debug_code to enable.
void AssertUnreachable(BailoutReason reason);
// Like Assert(), but always enabled.
void Check(Condition cc, BailoutReason reason);
// Check that the stack is aligned.
void CheckStackAlignment();
// Nop, because ia32 does not have a root register.
void InitializeRootRegister() {}
// Move a constant into a destination using the most efficient encoding.
void Move(Register dst, const Immediate& x);
void Move(Register dst, Smi* source) { Move(dst, Immediate(source)); }
// Move if the registers are not identical.
void Move(Register target, Register source);
void Move(const Operand& dst, const Immediate& x);
// Move an immediate into an XMM register.
void Move(XMMRegister dst, uint32_t src);
void Move(XMMRegister dst, uint64_t src);
void Move(XMMRegister dst, float src) { Move(dst, bit_cast<uint32_t>(src)); }
void Move(XMMRegister dst, double src) { Move(dst, bit_cast<uint64_t>(src)); }
void Move(Register dst, Handle<HeapObject> handle);
void Call(Handle<Code> target, RelocInfo::Mode rmode) { call(target, rmode); }
void Call(Label* target) { call(target); }
void CallForDeoptimization(Address target, RelocInfo::Mode rmode) {
call(target, rmode);
}
inline bool AllowThisStubCall(CodeStub* stub);
void CallStubDelayed(CodeStub* stub);
void CallRuntimeDelayed(Zone* zone, Runtime::FunctionId fid,
SaveFPRegsMode save_doubles = kDontSaveFPRegs);
// Jump the register contains a smi.
inline void JumpIfSmi(Register value, Label* smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(zero, smi_label, distance);
}
// Jump if the operand is a smi.
inline void JumpIfSmi(Operand value, Label* smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(zero, smi_label, distance);
}
void SmiUntag(Register reg) { sar(reg, kSmiTagSize); }
// 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.
// |number_of_temp_values_after_return_address| specifies the number of words
// pushed to the stack after the return address. This is to allow "allocation"
// of scratch registers that this function requires by saving their values on
// the stack.
void PrepareForTailCall(const ParameterCount& callee_args_count,
Register caller_args_count_reg, Register scratch0,
Register scratch1,
int number_of_temp_values_after_return_address);
// Before calling a C-function from generated code, align arguments on stack.
// After aligning the frame, arguments must be stored in esp[0], esp[4],
// etc., not pushed. The argument count assumes all arguments are word sized.
// 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_arguments, Register scratch);
// 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 ShlPair(Register high, Register low, uint8_t imm8);
void ShlPair_cl(Register high, Register low);
void ShrPair(Register high, Register low, uint8_t imm8);
void ShrPair_cl(Register high, Register src);
void SarPair(Register high, Register low, uint8_t imm8);
void SarPair_cl(Register high, Register low);
// Generates function and stub prologue code.
void StubPrologue(StackFrame::Type type);
Reland "[Compiler] Remove code aging support." > This reverts commit 42d3d36bc3b4e76cbdf883432dcc3647526fbf58. > > Original change's description: > > [Compiler] Remove code aging support. > > > > Code aging is no longer supported by any remaining compilers now > > that full codegen has been removed. This CL removes all vestiges of > > code aging. > > > > BUG=v8:6409 > > > > Change-Id: I945ebcc20c7c55120550c8ee36188bfa042ea65e > > Reviewed-on: https://chromium-review.googlesource.com/619153 > > Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> > > Reviewed-by: Yang Guo <yangguo@chromium.org> > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org> > > Reviewed-by: Marja Hölttä <marja@chromium.org> > > Commit-Queue: Ross McIlroy <rmcilroy@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#47501} > > TBR=ulan@chromium.org,rmcilroy@chromium.org,marja@chromium.org,yangguo@chromium.org,mstarzinger@chromium.org,rodolph.perfetta@arm.com > > Change-Id: I9d8b2985e2d472697908270d93a35eb7ef9c88a8 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: v8:6409 > Reviewed-on: https://chromium-review.googlesource.com/625998 > Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> > Commit-Queue: Ross McIlroy <rmcilroy@chromium.org> > Cr-Commit-Position: refs/heads/master@{#47506} TBR=ulan@chromium.org,rmcilroy@chromium.org,marja@chromium.org,yangguo@chromium.org,mstarzinger@chromium.org,rodolph.perfetta@arm.com Change-Id: I68785c6be7686e874b3848103e3a34483eaeb519 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: v8:6409 Reviewed-on: https://chromium-review.googlesource.com/625919 Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Commit-Queue: Ross McIlroy <rmcilroy@chromium.org> Cr-Commit-Position: refs/heads/master@{#47535}
2017-08-23 08:22:33 +00:00
void Prologue();
void Lzcnt(Register dst, Register src) { Lzcnt(dst, Operand(src)); }
void Lzcnt(Register dst, const Operand& src);
void Tzcnt(Register dst, Register src) { Tzcnt(dst, Operand(src)); }
void Tzcnt(Register dst, const Operand& src);
void Popcnt(Register dst, Register src) { Popcnt(dst, Operand(src)); }
void Popcnt(Register dst, const Operand& src);
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);
void Pshuflw(XMMRegister dst, XMMRegister src, uint8_t shuffle) {
Pshuflw(dst, Operand(src), shuffle);
}
void Pshuflw(XMMRegister dst, const Operand& src, uint8_t shuffle);
void Pshufd(XMMRegister dst, XMMRegister src, uint8_t shuffle) {
Pshufd(dst, Operand(src), shuffle);
}
void Pshufd(XMMRegister dst, const Operand& src, uint8_t shuffle);
// SSE/SSE2 instructions with AVX version.
#define AVX_OP2_WITH_TYPE(macro_name, name, dst_type, src_type) \
void macro_name(dst_type dst, src_type src) { \
if (CpuFeatures::IsSupported(AVX)) { \
CpuFeatureScope scope(this, AVX); \
v##name(dst, src); \
} else { \
name(dst, src); \
} \
}
AVX_OP2_WITH_TYPE(Movd, movd, XMMRegister, Register)
AVX_OP2_WITH_TYPE(Movd, movd, XMMRegister, const Operand&)
AVX_OP2_WITH_TYPE(Movd, movd, Register, XMMRegister)
AVX_OP2_WITH_TYPE(Movd, movd, const Operand&, XMMRegister)
#undef AVX_OP2_WITH_TYPE
// Only use these macros when non-destructive source of AVX version is not
// needed.
#define AVX_OP3_WITH_TYPE(macro_name, name, dst_type, src_type) \
void macro_name(dst_type dst, src_type src) { \
if (CpuFeatures::IsSupported(AVX)) { \
CpuFeatureScope scope(this, AVX); \
v##name(dst, dst, src); \
} else { \
name(dst, src); \
} \
}
#define AVX_OP3_XO(macro_name, name) \
AVX_OP3_WITH_TYPE(macro_name, name, XMMRegister, XMMRegister) \
AVX_OP3_WITH_TYPE(macro_name, name, XMMRegister, const Operand&)
AVX_OP3_XO(Pcmpeqd, pcmpeqd)
AVX_OP3_XO(Psubd, psubd)
AVX_OP3_XO(Pxor, pxor)
#undef AVX_OP3_XO
#undef AVX_OP3_WITH_TYPE
// Non-SSE2 instructions.
void Pshufb(XMMRegister dst, XMMRegister src) { Pshufb(dst, Operand(src)); }
void Pshufb(XMMRegister dst, const Operand& src);
void Psignd(XMMRegister dst, XMMRegister src) { Psignd(dst, Operand(src)); }
void Psignd(XMMRegister dst, const Operand& src);
void Pextrb(Register dst, XMMRegister src, int8_t imm8);
void Pextrw(Register dst, XMMRegister src, int8_t imm8);
void Pextrd(Register dst, XMMRegister src, int8_t imm8);
void Pinsrd(XMMRegister dst, Register src, int8_t imm8,
bool is_64_bits = false) {
Pinsrd(dst, Operand(src), imm8, is_64_bits);
}
void Pinsrd(XMMRegister dst, const Operand& src, int8_t imm8,
bool is_64_bits = false);
void LoadUint32(XMMRegister dst, Register src) {
LoadUint32(dst, Operand(src));
}
void LoadUint32(XMMRegister dst, const Operand& src);
// Expression support
// cvtsi2sd instruction only writes to the low 64-bit of dst register, which
// hinders register renaming and makes dependence chains longer. So we use
// xorps to clear the dst register before cvtsi2sd to solve this issue.
void Cvtsi2sd(XMMRegister dst, Register src) { Cvtsi2sd(dst, Operand(src)); }
void Cvtsi2sd(XMMRegister dst, const Operand& src);
void Cvtui2ss(XMMRegister dst, Register src, Register tmp);
Simplify DoubleToI stub. The DoubleToI stub is no longer called outside of TurboFan, and always in the same way: - The parameter is on top of the stack. - The stub is always called in a slow path. - It truncates. Therefore, we can simplify it to only support this case and remove dead code. On top of this, since the stub is always considered to be on a slow path for all backends, this patch takes the opportunity to remove the `skip_fastpath` optimisation. This would generate a stub which does not handle all inputs, assuming that the backend already handled some of the inputs in a fast path. Removing this allows the stub to have the same behaviour on all targets. On Arm, this patch reworks the stub a little. We could use ip instead of saving and restoring a register on the stack. Also, comments would mention that we assume the exponent to be greater than 31 when the it can be 30 or higher. As done for Arm64, let's check this at runtime in debug mode. On Arm64, we can also implement the stub without pushing and poping off the stack. It needs 2 general purpose and a double scratch registers which we have reserved already (ip0, ip1 and d30). This removes the need to check that the stack pointer is always 16-bytes aligned. Finally, this also fixes a potential bug on Arm64, in the `GetAllocatableRegisterThatIsNotOneOf` method which is now removed. We were picking an allocatable double register when we meant to pick a general one. Bug: v8:6644 Change-Id: I88d4597f377c9fc05432d5922a0d7129b6d19b47 Reviewed-on: https://chromium-review.googlesource.com/720963 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Commit-Queue: Pierre Langlois <pierre.langlois@arm.com> Cr-Commit-Position: refs/heads/master@{#48671}
2017-10-16 12:13:53 +00:00
void SlowTruncateToIDelayed(Zone* zone, Register result_reg);
void Push(Register src) { push(src); }
void Push(const Operand& src) { push(src); }
void Push(Immediate value) { push(value); }
void Push(Handle<HeapObject> handle) { push(Immediate(handle)); }
void Push(Smi* smi) { Push(Immediate(smi)); }
void SaveRegisters(RegList registers);
void RestoreRegisters(RegList registers);
void CallRecordWriteStub(Register object, Register address,
RememberedSetAction remembered_set_action,
SaveFPRegsMode fp_mode);
// 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;
// PushCallerSaved and PopCallerSaved 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.
// 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);
private:
bool has_frame_ = false;
Isolate* const isolate_;
// This handle will be patched with the code object on installation.
Handle<HeapObject> code_object_;
};
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler : public TurboAssembler {
public:
MacroAssembler(Isolate* isolate, void* buffer, int size,
CodeObjectRequired create_code_object);
// Load a register with a long value as efficiently as possible.
void Set(Register dst, int32_t x) {
if (x == 0) {
xor_(dst, dst);
} else {
mov(dst, Immediate(x));
}
}
void Set(const Operand& dst, int32_t x) { mov(dst, Immediate(x)); }
// Operations on roots in the root-array.
void LoadRoot(Register destination, Heap::RootListIndex index);
void CompareRoot(Register with, Register scratch, Heap::RootListIndex index);
// These methods can only be used with constant roots (i.e. non-writable
// and not in new space).
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);
}
// ---------------------------------------------------------------------------
// 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 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);
// 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);
// Frame restart support
void MaybeDropFrames();
// Enter specific kind of exit frame. Expects the number of
// arguments in register eax and sets up the number of arguments in
// register edi and the pointer to the first argument in register
// esi.
void EnterExitFrame(int argc, bool save_doubles, StackFrame::Type frame_type);
void EnterApiExitFrame(int argc);
// Leave the current exit frame. Expects the return value in
// register eax:edx (untouched) and the pointer to the first
// argument in register esi (if pop_arguments == true).
void LeaveExitFrame(bool save_doubles, bool pop_arguments = true);
// Leave the current exit frame. Expects the return value in
// register eax (untouched).
void LeaveApiExitFrame();
// Load the global proxy from the current context.
void LoadGlobalProxy(Register dst);
// Load the global function with the given index.
void LoadGlobalFunction(int index, Register function);
// Push and pop the registers that can hold pointers.
void PushSafepointRegisters() { pushad(); }
void PopSafepointRegisters() { popad(); }
// ---------------------------------------------------------------------------
// 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);
void InvokeFunction(Handle<JSFunction> function,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag);
// Compare object type for heap object.
// Incoming register is heap_object and outgoing register is map.
void CmpObjectType(Register heap_object, InstanceType type, Register map);
// Compare instance type for map.
void CmpInstanceType(Register map, InstanceType type);
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);
// Smi tagging support.
void SmiTag(Register reg) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
add(reg, reg);
}
// Modifies the register even if it does not contain a Smi!
void UntagSmi(Register reg, Label* is_smi) {
STATIC_ASSERT(kSmiTagSize == 1);
sar(reg, kSmiTagSize);
STATIC_ASSERT(kSmiTag == 0);
j(not_carry, is_smi);
}
// Jump if register contain a non-smi.
inline void JumpIfNotSmi(Register value, Label* not_smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(not_zero, not_smi_label, distance);
}
// Jump if the operand is not a smi.
inline void JumpIfNotSmi(Operand value, Label* smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(not_zero, smi_label, distance);
}
template<typename Field>
void DecodeField(Register reg) {
Sharing of descriptor arrays. This CL adds multiple things: Transition arrays do not directly point at their descriptor array anymore, but rather do so via an indirect pointer (a JSGlobalPropertyCell). An ownership bit is added to maps indicating whether it owns its own descriptor array or not. Maps owning a descriptor array can pass on ownership if a transition from that map is generated; but only if the descriptor array stays exactly the same; or if a descriptor is added. Maps that don't have ownership get ownership back if their direct child to which ownership was passed is cleared in ClearNonLiveTransitions. To detect which descriptors in an array are valid, each map knows its own NumberOfOwnDescriptors. Since the descriptors are sorted in order of addition, if we search and find a descriptor with index bigger than this number, it is not valid for the given map. We currently still build up an enumeration cache (although this may disappear). The enumeration cache is always built for the entire descriptor array, even if not all descriptors are owned by the map. Once a descriptor array has an enumeration cache for a given map; this invariant will always be true, even if the descriptor array was extended. The extended array will inherit the enumeration cache from the smaller descriptor array. If a map with more descriptors needs an enumeration cache, it's EnumLength will still be set to invalid, so it will have to recompute the enumeration cache. This new cache will also be valid for smaller maps since they have their own enumlength; and use this to loop over the cache. If the EnumLength is still invalid, but there is already a cache present that is big enough; we just initialize the EnumLength field for the map. When we apply ClearNonLiveTransitions and descriptor ownership is passed back to a parent map, the descriptor array is trimmed in-place and resorted. At the same time, the enumeration cache is trimmed in-place. Only transition arrays contain descriptor arrays. If we transition to a map and pass ownership of the descriptor array along, the child map will not store the descriptor array it owns. Rather its parent will keep the pointer. So for every leaf-map, we find the descriptor array by following the back pointer, reading out the transition array, and fetching the descriptor array from the JSGlobalPropertyCell. If a map has a transition array, we fetch it from there. If a map has undefined as its back-pointer and has no transition array; it is considered to have an empty descriptor array. When we modify properties, we cannot share the descriptor array. To accommodate this, the child map will get its own transition array; even if there are not necessarily any transitions leaving from the child map. This is necessary since it's the only way to store its own descriptor array. Review URL: https://chromiumcodereview.appspot.com/10909007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-09-12 16:43:57 +00:00
static const int shift = Field::kShift;
static const int mask = Field::kMask >> Field::kShift;
if (shift != 0) {
sar(reg, shift);
}
Sharing of descriptor arrays. This CL adds multiple things: Transition arrays do not directly point at their descriptor array anymore, but rather do so via an indirect pointer (a JSGlobalPropertyCell). An ownership bit is added to maps indicating whether it owns its own descriptor array or not. Maps owning a descriptor array can pass on ownership if a transition from that map is generated; but only if the descriptor array stays exactly the same; or if a descriptor is added. Maps that don't have ownership get ownership back if their direct child to which ownership was passed is cleared in ClearNonLiveTransitions. To detect which descriptors in an array are valid, each map knows its own NumberOfOwnDescriptors. Since the descriptors are sorted in order of addition, if we search and find a descriptor with index bigger than this number, it is not valid for the given map. We currently still build up an enumeration cache (although this may disappear). The enumeration cache is always built for the entire descriptor array, even if not all descriptors are owned by the map. Once a descriptor array has an enumeration cache for a given map; this invariant will always be true, even if the descriptor array was extended. The extended array will inherit the enumeration cache from the smaller descriptor array. If a map with more descriptors needs an enumeration cache, it's EnumLength will still be set to invalid, so it will have to recompute the enumeration cache. This new cache will also be valid for smaller maps since they have their own enumlength; and use this to loop over the cache. If the EnumLength is still invalid, but there is already a cache present that is big enough; we just initialize the EnumLength field for the map. When we apply ClearNonLiveTransitions and descriptor ownership is passed back to a parent map, the descriptor array is trimmed in-place and resorted. At the same time, the enumeration cache is trimmed in-place. Only transition arrays contain descriptor arrays. If we transition to a map and pass ownership of the descriptor array along, the child map will not store the descriptor array it owns. Rather its parent will keep the pointer. So for every leaf-map, we find the descriptor array by following the back pointer, reading out the transition array, and fetching the descriptor array from the JSGlobalPropertyCell. If a map has a transition array, we fetch it from there. If a map has undefined as its back-pointer and has no transition array; it is considered to have an empty descriptor array. When we modify properties, we cannot share the descriptor array. To accommodate this, the child map will get its own transition array; even if there are not necessarily any transitions leaving from the child map. This is necessary since it's the only way to store its own descriptor array. Review URL: https://chromiumcodereview.appspot.com/10909007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-09-12 16:43:57 +00:00
and_(reg, Immediate(mask));
}
// Abort execution if argument is not a smi, enabled via --debug-code.
void AssertSmi(Register object);
// Abort execution if argument is a smi, enabled via --debug-code.
void AssertNotSmi(Register object);
// Abort execution if argument is not a FixedArray, enabled via --debug-code.
void AssertFixedArray(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);
[generators] remove SuspendFlags enum and related code SuspendFlags was originally used by the suspend operation to determine which field to record the bytecode offset of a suspended generator, and the value the generator was resumed with. For async generators, await operations would use a separate field, in order to preserve the previous yield input value. This was important to ensure `function.sent` continued to function correctly. As function.sent is being retired, this allows the removal of support for that. Given that this was the only real need for SuspendFlags in the first place (with other uses tacked on as a hack), this involves several other changes as well: - Modification of MacroAssembler AssertGeneratorObject. No longer accepts a SuspendFlags parameter to determine which type of check to perform. - Removal of `flags` operand from SuspendGenerator bytecode, and the GeneratorStore js-operator. - Removal of `flags` parameter from ResumeGeneratorTrampoline builtins. - Removal of Runtime functions, interpreter intrinsics and AccessBuilders associated with the [[await_input_or_debug_pos]] field in JSAsyncGeneratorObject, as this field no longer exists. - Addition of a new `Yield` AST node (subclass of Suspend) in order to prevent the need for the other SuspendFlag values. BUG=v8:5855 TBR=bmeurer@chromium.org Change-Id: Iff2881e4742497fe5b774915e988c3d9d8fbe487 Reviewed-on: https://chromium-review.googlesource.com/570485 Commit-Queue: Caitlin Potter <caitp@igalia.com> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#46683}
2017-07-14 15:20:23 +00:00
// Abort execution if argument is not a JSGeneratorObject (or subclass),
// enabled via --debug-code.
[generators] remove SuspendFlags enum and related code SuspendFlags was originally used by the suspend operation to determine which field to record the bytecode offset of a suspended generator, and the value the generator was resumed with. For async generators, await operations would use a separate field, in order to preserve the previous yield input value. This was important to ensure `function.sent` continued to function correctly. As function.sent is being retired, this allows the removal of support for that. Given that this was the only real need for SuspendFlags in the first place (with other uses tacked on as a hack), this involves several other changes as well: - Modification of MacroAssembler AssertGeneratorObject. No longer accepts a SuspendFlags parameter to determine which type of check to perform. - Removal of `flags` operand from SuspendGenerator bytecode, and the GeneratorStore js-operator. - Removal of `flags` parameter from ResumeGeneratorTrampoline builtins. - Removal of Runtime functions, interpreter intrinsics and AccessBuilders associated with the [[await_input_or_debug_pos]] field in JSAsyncGeneratorObject, as this field no longer exists. - Addition of a new `Yield` AST node (subclass of Suspend) in order to prevent the need for the other SuspendFlag values. BUG=v8:5855 TBR=bmeurer@chromium.org Change-Id: Iff2881e4742497fe5b774915e988c3d9d8fbe487 Reviewed-on: https://chromium-review.googlesource.com/570485 Commit-Queue: Caitlin Potter <caitp@igalia.com> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#46683}
2017-07-14 15:20:23 +00:00
void AssertGeneratorObject(Register object);
// Abort execution if argument is not undefined or an AllocationSite, enabled
// via --debug-code.
void AssertUndefinedOrAllocationSite(Register object);
// ---------------------------------------------------------------------------
// 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();
// ---------------------------------------------------------------------------
// Runtime calls
// Call a code stub. Generate the code if necessary.
void CallStub(CodeStub* stub);
// Tail call a code stub (jump). Generate the code if necessary.
void TailCallStub(CodeStub* stub);
// 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& ext,
bool builtin_exit_frame = false);
// ---------------------------------------------------------------------------
// Utilities
// Emit code to discard a non-negative number of pointer-sized elements
// from the stack, clobbering only the esp register.
void Drop(int element_count);
void Jump(Handle<Code> target, RelocInfo::Mode rmode) { jmp(target, rmode); }
void Pop(Register dst) { pop(dst); }
void Pop(const Operand& dst) { pop(dst); }
void PushReturnAddressFrom(Register src) { push(src); }
void PopReturnAddressTo(Register dst) { pop(dst); }
// ---------------------------------------------------------------------------
// StatsCounter support
void IncrementCounter(StatsCounter* counter, int value);
void DecrementCounter(StatsCounter* counter, int value);
static int SafepointRegisterStackIndex(Register reg) {
return SafepointRegisterStackIndex(reg.code());
}
void EnterBuiltinFrame(Register context, Register target, Register argc);
void LeaveBuiltinFrame(Register context, Register target, Register argc);
private:
// Helper functions for generating invokes.
void InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual, Label* done,
bool* definitely_mismatches, InvokeFlag flag,
Label::Distance done_distance);
void EnterExitFramePrologue(StackFrame::Type frame_type);
void EnterExitFrameEpilogue(int argc, bool save_doubles);
void LeaveExitFrameEpilogue();
// Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace.
void InNewSpace(Register object, Register scratch, Condition cc,
Label* condition_met,
Label::Distance condition_met_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 ecx 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.
static int SafepointRegisterStackIndex(int reg_code);
// Needs access to SafepointRegisterStackIndex for compiled frame
// traversal.
friend class StandardFrame;
};
// -----------------------------------------------------------------------------
// 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 FixedArrayElementOperand(Register array, Register index_as_smi,
int additional_offset = 0) {
int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize;
return FieldOperand(array, index_as_smi, times_half_pointer_size, offset);
}
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(esi, Context::NATIVE_CONTEXT_INDEX);
}
#define ACCESS_MASM(masm) masm->
} // namespace internal
} // namespace v8
#endif // V8_IA32_MACRO_ASSEMBLER_IA32_H_