#include <climits>
#include <cstdint>
#include "src/sandbox/js-dispatch-table.h"
#if V8_TARGET_ARCH_X64
#include <optional>
#include "src/base/bits.h"
#include "src/base/division-by-constant.h"
#include "src/base/utils/random-number-generator.h"
#include "src/builtins/builtins-inl.h"
#include "src/codegen/callable.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/cpu-features.h"
#include "src/codegen/external-reference-table.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/register-configuration.h"
#include "src/codegen/register.h"
#include "src/codegen/x64/assembler-x64.h"
#include "src/codegen/x64/register-x64.h"
#include "src/common/globals.h"
#include "src/debug/debug.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/frames-inl.h"
#include "src/heap/mutable-page-metadata.h"
#include "src/init/bootstrapper.h"
#include "src/logging/counters.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/smi.h"
#include "src/sandbox/external-pointer.h"
#include "src/snapshot/snapshot.h"
#if 0
#include "src/codegen/x64/macro-assembler-x64.h"
#endif
#define __ ACCESS_MASM(masm)
namespace v8 {
namespace internal {
Operand StackArgumentsAccessor::GetArgumentOperand(int index) const {
DCHECK_GE(index, 0);
return Operand(rsp, kPCOnStackSize + index * kSystemPointerSize);
}
void MacroAssembler::CodeEntry() {
endbr64();
}
void MacroAssembler::ExceptionHandler() {
CodeEntry();
AssertInSandboxedExecutionMode();
if (sandboxing_mode() == CodeSandboxingMode::kUnsandboxed) {
ExitSandbox();
}
}
void MacroAssembler::Load(Register destination, ExternalReference source) {
if (root_array_available_ && options().enable_root_relative_access) {
intptr_t delta = RootRegisterOffsetForExternalReference(isolate(), source);
if (is_int32(delta)) {
movq(destination, Operand(kRootRegister, static_cast<int32_t>(delta)));
return;
}
}
if (destination == rax && !options().isolate_independent_code) {
load_rax(source);
} else {
movq(destination, ExternalReferenceAsOperand(source));
}
}
void MacroAssembler::Store(ExternalReference destination, Register source) {
if (root_array_available_ && options().enable_root_relative_access) {
intptr_t delta =
RootRegisterOffsetForExternalReference(isolate(), destination);
if (is_int32(delta)) {
movq(Operand(kRootRegister, static_cast<int32_t>(delta)), source);
return;
}
}
if (source == rax && !options().isolate_independent_code) {
store_rax(destination);
} else {
movq(ExternalReferenceAsOperand(destination), source);
}
}
void MacroAssembler::LoadFromConstantsTable(Register destination,
int constant_index) {
DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable));
LoadRoot(destination, RootIndex::kBuiltinsConstantsTable);
LoadTaggedField(
destination,
FieldOperand(destination, FixedArray::OffsetOfElementAt(constant_index)));
}
void MacroAssembler::LoadRootRegisterOffset(Register destination,
intptr_t offset) {
DCHECK(is_int32(offset));
if (offset == 0) {
Move(destination, kRootRegister);
} else {
leaq(destination, Operand(kRootRegister, static_cast<int32_t>(offset)));
}
}
void MacroAssembler::LoadRootRelative(Register destination, int32_t offset) {
movq(destination, Operand(kRootRegister, offset));
}
void MacroAssembler::StoreRootRelative(int32_t offset, Register value) {
movq(Operand(kRootRegister, offset), value);
}
void MacroAssembler::LoadAddress(Register destination,
ExternalReference source) {
if (root_array_available()) {
if (source.IsIsolateFieldId()) {
leaq(destination,
Operand(kRootRegister, source.offset_from_root_register()));
return;
}
if (options().enable_root_relative_access) {
intptr_t delta =
RootRegisterOffsetForExternalReference(isolate(), source);
if (is_int32(delta)) {
leaq(destination, Operand(kRootRegister, static_cast<int32_t>(delta)));
return;
}
} else if (options().isolate_independent_code) {
IndirectLoadExternalReference(destination, source);
return;
}
}
Move(destination, source);
}
Operand MacroAssembler::ExternalReferenceAsOperand(ExternalReference reference,
Register scratch) {
if (root_array_available()) {
if (reference.IsIsolateFieldId()) {
return Operand(kRootRegister, reference.offset_from_root_register());
}
if (options().enable_root_relative_access) {
int64_t delta =
RootRegisterOffsetForExternalReference(isolate(), reference);
if (is_int32(delta)) {
return Operand(kRootRegister, static_cast<int32_t>(delta));
}
}
if (options().isolate_independent_code) {
if (IsAddressableThroughRootRegister(isolate(), reference)) {
intptr_t offset =
RootRegisterOffsetForExternalReference(isolate(), reference);
CHECK(is_int32(offset));
return Operand(kRootRegister, static_cast<int32_t>(offset));
} else {
movq(scratch, Operand(kRootRegister,
RootRegisterOffsetForExternalReferenceTableEntry(
isolate(), reference)));
return Operand(scratch, 0);
}
}
}
Move(scratch, reference);
return Operand(scratch, 0);
}
void MacroAssembler::PushAddress(ExternalReference source) {
LoadAddress(kScratchRegister, source);
Push(kScratchRegister);
}
Operand MacroAssembler::RootAsOperand(RootIndex index) {
DCHECK(root_array_available());
return Operand(kRootRegister, RootRegisterOffsetForRootIndex(index));
}
void MacroAssembler::LoadTaggedRoot(Register destination, RootIndex index) {
static_assert(!CanBeImmediate(RootIndex::kUndefinedValue) ||
std::is_same_v<Tagged_t, uint32_t>);
if (CanBeImmediate(index)) {
mov_tagged(destination,
Immediate(static_cast<uint32_t>(ReadOnlyRootPtr(index))));
return;
}
DCHECK(root_array_available_);
movq(destination, RootAsOperand(index));
}
void MacroAssembler::LoadRoot(Register destination, RootIndex index) {
if (CanBeImmediate(index)) {
DecompressTagged(destination,
static_cast<uint32_t>(ReadOnlyRootPtr(index)));
return;
}
DCHECK(root_array_available_);
movq(destination, RootAsOperand(index));
}
void MacroAssembler::PushRoot(RootIndex index) {
DCHECK(root_array_available_);
Push(RootAsOperand(index));
}
void MacroAssembler::CompareRoot(Register with, RootIndex index,
ComparisonMode mode) {
if (mode == ComparisonMode::kFullPointer ||
!base::IsInRange(index, RootIndex::kFirstStrongOrReadOnlyRoot,
RootIndex::kLastStrongOrReadOnlyRoot)) {
cmpq(with, RootAsOperand(index));
return;
}
CompareTaggedRoot(with, index);
}
void MacroAssembler::CompareTaggedRoot(Register with, RootIndex index) {
AssertSmiOrHeapObjectInMainCompressionCage(with);
if (CanBeImmediate(index)) {
cmp_tagged(with, Immediate(static_cast<uint32_t>(ReadOnlyRootPtr(index))));
return;
}
DCHECK(root_array_available_);
DCHECK(base::IsInRange(index, RootIndex::kFirstStrongOrReadOnlyRoot,
RootIndex::kLastStrongOrReadOnlyRoot));
cmp_tagged(with, RootAsOperand(index));
}
void MacroAssembler::CompareRoot(Operand with, RootIndex index) {
if (CanBeImmediate(index)) {
cmp_tagged(with, Immediate(static_cast<uint32_t>(ReadOnlyRootPtr(index))));
return;
}
DCHECK(root_array_available_);
DCHECK(!with.AddressUsesRegister(kScratchRegister));
if (base::IsInRange(index, RootIndex::kFirstStrongOrReadOnlyRoot,
RootIndex::kLastStrongOrReadOnlyRoot)) {
mov_tagged(kScratchRegister, RootAsOperand(index));
cmp_tagged(with, kScratchRegister);
} else {
movq(kScratchRegister, RootAsOperand(index));
cmpq(with, kScratchRegister);
}
}
void MacroAssembler::LoadCompressedMap(Register destination, Register object) {
CHECK(COMPRESS_POINTERS_BOOL);
mov_tagged(destination, FieldOperand(object, HeapObject::kMapOffset));
}
void MacroAssembler::LoadMap(Register destination, Register object) {
LoadTaggedField(destination, FieldOperand(object, HeapObject::kMapOffset));
#ifdef V8_MAP_PACKING
UnpackMapWord(destination);
#endif
}
void MacroAssembler::LoadFeedbackVector(Register dst, Register closure,
Label* fbv_undef,
Label::Distance distance) {
Label done;
TaggedRegister feedback_cell(dst);
LoadTaggedField(feedback_cell,
FieldOperand(closure, JSFunction::kFeedbackCellOffset));
LoadTaggedField(dst, FieldOperand(feedback_cell, FeedbackCell::kValueOffset));
IsObjectType(dst, FEEDBACK_VECTOR_TYPE, rcx);
j(equal, &done, Label::kNear);
LoadRoot(dst, RootIndex::kUndefinedValue);
jmp(fbv_undef, distance);
bind(&done);
}
void MacroAssembler::LoadInterpreterDataBytecodeArray(
Register destination, Register interpreter_data) {
LoadProtectedPointerField(
destination, FieldOperand(interpreter_data,
offsetof(InterpreterData, bytecode_array_)));
}
void MacroAssembler::LoadInterpreterDataInterpreterTrampoline(
Register destination, Register interpreter_data) {
LoadProtectedPointerField(
destination,
FieldOperand(interpreter_data,
offsetof(InterpreterData, interpreter_trampoline_)));
}
void MacroAssembler::LoadTaggedField(Register destination,
Operand field_operand) {
if (COMPRESS_POINTERS_BOOL) {
DecompressTagged(destination, field_operand);
} else {
mov_tagged(destination, field_operand);
}
}
void MacroAssembler::LoadTaggedField(TaggedRegister destination,
Operand field_operand) {
LoadTaggedFieldWithoutDecompressing(destination.reg(), field_operand);
}
void MacroAssembler::LoadTaggedFieldWithoutDecompressing(
Register destination, Operand field_operand) {
mov_tagged(destination, field_operand);
}
#ifdef V8_MAP_PACKING
void MacroAssembler::UnpackMapWord(Register r) {
shlq(r, Immediate(16));
shrq(r, Immediate(16));
xorq(r, Immediate(Internals::kMapWordXorMask));
}
#endif
void MacroAssembler::LoadTaggedSignedField(Register destination,
Operand field_operand) {
if (COMPRESS_POINTERS_BOOL) {
DecompressTaggedSigned(destination, field_operand);
} else {
mov_tagged(destination, field_operand);
}
}
void MacroAssembler::PushTaggedField(Operand field_operand, Register scratch) {
if (COMPRESS_POINTERS_BOOL) {
DCHECK(!field_operand.AddressUsesRegister(scratch));
DecompressTagged(scratch, field_operand);
Push(scratch);
} else {
Push(field_operand);
}
}
void MacroAssembler::SmiUntagField(Register dst, Operand src) {
SmiUntag(dst, src);
}
void MacroAssembler::SmiUntagFieldUnsigned(Register dst, Operand src) {
SmiUntagUnsigned(dst, src);
}
void MacroAssembler::StoreTaggedField(Operand dst_field_operand,
Immediate value) {
if (COMPRESS_POINTERS_BOOL) {
movl(dst_field_operand, value);
} else {
movq(dst_field_operand, value);
}
}
void MacroAssembler::StoreTaggedField(Operand dst_field_operand,
Register value) {
if (COMPRESS_POINTERS_BOOL) {
movl(dst_field_operand, value);
} else {
movq(dst_field_operand, value);
}
}
void MacroAssembler::StoreTaggedSignedField(Operand dst_field_operand,
Tagged<Smi> value) {
if (SmiValuesAre32Bits()) {
Move(kScratchRegister, value);
movq(dst_field_operand, kScratchRegister);
} else {
StoreTaggedField(dst_field_operand, Immediate(value));
}
}
void MacroAssembler::AtomicStoreTaggedField(Operand dst_field_operand,
Register value) {
if (COMPRESS_POINTERS_BOOL) {
movl(kScratchRegister, value);
xchgl(kScratchRegister, dst_field_operand);
} else {
movq(kScratchRegister, value);
xchgq(kScratchRegister, dst_field_operand);
}
}
void MacroAssembler::DecompressTaggedSigned(Register destination,
Operand field_operand) {
ASM_CODE_COMMENT(this);
movl(destination, field_operand);
}
void MacroAssembler::DecompressTagged(Register destination,
Operand field_operand) {
ASM_CODE_COMMENT(this);
movl(destination, field_operand);
addq(destination, kPtrComprCageBaseRegister);
}
void MacroAssembler::DecompressTagged(Register destination, Register source) {
ASM_CODE_COMMENT(this);
movl(destination, source);
addq(destination, kPtrComprCageBaseRegister);
}
void MacroAssembler::DecompressTagged(Register destination,
Tagged_t immediate) {
ASM_CODE_COMMENT(this);
leaq(destination,
Operand(kPtrComprCageBaseRegister, static_cast<int32_t>(immediate)));
}
void MacroAssembler::DecompressProtected(Register destination,
Operand field_operand) {
#if V8_ENABLE_SANDBOX
ASM_CODE_COMMENT(this);
movl(destination, field_operand);
DCHECK(root_array_available_);
orq(destination,
Operand{kRootRegister, IsolateData::trusted_cage_base_offset()});
#else
UNREACHABLE();
#endif
}
void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register slot_address,
SaveFPRegsMode save_fp,
SmiCheck smi_check,
ReadOnlyCheck ro_check,
SlotDescriptor slot) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, value, slot_address));
Label done;
if (ro_check == ReadOnlyCheck::kInline) {
MaybeJumpIfReadOnlyOrSmallSmi(value, &done);
}
if (smi_check == SmiCheck::kInline) {
JumpIfSmi(value, &done);
}
DCHECK(IsAligned(offset, kTaggedSize));
leaq(slot_address, FieldOperand(object, offset));
if (v8_flags.slow_debug_code) {
ASM_CODE_COMMENT_STRING(this, "Debug check slot_address");
Label ok;
testb(slot_address, Immediate(kTaggedSize - 1));
j(zero, &ok, Label::kNear);
int3();
bind(&ok);
}
RecordWrite(object, slot_address, value, save_fp, SmiCheck::kOmit,
ReadOnlyCheck::kOmit, slot);
bind(&done);
if (v8_flags.slow_debug_code) {
ASM_CODE_COMMENT_STRING(this, "Zap scratch registers");
Move(value, kZapValue, RelocInfo::NO_INFO);
Move(slot_address, kZapValue, RelocInfo::NO_INFO);
}
}
void MacroAssembler::EnterSandbox() {
#ifdef V8_ENABLE_SANDBOX_HARDWARE_SUPPORT
pushq(rax);
pushq(rbx);
pushq(rcx);
pushq(rdx);
xorq(rcx, rcx);
xorq(rdx, rdx);
LoadAddress(rbx, ExternalReference::sandboxed_mode_pkey_mask_address());
movl(rbx, Operand(rbx, 0));
if (v8_flags.debug_code) {
HardAbortScope hard_abort(this);
rdpkru();
andl(rax, rbx);
Assert(zero, AbortReason::kUnexpectedSandboxMode);
}
rdpkru();
orl(rax, rbx);
wrpkru();
popq(rdx);
popq(rcx);
popq(rbx);
popq(rax);
#endif
}
void MacroAssembler::ExitSandbox() {
#ifdef V8_ENABLE_SANDBOX_HARDWARE_SUPPORT
pushq(rax);
pushq(rbx);
pushq(rcx);
pushq(rdx);
xorq(rcx, rcx);
xorq(rdx, rdx);
LoadAddress(rbx, ExternalReference::sandboxed_mode_pkey_mask_address());
movl(rbx, Operand(rbx, 0));
if (v8_flags.debug_code) {
HardAbortScope hard_abort(this);
Label hardware_support_not_active;
testl(rbx, rbx);
j(zero, &hardware_support_not_active);
rdpkru();
andl(rax, rbx);
Assert(not_zero, AbortReason::kUnexpectedSandboxMode);
bind(&hardware_support_not_active);
}
rdpkru();
notl(rbx);
andl(rax, rbx);
wrpkru();
popq(rdx);
popq(rcx);
popq(rbx);
popq(rax);
#endif
}
void MacroAssembler::AssertInSandboxedExecutionMode() {
#ifdef V8_ENABLE_SANDBOX_HARDWARE_SUPPORT
if (v8_flags.debug_code) {
HardAbortScope hard_abort(this);
pushq(rax);
pushq(rbx);
pushq(rcx);
pushq(rdx);
xorq(rcx, rcx);
xorq(rdx, rdx);
LoadAddress(rbx, ExternalReference::sandboxed_mode_pkey_mask_address());
movl(rbx, Operand(rbx, 0));
Label hardware_support_not_active;
testl(rbx, rbx);
j(zero, &hardware_support_not_active);
rdpkru();
andl(rax, rbx);
Assert(not_zero, AbortReason::kUnexpectedSandboxMode);
bind(&hardware_support_not_active);
popq(rdx);
popq(rcx);
popq(rbx);
popq(rax);
}
#endif
}
void MacroAssembler::SwitchSandboxingModeTo(CodeSandboxingMode mode) {
switch (mode) {
case CodeSandboxingMode::kSandboxed:
return EnterSandbox();
case CodeSandboxingMode::kUnsandboxed:
return ExitSandbox();
}
}
CodeSandboxingMode MacroAssembler::SwitchSandboxingModeBeforeCallIfNeeded(
CodeSandboxingMode target_sandboxing_mode) {
CodeSandboxingMode previous_sandboxing_mode = sandboxing_mode();
if (sandboxing_mode() != target_sandboxing_mode) {
SwitchSandboxingModeTo(target_sandboxing_mode);
sandboxing_mode_ = target_sandboxing_mode;
}
return previous_sandboxing_mode;
}
void MacroAssembler::SwitchSandboxingModeAfterCallIfNeeded(
CodeSandboxingMode previous_sandboxing_mode) {
if (sandboxing_mode() != previous_sandboxing_mode) {
SwitchSandboxingModeTo(previous_sandboxing_mode);
sandboxing_mode_ = previous_sandboxing_mode;
}
}
void MacroAssembler::EncodeSandboxedPointer(Register value) {
ASM_CODE_COMMENT(this);
#ifdef V8_ENABLE_SANDBOX
subq(value, kPtrComprCageBaseRegister);
shlq(value, Immediate(kSandboxedPointerShift));
#else
UNREACHABLE();
#endif
}
void MacroAssembler::DecodeSandboxedPointer(Register value) {
ASM_CODE_COMMENT(this);
#ifdef V8_ENABLE_SANDBOX
shrq(value, Immediate(kSandboxedPointerShift));
addq(value, kPtrComprCageBaseRegister);
#else
UNREACHABLE();
#endif
}
void MacroAssembler::LoadSandboxedPointerField(Register destination,
Operand field_operand) {
ASM_CODE_COMMENT(this);
movq(destination, field_operand);
DecodeSandboxedPointer(destination);
}
void MacroAssembler::StoreSandboxedPointerField(Operand dst_field_operand,
Register value) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(value, kScratchRegister));
DCHECK(!dst_field_operand.AddressUsesRegister(kScratchRegister));
movq(kScratchRegister, value);
EncodeSandboxedPointer(kScratchRegister);
movq(dst_field_operand, kScratchRegister);
}
void MacroAssembler::LoadExternalPointerField(
Register destination, Operand field_operand,
ExternalPointerTagRange tag_range, Register scratch,
IsolateRootLocation isolateRootLocation) {
DCHECK(!AreAliased(destination, scratch));
#ifdef V8_ENABLE_SANDBOX
DCHECK(!tag_range.IsEmpty());
DCHECK(!IsSharedExternalPointerType(tag_range));
DCHECK(!field_operand.AddressUsesRegister(scratch));
if (isolateRootLocation == IsolateRootLocation::kInRootRegister) {
DCHECK(root_array_available_);
movq(scratch,
Operand(kRootRegister,
IsolateData::external_pointer_table_offset() +
Internals::kExternalPointerTableBasePointerOffset));
} else {
DCHECK(isolateRootLocation == IsolateRootLocation::kInScratchRegister);
movq(scratch,
Operand(scratch,
IsolateData::external_pointer_table_offset() +
Internals::kExternalPointerTableBasePointerOffset));
}
movl(destination, field_operand);
shrq(destination, Immediate(kExternalPointerIndexShift));
static_assert(kExternalPointerTableEntrySize == 8);
movq(destination, Operand(scratch, destination, times_8, 0));
DCHECK(!ExternalPointerCanBeEmpty(tag_range));
if (tag_range.Size() == 1) {
movq(scratch, destination);
shrq(scratch, Immediate(kExternalPointerTagShift));
andl(scratch, Immediate(kExternalPointerShiftedTagMask));
cmpl(scratch, Immediate(tag_range.first));
SbxCheck(equal, AbortReason::kExternalPointerTagMismatch);
movq(scratch, Immediate64(kExternalPointerPayloadMask));
andq(destination, scratch);
} else {
DCHECK_NE(tag_range, kAnyExternalPointerTagRange);
UNREACHABLE();
}
#else
movq(destination, field_operand);
#endif
}
void MacroAssembler::LoadTrustedPointerField(Register destination,
Operand field_operand,
IndirectPointerTag tag,
Register scratch) {
#ifdef V8_ENABLE_SANDBOX
LoadIndirectPointerField(destination, field_operand, tag, scratch);
#else
LoadTaggedField(destination, field_operand);
#endif
}
void MacroAssembler::LoadTrustedUnknownPointerField(
Register destination, Operand field_operand, Register scratch,
const std::initializer_list<
std::tuple<InstanceType, Label*, Label::Distance>>& cases) {
DCHECK(!AreAliased(destination, scratch));
Label done;
#ifdef V8_ENABLE_SANDBOX
{
Register handle = scratch;
movl(handle, field_operand);
bool handles_code_case = false;
for (auto& [type, label, distance] : cases) {
if (type == CODE_TYPE) {
handles_code_case = true;
Label not_code_handle;
testl(handle, Immediate(kCodePointerHandleMarker));
j(zero, ¬_code_handle, Label::kNear);
ResolveCodePointerHandle(destination, handle);
jmp(label, distance);
bind(¬_code_handle);
break;
}
}
if (!handles_code_case) {
testl(handle, Immediate(kCodePointerHandleMarker));
j(not_zero, &done, Label::kNear);
}
ResolveTrustedPointerHandle(destination, handle,
kUnknownIndirectPointerTag);
}
#else
LoadTaggedField(destination, field_operand);
#endif
#if V8_STATIC_ROOTS_BOOL
LoadCompressedMap(scratch, destination);
for (auto& [type, label, distance] : cases) {
if (V8_ENABLE_SANDBOX_BOOL && type == CODE_TYPE) {
continue;
}
CompareInstanceTypeWithUniqueCompressedMap(scratch, type);
j(equal, label, distance);
}
#else
LoadMap(scratch, destination);
for (auto& [type, label, distance] : cases) {
if (V8_ENABLE_SANDBOX_BOOL && type == CODE_TYPE) {
continue;
}
CmpInstanceType(scratch, type);
j(equal, label, distance);
}
#endif
bind(&done);
xorq(destination, destination);
}
void MacroAssembler::StoreTrustedPointerField(Operand dst_field_operand,
Register value) {
#ifdef V8_ENABLE_SANDBOX
StoreIndirectPointerField(dst_field_operand, value);
#else
StoreTaggedField(dst_field_operand, value);
#endif
}
void MacroAssembler::LoadIndirectPointerField(Register destination,
Operand field_operand,
IndirectPointerTag tag,
Register scratch) {
#ifdef V8_ENABLE_SANDBOX
DCHECK(!AreAliased(destination, scratch));
Register handle = scratch;
movl(handle, field_operand);
ResolveIndirectPointerHandle(destination, handle, tag);
#else
UNREACHABLE();
#endif
}
void MacroAssembler::StoreIndirectPointerField(Operand dst_field_operand,
Register value) {
#ifdef V8_ENABLE_SANDBOX
movl(kScratchRegister,
FieldOperand(value, ExposedTrustedObject::kSelfIndirectPointerOffset));
movl(dst_field_operand, kScratchRegister);
#else
UNREACHABLE();
#endif
}
#ifdef V8_ENABLE_SANDBOX
void MacroAssembler::ResolveIndirectPointerHandle(Register destination,
Register handle,
IndirectPointerTag tag) {
CHECK_NE(tag, kUnknownIndirectPointerTag);
if (tag == kCodeIndirectPointerTag) {
ResolveCodePointerHandle(destination, handle);
} else {
ResolveTrustedPointerHandle(destination, handle, tag);
}
}
void MacroAssembler::ResolveTrustedPointerHandle(Register destination,
Register handle,
IndirectPointerTag tag) {
DCHECK_NE(tag, kCodeIndirectPointerTag);
DCHECK(!AreAliased(handle, destination));
shrl(handle, Immediate(kTrustedPointerHandleShift));
static_assert(kTrustedPointerTableEntrySize == 8);
DCHECK(root_array_available_);
movq(destination,
Operand{kRootRegister, IsolateData::trusted_pointer_table_offset()});
movq(destination, Operand{destination, handle, times_8, 0});
Register tag_reg = handle;
movq(tag_reg, Immediate64(~(tag | kTrustedPointerTableMarkBit)));
andq(destination, tag_reg);
}
void MacroAssembler::ResolveCodePointerHandle(Register destination,
Register handle) {
DCHECK(!AreAliased(handle, destination));
Register table = destination;
LoadCodePointerTableBase(table);
shrl(handle, Immediate(kCodePointerHandleShift));
shll(handle, Immediate(kCodePointerTableEntrySizeLog2));
movq(destination,
Operand(table, handle, times_1, kCodePointerTableEntryCodeObjectOffset));
orq(destination, Immediate(kHeapObjectTag));
}
void MacroAssembler::LoadCodeEntrypointViaCodePointer(Register destination,
Operand field_operand,
CodeEntrypointTag tag) {
DCHECK(!AreAliased(destination, kScratchRegister));
DCHECK(!field_operand.AddressUsesRegister(kScratchRegister));
DCHECK_NE(tag, kInvalidEntrypointTag);
LoadCodePointerTableBase(kScratchRegister);
movl(destination, field_operand);
shrl(destination, Immediate(kCodePointerHandleShift));
shll(destination, Immediate(kCodePointerTableEntrySizeLog2));
movq(destination, Operand(kScratchRegister, destination, times_1, 0));
if (tag != 0) {
movq(kScratchRegister, Immediate64(tag));
xorq(destination, kScratchRegister);
}
}
void MacroAssembler::LoadCodePointerTableBase(Register destination) {
#ifdef V8_COMPRESS_POINTERS_IN_MULTIPLE_CAGES
if (!options().isolate_independent_code && isolate()) {
LoadAddress(destination,
ExternalReference::code_pointer_table_base_address(isolate()));
} else {
Load(destination,
ExternalReference::address_of_code_pointer_table_base_address());
}
#else
LoadAddress(destination,
ExternalReference::global_code_pointer_table_base_address());
#endif
}
#endif
void MacroAssembler::LoadEntrypointFromJSDispatchTable(
Register destination, Register dispatch_handle) {
DCHECK(!AreAliased(destination, dispatch_handle, kScratchRegister));
CHECK(root_array_available());
movq(kScratchRegister,
ExternalReferenceAsOperand(IsolateFieldId::kJSDispatchTable));
movq(destination, dispatch_handle);
shrl(destination, Immediate(kJSDispatchHandleShift));
shll(destination, Immediate(kJSDispatchTableEntrySizeLog2));
movq(destination, Operand(kScratchRegister, destination, times_1,
JSDispatchEntry::kEntrypointOffset));
}
void MacroAssembler::LoadParameterCountFromJSDispatchTable(
Register destination, Register dispatch_handle) {
DCHECK(!AreAliased(destination, dispatch_handle, kScratchRegister));
CHECK(root_array_available());
movq(kScratchRegister,
ExternalReferenceAsOperand(IsolateFieldId::kJSDispatchTable));
movq(destination, dispatch_handle);
shrl(destination, Immediate(kJSDispatchHandleShift));
shll(destination, Immediate(kJSDispatchTableEntrySizeLog2));
static_assert(JSDispatchEntry::kParameterCountMask == 0xffff);
movzxwq(destination, Operand(kScratchRegister, destination, times_1,
JSDispatchEntry::kCodeObjectOffset));
}
void MacroAssembler::LoadEntrypointAndParameterCountFromJSDispatchTable(
Register entrypoint, Register parameter_count, Register dispatch_handle) {
DCHECK(!AreAliased(entrypoint, parameter_count, dispatch_handle,
kScratchRegister));
CHECK(root_array_available());
movq(kScratchRegister,
ExternalReferenceAsOperand(IsolateFieldId::kJSDispatchTable));
Register offset = parameter_count;
movq(offset, dispatch_handle);
shrl(offset, Immediate(kJSDispatchHandleShift));
shll(offset, Immediate(kJSDispatchTableEntrySizeLog2));
movq(entrypoint, Operand(kScratchRegister, offset, times_1,
JSDispatchEntry::kEntrypointOffset));
static_assert(JSDispatchEntry::kParameterCountMask == 0xffff);
movzxwq(parameter_count, Operand(kScratchRegister, offset, times_1,
JSDispatchEntry::kCodeObjectOffset));
}
void MacroAssembler::LoadProtectedPointerField(Register destination,
Operand field_operand) {
DCHECK(root_array_available());
#ifdef V8_ENABLE_SANDBOX
DecompressProtected(destination, field_operand);
#else
LoadTaggedField(destination, field_operand);
#endif
}
void MacroAssembler::CallEphemeronKeyBarrier(Register object,
Register slot_address,
SaveFPRegsMode fp_mode) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, slot_address));
RegList registers =
WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address);
PushAll(registers);
Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
Register slot_address_parameter =
WriteBarrierDescriptor::SlotAddressRegister();
MovePair(slot_address_parameter, slot_address, object_parameter, object);
CallBuiltin(Builtins::EphemeronKeyBarrier(fp_mode));
PopAll(registers);
}
void MacroAssembler::CallIndirectPointerBarrier(Register object,
Register slot_address,
SaveFPRegsMode fp_mode,
IndirectPointerTag tag) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, slot_address));
RegList registers =
IndirectPointerWriteBarrierDescriptor::ComputeSavedRegisters(
object, slot_address);
PushAll(registers);
Register object_parameter =
IndirectPointerWriteBarrierDescriptor::ObjectRegister();
Register slot_address_parameter =
IndirectPointerWriteBarrierDescriptor::SlotAddressRegister();
MovePair(slot_address_parameter, slot_address, object_parameter, object);
Register tag_parameter =
IndirectPointerWriteBarrierDescriptor::IndirectPointerTagRegister();
Move(tag_parameter, tag);
CallBuiltin(Builtins::IndirectPointerBarrier(fp_mode));
PopAll(registers);
}
void MacroAssembler::CallRecordWriteStubSaveRegisters(Register object,
Register slot_address,
SaveFPRegsMode fp_mode,
StubCallMode mode) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, slot_address));
RegList registers =
WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address);
PushAll(registers);
Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
Register slot_address_parameter =
WriteBarrierDescriptor::SlotAddressRegister();
MovePair(object_parameter, object, slot_address_parameter, slot_address);
CallRecordWriteStub(object_parameter, slot_address_parameter, fp_mode, mode);
PopAll(registers);
}
void MacroAssembler::CallRecordWriteStub(Register object, Register slot_address,
SaveFPRegsMode fp_mode,
StubCallMode mode) {
ASM_CODE_COMMENT(this);
DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
#if V8_ENABLE_WEBASSEMBLY
if (mode == StubCallMode::kCallWasmRuntimeStub) {
Builtin wasm_target = wasm::WasmCode::GetRecordWriteBuiltin(fp_mode);
CodeSandboxingMode previous_mode = SwitchSandboxingModeBeforeCallIfNeeded(
Builtins::SandboxingModeOf(wasm_target));
near_call(static_cast<intptr_t>(wasm_target), RelocInfo::WASM_STUB_CALL);
SwitchSandboxingModeAfterCallIfNeeded(previous_mode);
#else
if (false) {
#endif
} else {
CallBuiltin(Builtins::RecordWrite(fp_mode));
}
}
void MacroAssembler::CallVerifySkippedWriteBarrierStubSaveRegisters(
Register object, Register value, SaveFPRegsMode fp_mode) {
ASM_CODE_COMMENT(this);
PushCallerSaved(fp_mode);
CallVerifySkippedWriteBarrierStub(object, value);
PopCallerSaved(fp_mode);
}
void MacroAssembler::CallVerifySkippedWriteBarrierStub(Register object,
Register value) {
ASM_CODE_COMMENT(this);
MovePair(kCArgRegs[0], object, kCArgRegs[1], value);
PrepareCallCFunction(2);
CallCFunction(ExternalReference::verify_skipped_write_barrier(), 2);
}
void MacroAssembler::CallVerifySkippedIndirectWriteBarrierStubSaveRegisters(
Register object, Register value, SaveFPRegsMode fp_mode) {
ASM_CODE_COMMENT(this);
PushCallerSaved(fp_mode);
CallVerifySkippedIndirectWriteBarrierStub(object, value);
PopCallerSaved(fp_mode);
}
void MacroAssembler::CallVerifySkippedIndirectWriteBarrierStub(Register object,
Register value) {
ASM_CODE_COMMENT(this);
MovePair(kCArgRegs[0], object, kCArgRegs[1], value);
PrepareCallCFunction(2);
CallCFunction(ExternalReference::verify_skipped_indirect_write_barrier(), 2);
}
#ifdef V8_IS_TSAN
void MacroAssembler::CallTSANStoreStub(Register address, Register value,
SaveFPRegsMode fp_mode, int size,
StubCallMode mode,
std::memory_order order) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(address, value));
TSANStoreDescriptor descriptor;
RegList registers = descriptor.allocatable_registers();
PushAll(registers);
Register address_parameter(
descriptor.GetRegisterParameter(TSANStoreDescriptor::kAddress));
Register value_parameter(
descriptor.GetRegisterParameter(TSANStoreDescriptor::kValue));
MovePair(address_parameter, address, value_parameter, value);
#if V8_ENABLE_WEBASSEMBLY
if (mode != StubCallMode::kCallWasmRuntimeStub) {
CallBuiltin(CodeFactory::GetTSANStoreStub(fp_mode, size, order));
} else {
auto wasm_target = static_cast<intptr_t>(
wasm::WasmCode::GetTSANStoreBuiltin(fp_mode, size, order));
near_call(wasm_target, RelocInfo::WASM_STUB_CALL);
}
#else
CallBuiltin(CodeFactory::GetTSANStoreStub(fp_mode, size, order));
#endif
PopAll(registers);
}
void MacroAssembler::CallTSANRelaxedLoadStub(Register address,
SaveFPRegsMode fp_mode, int size,
StubCallMode mode) {
TSANLoadDescriptor descriptor;
RegList registers = descriptor.allocatable_registers();
PushAll(registers);
Register address_parameter(
descriptor.GetRegisterParameter(TSANLoadDescriptor::kAddress));
Move(address_parameter, address);
#if V8_ENABLE_WEBASSEMBLY
if (mode != StubCallMode::kCallWasmRuntimeStub) {
CallBuiltin(CodeFactory::GetTSANRelaxedLoadStub(fp_mode, size));
} else {
auto wasm_target = static_cast<intptr_t>(
wasm::WasmCode::GetTSANRelaxedLoadBuiltin(fp_mode, size));
near_call(wasm_target, RelocInfo::WASM_STUB_CALL);
}
#else
CallBuiltin(CodeFactory::GetTSANRelaxedLoadStub(fp_mode, size));
#endif
PopAll(registers);
}
#endif
void MacroAssembler::MaybeJumpIfReadOnlyOrSmallSmi(Register value,
Label* dest) {
#if V8_STATIC_ROOTS_BOOL
constexpr int kLastStaticRootPage =
RoundUp<kRegularPageSize>(StaticReadOnlyRoot::kLastAllocatedRoot);
static_assert(kLastStaticRootPage <=
V8_CONTIGUOUS_COMPRESSED_RO_SPACE_SIZE_MB * MB);
JumpIfUnsignedLessThan(value, kLastStaticRootPage, dest);
#endif
}
void MacroAssembler::RecordWrite(Register object, Register slot_address,
Register value, SaveFPRegsMode fp_mode,
SmiCheck smi_check, ReadOnlyCheck ro_check,
SlotDescriptor slot) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, slot_address, value));
AssertNotSmi(object);
if (v8_flags.disable_write_barriers) {
return;
}
if (v8_flags.slow_debug_code) {
ASM_CODE_COMMENT_STRING(this, "Debug check slot_address");
Label ok;
if (slot.contains_indirect_pointer()) {
Push(object);
Register scratch = object;
Push(slot_address);
Register value_in_slot = slot_address;
LoadIndirectPointerField(value_in_slot, Operand(slot_address, 0),
slot.indirect_pointer_tag(), scratch);
cmp_tagged(value, value_in_slot);
Pop(slot_address);
Pop(object);
} else {
cmp_tagged(value, Operand(slot_address, 0));
}
j(equal, &ok, Label::kNear);
int3();
bind(&ok);
}
Label done;
if (ro_check == ReadOnlyCheck::kInline) {
MaybeJumpIfReadOnlyOrSmallSmi(value, &done);
}
if (smi_check == SmiCheck::kInline) {
JumpIfSmi(value, &done);
}
if (slot.contains_indirect_pointer()) {
JumpIfNotMarking(&done);
} else {
#if V8_ENABLE_STICKY_MARK_BITS_BOOL
DCHECK(!AreAliased(kScratchRegister, object, slot_address, value));
Label stub_call;
JumpIfMarking(&stub_call);
movq(kScratchDoubleReg, slot_address);
Register scratch0 = slot_address;
CheckMarkBit(object, kScratchRegister, scratch0, carry, &done);
CheckPageFlag(value, kScratchRegister, MemoryChunk::kIsInReadOnlyHeapMask,
not_zero, &done, Label::kFar);
CheckMarkBit(value, kScratchRegister, scratch0, carry, &done);
movq(slot_address, kScratchDoubleReg);
bind(&stub_call);
#else
CheckPageFlag(value,
value,
MemoryChunk::kPointersToHereAreInterestingMask, zero, &done,
Label::kFar);
CheckPageFlag(object,
value,
MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done,
Label::kFar);
#endif
}
if (slot.contains_direct_pointer()) {
CallRecordWriteStub(object, slot_address, fp_mode,
StubCallMode::kCallBuiltinPointer);
} else {
DCHECK(slot.contains_indirect_pointer());
CallIndirectPointerBarrier(object, slot_address, fp_mode,
slot.indirect_pointer_tag());
}
bind(&done);
if (v8_flags.slow_debug_code) {
ASM_CODE_COMMENT_STRING(this, "Zap scratch registers");
Move(slot_address, kZapValue, RelocInfo::NO_INFO);
Move(value, kZapValue, RelocInfo::NO_INFO);
}
}
void MacroAssembler::Check(Condition cc, AbortReason reason) {
Label L;
j(cc, &L, Label::kNear);
Abort(reason);
bind(&L);
}
void MacroAssembler::SbxCheck(Condition cc, AbortReason reason) {
Check(cc, reason);
}
void MacroAssembler::CheckStackAlignment() {
int frame_alignment = base::OS::ActivationFrameAlignment();
int frame_alignment_mask = frame_alignment - 1;
if (frame_alignment > kSystemPointerSize) {
ASM_CODE_COMMENT(this);
DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
Label alignment_as_expected;
testq(rsp, Immediate(frame_alignment_mask));
j(zero, &alignment_as_expected, Label::kNear);
int3();
bind(&alignment_as_expected);
}
}
void MacroAssembler::AlignStackPointer() {
const int kFrameAlignment = base::OS::ActivationFrameAlignment();
if (kFrameAlignment > 0) {
DCHECK(base::bits::IsPowerOfTwo(kFrameAlignment));
DCHECK(is_int8(kFrameAlignment));
andq(rsp, Immediate(-kFrameAlignment));
}
}
void MacroAssembler::Abort(AbortReason reason) {
ASM_CODE_COMMENT(this);
if (v8_flags.code_comments) {
RecordComment("Abort message:", SourceLocation{});
RecordComment(GetAbortReason(reason), SourceLocation{});
}
if (!v8_flags.debug_code || v8_flags.trap_on_abort) {
int3();
return;
}
if (should_abort_hard()) {
FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE);
Move(kCArgRegs[0], static_cast<int>(reason));
PrepareCallCFunction(1);
LoadAddress(rax, ExternalReference::abort_with_reason());
call(rax);
return;
}
Move(rdx, Smi::FromInt(static_cast<int>(reason)));
{
FrameScope scope(this, StackFrame::NO_FRAME_TYPE);
if (root_array_available()) {
Call(EntryFromBuiltinAsOperand(Builtin::kAbort));
} else {
CallBuiltin(Builtin::kAbort);
}
}
int3();
}
void MacroAssembler::CallRuntime(const Runtime::Function* f,
int num_arguments) {
ASM_CODE_COMMENT(this);
CHECK(f->nargs < 0 || f->nargs == num_arguments);
Move(rax, num_arguments);
LoadAddress(rbx, ExternalReference::Create(f));
bool switch_to_central = options().is_wasm;
CallBuiltin(Builtins::RuntimeCEntry(f->result_size, switch_to_central));
}
void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
ASM_CODE_COMMENT(this);
const Runtime::Function* function = Runtime::FunctionForId(fid);
DCHECK_EQ(1, function->result_size);
if (function->nargs >= 0) {
Move(rax, function->nargs);
}
JumpToExternalReference(ExternalReference::Create(fid));
}
void MacroAssembler::JumpToExternalReference(const ExternalReference& ext,
bool builtin_exit_frame) {
ASM_CODE_COMMENT(this);
LoadAddress(rbx, ext);
TailCallBuiltin(Builtins::CEntry(1, ArgvMode::kStack, builtin_exit_frame));
}
#ifdef V8_ENABLE_DEBUG_CODE
void MacroAssembler::AssertFeedbackCell(Register object, Register scratch) {
if (v8_flags.debug_code) {
IsObjectType(object, FEEDBACK_CELL_TYPE, scratch);
Assert(equal, AbortReason::kExpectedFeedbackCell);
}
}
void MacroAssembler::AssertFeedbackVector(Register object, Register scratch) {
if (v8_flags.debug_code) {
IsObjectType(object, FEEDBACK_VECTOR_TYPE, scratch);
Assert(equal, AbortReason::kExpectedFeedbackVector);
}
}
#endif
void MacroAssembler::GenerateTailCallToReturnedCode(
Runtime::FunctionId function_id, JumpMode jump_mode) {
ASM_CODE_COMMENT(this);
{
FrameScope scope(this, StackFrame::INTERNAL);
Push(kJavaScriptCallTargetRegister);
Push(kJavaScriptCallNewTargetRegister);
SmiTag(kJavaScriptCallArgCountRegister);
Push(kJavaScriptCallArgCountRegister);
#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
static_assert(kJSDispatchHandleShift > 0);
AssertSmi(kJavaScriptCallDispatchHandleRegister);
Push(kJavaScriptCallDispatchHandleRegister);
#endif
Push(kJavaScriptCallTargetRegister);
CallRuntime(function_id, 1);
#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
Pop(kJavaScriptCallDispatchHandleRegister);
#endif
Pop(kJavaScriptCallArgCountRegister);
SmiUntagUnsigned(kJavaScriptCallArgCountRegister);
Pop(kJavaScriptCallNewTargetRegister);
Pop(kJavaScriptCallTargetRegister);
}
static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch");
#ifndef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
movl(kJavaScriptCallDispatchHandleRegister,
FieldOperand(kJavaScriptCallTargetRegister,
JSFunction::kDispatchHandleOffset));
#endif
LoadEntrypointFromJSDispatchTable(rcx, kJavaScriptCallDispatchHandleRegister);
DCHECK_EQ(jump_mode, JumpMode::kJump);
jmp(rcx);
}
int MacroAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
Register exclusion) const {
int bytes = 0;
RegList saved_regs = kCallerSaved - exclusion;
bytes += kSystemPointerSize * saved_regs.Count();
if (fp_mode == SaveFPRegsMode::kSave) {
bytes += kStackSavedSavedFPSize * kAllocatableDoubleRegisters.Count();
}
return bytes;
}
int MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode,
Register exclusion) {
ASM_CODE_COMMENT(this);
int bytes = 0;
bytes += PushAll(kCallerSaved - exclusion);
if (fp_mode == SaveFPRegsMode::kSave) {
bytes += PushAll(kAllocatableDoubleRegisters);
}
return bytes;
}
int MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion) {
ASM_CODE_COMMENT(this);
int bytes = 0;
if (fp_mode == SaveFPRegsMode::kSave) {
bytes += PopAll(kAllocatableDoubleRegisters);
}
bytes += PopAll(kCallerSaved - exclusion);
return bytes;
}
int MacroAssembler::PushAll(RegList registers) {
int bytes = 0;
for (Register reg : registers) {
pushq(reg);
bytes += kSystemPointerSize;
}
return bytes;
}
int MacroAssembler::PopAll(RegList registers) {
int bytes = 0;
for (Register reg : base::Reversed(registers)) {
popq(reg);
bytes += kSystemPointerSize;
}
return bytes;
}
int MacroAssembler::PushAll(DoubleRegList registers, int stack_slot_size) {
if (registers.is_empty()) return 0;
const int delta = stack_slot_size * registers.Count();
AllocateStackSpace(delta);
int slot = 0;
for (XMMRegister reg : registers) {
if (stack_slot_size == kDoubleSize) {
Movsd(Operand(rsp, slot), reg);
} else {
DCHECK_EQ(stack_slot_size, 2 * kDoubleSize);
Movdqu(Operand(rsp, slot), reg);
}
slot += stack_slot_size;
}
DCHECK_EQ(slot, delta);
return delta;
}
int MacroAssembler::PopAll(DoubleRegList registers, int stack_slot_size) {
if (registers.is_empty()) return 0;
int slot = 0;
for (XMMRegister reg : registers) {
if (stack_slot_size == kDoubleSize) {
Movsd(reg, Operand(rsp, slot));
} else {
DCHECK_EQ(stack_slot_size, 2 * kDoubleSize);
Movdqu(reg, Operand(rsp, slot));
}
slot += stack_slot_size;
}
DCHECK_EQ(slot, stack_slot_size * registers.Count());
addq(rsp, Immediate(slot));
return slot;
}
void MacroAssembler::Movq(XMMRegister dst, Register src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope avx_scope(this, AVX);
vmovq(dst, src);
} else {
movq(dst, src);
}
}
void MacroAssembler::Movq(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope avx_scope(this, AVX);
vmovq(dst, src);
} else {
movq(dst, src);
}
}
void MacroAssembler::Pextrq(Register dst, XMMRegister src, int8_t imm8) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope avx_scope(this, AVX);
vpextrq(dst, src, imm8);
} else {
CpuFeatureScope sse_scope(this, SSE4_1);
pextrq(dst, src, imm8);
}
}
void MacroAssembler::Cvtss2sd(XMMRegister dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtss2sd(dst, src, src);
} else {
cvtss2sd(dst, src);
}
}
void MacroAssembler::Cvtss2sd(XMMRegister dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtss2sd(dst, dst, src);
} else {
cvtss2sd(dst, src);
}
}
void MacroAssembler::Cvtsd2ss(XMMRegister dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtsd2ss(dst, src, src);
} else {
cvtsd2ss(dst, src);
}
}
void MacroAssembler::Cvtsd2ss(XMMRegister dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtsd2ss(dst, dst, src);
} else {
cvtsd2ss(dst, src);
}
}
void MacroAssembler::Cvtlsi2sd(XMMRegister dst, Register src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtlsi2sd(dst, kScratchDoubleReg, src);
} else {
xorpd(dst, dst);
cvtlsi2sd(dst, src);
}
}
void MacroAssembler::Cvtlsi2sd(XMMRegister dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtlsi2sd(dst, kScratchDoubleReg, src);
} else {
xorpd(dst, dst);
cvtlsi2sd(dst, src);
}
}
void MacroAssembler::Cvtlsi2ss(XMMRegister dst, Register src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtlsi2ss(dst, kScratchDoubleReg, src);
} else {
xorps(dst, dst);
cvtlsi2ss(dst, src);
}
}
void MacroAssembler::Cvtlsi2ss(XMMRegister dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtlsi2ss(dst, kScratchDoubleReg, src);
} else {
xorps(dst, dst);
cvtlsi2ss(dst, src);
}
}
void MacroAssembler::Cvtqsi2ss(XMMRegister dst, Register src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtqsi2ss(dst, kScratchDoubleReg, src);
} else {
xorps(dst, dst);
cvtqsi2ss(dst, src);
}
}
void MacroAssembler::Cvtqsi2ss(XMMRegister dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtqsi2ss(dst, kScratchDoubleReg, src);
} else {
xorps(dst, dst);
cvtqsi2ss(dst, src);
}
}
void MacroAssembler::Cvtqsi2sd(XMMRegister dst, Register src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtqsi2sd(dst, kScratchDoubleReg, src);
} else {
xorpd(dst, dst);
cvtqsi2sd(dst, src);
}
}
void MacroAssembler::Cvtqsi2sd(XMMRegister dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvtqsi2sd(dst, kScratchDoubleReg, src);
} else {
xorpd(dst, dst);
cvtqsi2sd(dst, src);
}
}
void MacroAssembler::Cvtlui2ss(XMMRegister dst, Register src) {
movl(kScratchRegister, src);
Cvtqsi2ss(dst, kScratchRegister);
}
void MacroAssembler::Cvtlui2ss(XMMRegister dst, Operand src) {
movl(kScratchRegister, src);
Cvtqsi2ss(dst, kScratchRegister);
}
void MacroAssembler::Cvtlui2sd(XMMRegister dst, Register src) {
movl(kScratchRegister, src);
Cvtqsi2sd(dst, kScratchRegister);
}
void MacroAssembler::Cvtlui2sd(XMMRegister dst, Operand src) {
movl(kScratchRegister, src);
Cvtqsi2sd(dst, kScratchRegister);
}
void MacroAssembler::Cvtqui2ss(XMMRegister dst, Register src) {
Label done;
Cvtqsi2ss(dst, src);
testq(src, src);
j(positive, &done, Label::kNear);
if (src != kScratchRegister) movq(kScratchRegister, src);
shrq(kScratchRegister, Immediate(1));
Label msb_not_set;
j(not_carry, &msb_not_set, Label::kNear);
orq(kScratchRegister, Immediate(1));
bind(&msb_not_set);
Cvtqsi2ss(dst, kScratchRegister);
Addss(dst, dst);
bind(&done);
}
void MacroAssembler::Cvtqui2ss(XMMRegister dst, Operand src) {
movq(kScratchRegister, src);
Cvtqui2ss(dst, kScratchRegister);
}
void MacroAssembler::Cvtqui2sd(XMMRegister dst, Register src) {
Label done;
Cvtqsi2sd(dst, src);
testq(src, src);
j(positive, &done, Label::kNear);
if (src != kScratchRegister) movq(kScratchRegister, src);
shrq(kScratchRegister, Immediate(1));
Label msb_not_set;
j(not_carry, &msb_not_set, Label::kNear);
orq(kScratchRegister, Immediate(1));
bind(&msb_not_set);
Cvtqsi2sd(dst, kScratchRegister);
Addsd(dst, dst);
bind(&done);
}
void MacroAssembler::Cvtqui2sd(XMMRegister dst, Operand src) {
movq(kScratchRegister, src);
Cvtqui2sd(dst, kScratchRegister);
}
void MacroAssembler::Cvttss2si(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttss2si(dst, src);
} else {
cvttss2si(dst, src);
}
}
void MacroAssembler::Cvttss2si(Register dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttss2si(dst, src);
} else {
cvttss2si(dst, src);
}
}
void MacroAssembler::Cvttsd2si(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttsd2si(dst, src);
} else {
cvttsd2si(dst, src);
}
}
void MacroAssembler::Cvttsd2si(Register dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttsd2si(dst, src);
} else {
cvttsd2si(dst, src);
}
}
void MacroAssembler::Cvttss2siq(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttss2siq(dst, src);
} else {
cvttss2siq(dst, src);
}
}
void MacroAssembler::Cvttss2siq(Register dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttss2siq(dst, src);
} else {
cvttss2siq(dst, src);
}
}
void MacroAssembler::Cvttsd2siq(Register dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttsd2siq(dst, src);
} else {
cvttsd2siq(dst, src);
}
}
void MacroAssembler::Cvttsd2siq(Register dst, Operand src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(this, AVX);
vcvttsd2siq(dst, src);
} else {
cvttsd2siq(dst, src);
}
}
void MacroAssembler::Cvtph2pd(XMMRegister dst, XMMRegister src) {
ASM_CODE_COMMENT(this);
CpuFeatureScope f16c_scope(this, F16C);
CpuFeatureScope avx_scope(this, AVX);
vcvtph2ps(dst, src);
Cvtss2sd(dst, dst);
}
void MacroAssembler::Cvtpd2ph(XMMRegister dst, XMMRegister src, Register tmp) {
ASM_CODE_COMMENT(this);
CpuFeatureScope f16c_scope(this, F16C);
CpuFeatureScope avx_scope(this, AVX);
Register tmp2 = kScratchRegister;
DCHECK_NE(tmp, tmp2);
DCHECK_NE(dst, src);
Label f32tof16;
Cvtsd2ss(dst, src);
vmovd(tmp, dst);
andl(tmp, Immediate(kFP32WithoutSignMask));
cmpl(tmp, Immediate(kFP32MinFP16ZeroRepresentable));
j(below, &f32tof16);
cmpl(tmp, Immediate(kFP32MaxFP16Representable));
j(above_equal, &f32tof16);
cmpl(tmp, Immediate(kFP32SubnormalThresholdOfFP16));
setcc(above_equal, tmp2);
movzxbl(tmp2, tmp2);
shll(tmp2, Immediate(12));
andl(tmp, Immediate(0x1fff));
cmpl(tmp, tmp2);
j(not_equal, &f32tof16);
Move(kScratchDoubleReg, static_cast<uint32_t>(1));
psignd(kScratchDoubleReg, src);
paddd(dst, kScratchDoubleReg);
bind(&f32tof16);
vcvtps2ph(dst, dst, 4);
}
namespace {
template <typename OperandOrXMMRegister, bool is_double>
void ConvertFloatToUint64(MacroAssembler* masm, Register dst,
OperandOrXMMRegister src, Label* fail) {
Label success;
if (is_double) {
masm->Cvttsd2siq(dst, src);
} else {
masm->Cvttss2siq(dst, src);
}
masm->testq(dst, dst);
masm->j(positive, &success);
if (is_double) {
masm->Move(kScratchDoubleReg, -9223372036854775808.0);
masm->Addsd(kScratchDoubleReg, src);
masm->Cvttsd2siq(dst, kScratchDoubleReg);
} else {
masm->Move(kScratchDoubleReg, -9223372036854775808.0f);
masm->Addss(kScratchDoubleReg, src);
masm->Cvttss2siq(dst, kScratchDoubleReg);
}
masm->testq(dst, dst);
masm->j(negative, fail ? fail : &success);
masm->Move(kScratchRegister, 0x8000000000000000);
masm->orq(dst, kScratchRegister);
masm->bind(&success);
}
template <typename OperandOrXMMRegister, bool is_double>
void ConvertFloatToUint32(MacroAssembler* masm, Register dst,
OperandOrXMMRegister src, Label* fail) {
Label success;
if (is_double) {
masm->Cvttsd2si(dst, src);
} else {
masm->Cvttss2si(dst, src);
}
masm->testl(dst, dst);
masm->j(positive, &success);
if (is_double) {
masm->Move(kScratchDoubleReg, -2147483648.0);
masm->Addsd(kScratchDoubleReg, src);
masm->Cvttsd2si(dst, kScratchDoubleReg);
} else {
masm->Move(kScratchDoubleReg, -2147483648.0f);
masm->Addss(kScratchDoubleReg, src);
masm->Cvttss2si(dst, kScratchDoubleReg);
}
masm->testl(dst, dst);
masm->j(negative, fail ? fail : &success);
masm->Move(kScratchRegister, 0x80000000);
masm->orl(dst, kScratchRegister);
masm->bind(&success);
}
}
void MacroAssembler::Cvttsd2uiq(Register dst, Operand src, Label* fail) {
ConvertFloatToUint64<Operand, true>(this, dst, src, fail);
}
void MacroAssembler::Cvttsd2uiq(Register dst, XMMRegister src, Label* fail) {
ConvertFloatToUint64<XMMRegister, true>(this, dst, src, fail);
}
void MacroAssembler::Cvttsd2ui(Register dst, Operand src, Label* fail) {
ConvertFloatToUint32<Operand, true>(this, dst, src, fail);
}
void MacroAssembler::Cvttsd2ui(Register dst, XMMRegister src, Label* fail) {
ConvertFloatToUint32<XMMRegister, true>(this, dst, src, fail);
}
void MacroAssembler::Cvttss2uiq(Register dst, Operand src, Label* fail) {
ConvertFloatToUint64<Operand, false>(this, dst, src, fail);
}
void MacroAssembler::Cvttss2uiq(Register dst, XMMRegister src, Label* fail) {
ConvertFloatToUint64<XMMRegister, false>(this, dst, src, fail);
}
void MacroAssembler::Cvttss2ui(Register dst, Operand src, Label* fail) {
ConvertFloatToUint32<Operand, false>(this, dst, src, fail);
}
void MacroAssembler::Cvttss2ui(Register dst, XMMRegister src, Label* fail) {
ConvertFloatToUint32<XMMRegister, false>(this, dst, src, fail);
}
void MacroAssembler::Cmpeqss(XMMRegister dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope avx_scope(this, AVX);
vcmpeqss(dst, src);
} else {
cmpeqss(dst, src);
}
}
void MacroAssembler::Cmpeqsd(XMMRegister dst, XMMRegister src) {
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope avx_scope(this, AVX);
vcmpeqsd(dst, src);
} else {
cmpeqsd(dst, src);
}
}
void MacroAssembler::S256Not(YMMRegister dst, YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
CpuFeatureScope avx2_scope(this, AVX2);
if (dst == src) {
vpcmpeqd(scratch, scratch, scratch);
vpxor(dst, dst, scratch);
} else {
vpcmpeqd(dst, dst, dst);
vpxor(dst, dst, src);
}
}
void MacroAssembler::S256Select(YMMRegister dst, YMMRegister mask,
YMMRegister src1, YMMRegister src2,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
CpuFeatureScope avx2_scope(this, AVX2);
vpandn(scratch, mask, src2);
vpand(dst, src1, mask);
vpor(dst, dst, scratch);
}
Register MacroAssembler::GetSmiConstant(Tagged<Smi> source) {
Move(kScratchRegister, source);
return kScratchRegister;
}
void MacroAssembler::Cmp(Register dst, int32_t src) {
if (src == 0) {
testl(dst, dst);
} else {
cmpl(dst, Immediate(src));
}
}
void MacroAssembler::I64x4Mul(YMMRegister dst, YMMRegister lhs, YMMRegister rhs,
YMMRegister tmp1, YMMRegister tmp2) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(dst, tmp1, tmp2));
DCHECK(!AreAliased(lhs, tmp1, tmp2));
DCHECK(!AreAliased(rhs, tmp1, tmp2));
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX2);
vpsrlq(tmp1, lhs, uint8_t{32});
vpmuludq(tmp1, tmp1, rhs);
vpsrlq(tmp2, rhs, uint8_t{32});
vpmuludq(tmp2, tmp2, lhs);
vpaddq(tmp2, tmp2, tmp1);
vpsllq(tmp2, tmp2, uint8_t{32});
vpmuludq(dst, lhs, rhs);
vpaddq(dst, dst, tmp2);
}
#define DEFINE_ISPLAT(name, suffix, instr_mov) \
void MacroAssembler::name(YMMRegister dst, Register src) { \
ASM_CODE_COMMENT(this); \
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2)); \
CpuFeatureScope avx_scope(this, AVX); \
CpuFeatureScope avx2_scope(this, AVX2); \
instr_mov(dst, src); \
vpbroadcast##suffix(dst, dst); \
} \
\
void MacroAssembler::name(YMMRegister dst, Operand src) { \
ASM_CODE_COMMENT(this); \
DCHECK(CpuFeatures::IsSupported(AVX2)); \
CpuFeatureScope avx2_scope(this, AVX2); \
vpbroadcast##suffix(dst, src); \
}
MACRO_ASM_X64_ISPLAT_LIST(DEFINE_ISPLAT)
#undef DEFINE_ISPLAT
void MacroAssembler::F64x4Splat(YMMRegister dst, XMMRegister src) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
vbroadcastsd(dst, src);
}
void MacroAssembler::F32x8Splat(YMMRegister dst, XMMRegister src) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
vbroadcastss(dst, src);
}
void MacroAssembler::F64x4Min(YMMRegister dst, YMMRegister lhs, YMMRegister rhs,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vminpd(scratch, lhs, rhs);
vminpd(dst, rhs, lhs);
vorpd(scratch, scratch, dst);
vcmpunordpd(dst, dst, scratch);
vorpd(scratch, scratch, dst);
vpsrlq(dst, dst, uint8_t{13});
vandnpd(dst, dst, scratch);
}
void MacroAssembler::F64x4Max(YMMRegister dst, YMMRegister lhs, YMMRegister rhs,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vmaxpd(scratch, lhs, rhs);
vmaxpd(dst, rhs, lhs);
vxorpd(dst, dst, scratch);
vorpd(scratch, scratch, dst);
vsubpd(scratch, scratch, dst);
vcmpunordpd(dst, dst, scratch);
vpsrlq(dst, dst, uint8_t{13});
vandnpd(dst, dst, scratch);
}
void MacroAssembler::F32x8Min(YMMRegister dst, YMMRegister lhs, YMMRegister rhs,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vminps(scratch, lhs, rhs);
vminps(dst, rhs, lhs);
vorps(scratch, scratch, dst);
vcmpunordps(dst, dst, scratch);
vorps(scratch, scratch, dst);
vpsrld(dst, dst, uint8_t{10});
vandnps(dst, dst, scratch);
}
void MacroAssembler::F32x8Max(YMMRegister dst, YMMRegister lhs, YMMRegister rhs,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vmaxps(scratch, lhs, rhs);
vmaxps(dst, rhs, lhs);
vxorps(dst, dst, scratch);
vorps(scratch, scratch, dst);
vsubps(scratch, scratch, dst);
vcmpunordps(dst, dst, scratch);
vpsrld(dst, dst, uint8_t{10});
vandnps(dst, dst, scratch);
}
void MacroAssembler::F16x8Min(YMMRegister dst, XMMRegister lhs, XMMRegister rhs,
YMMRegister scratch, YMMRegister scratch2) {
ASM_CODE_COMMENT(this);
CpuFeatureScope f16c_scope(this, F16C);
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vcvtph2ps(scratch, lhs);
vcvtph2ps(scratch2, rhs);
vminps(dst, scratch, scratch2);
vminps(scratch, scratch2, scratch);
vorps(scratch, scratch, dst);
vcmpunordps(dst, dst, scratch);
vorps(scratch, scratch, dst);
vpsrld(dst, dst, uint8_t{10});
vandnps(dst, dst, scratch);
vcvtps2ph(dst, dst, 0);
}
void MacroAssembler::F16x8Max(YMMRegister dst, XMMRegister lhs, XMMRegister rhs,
YMMRegister scratch, YMMRegister scratch2) {
ASM_CODE_COMMENT(this);
CpuFeatureScope f16c_scope(this, F16C);
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vcvtph2ps(scratch, lhs);
vcvtph2ps(scratch2, rhs);
vmaxps(dst, scratch, scratch2);
vmaxps(scratch, scratch2, scratch);
vxorps(dst, dst, scratch);
vorps(scratch, scratch, dst);
vsubps(scratch, scratch, dst);
vcmpunordps(dst, dst, scratch);
vpsrld(dst, dst, uint8_t{10});
vandnps(dst, dst, scratch);
vcvtps2ph(dst, dst, 0);
}
void MacroAssembler::I64x4ExtMul(YMMRegister dst, XMMRegister src1,
XMMRegister src2, YMMRegister scratch,
bool is_signed) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX2);
vpmovzxdq(scratch, src1);
vpmovzxdq(dst, src2);
if (is_signed) {
vpmuldq(dst, scratch, dst);
} else {
vpmuludq(dst, scratch, dst);
}
}
void MacroAssembler::I32x8ExtMul(YMMRegister dst, XMMRegister src1,
XMMRegister src2, YMMRegister scratch,
bool is_signed) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX2);
is_signed ? vpmovsxwd(scratch, src1) : vpmovzxwd(scratch, src1);
is_signed ? vpmovsxwd(dst, src2) : vpmovzxwd(dst, src2);
vpmulld(dst, dst, scratch);
}
void MacroAssembler::I16x16ExtMul(YMMRegister dst, XMMRegister src1,
XMMRegister src2, YMMRegister scratch,
bool is_signed) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX2);
is_signed ? vpmovsxbw(scratch, src1) : vpmovzxbw(scratch, src1);
is_signed ? vpmovsxbw(dst, src2) : vpmovzxbw(dst, src2);
vpmullw(dst, dst, scratch);
}
void MacroAssembler::I32x8ExtAddPairwiseI16x16S(YMMRegister dst,
YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
Move(scratch, uint32_t{1});
vpbroadcastw(scratch, scratch);
vpmaddwd(dst, src, scratch);
}
void MacroAssembler::I32x8ExtAddPairwiseI16x16U(YMMRegister dst,
YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
vpsrld(scratch, src, 16);
vpblendw(dst, src, scratch, 0xAA);
vpaddd(dst, dst, scratch);
}
void MacroAssembler::I16x16ExtAddPairwiseI8x32S(YMMRegister dst,
YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
Move(scratch, uint32_t{1});
vpbroadcastb(scratch, scratch);
vpmaddubsw(dst, scratch, src);
}
void MacroAssembler::I16x16ExtAddPairwiseI8x32U(YMMRegister dst,
YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
Move(scratch, uint32_t{1});
vpbroadcastb(scratch, scratch);
vpmaddubsw(dst, src, scratch);
}
void MacroAssembler::I32x8SConvertF32x8(YMMRegister dst, YMMRegister src,
YMMRegister tmp, Register scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
Operand int32_overflow_as_float = ExternalReferenceAsOperand(
ExternalReference::address_of_wasm_i32x8_int32_overflow_as_float(),
scratch);
vcmpeqps(tmp, src, src);
vandps(dst, src, tmp);
vcmpgeps(tmp, src, int32_overflow_as_float);
vcvttps2dq(dst, dst);
vpxor(dst, dst, tmp);
}
void MacroAssembler::I16x8SConvertF16x8(YMMRegister dst, XMMRegister src,
YMMRegister tmp, Register scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2) &&
CpuFeatures::IsSupported(F16C));
CpuFeatureScope f16c_scope(this, F16C);
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
Operand op = ExternalReferenceAsOperand(
ExternalReference::address_of_wasm_i32x8_int32_overflow_as_float(),
scratch);
vcvtph2ps(dst, src);
vcmpeqps(tmp, dst, dst);
vandps(dst, dst, tmp);
vcmpgeps(tmp, dst, op);
vcvttps2dq(dst, dst);
vpxor(dst, dst, tmp);
vpermq(tmp, dst, 0x4E);
vpackssdw(dst, dst, tmp);
}
void MacroAssembler::I16x8TruncF16x8U(YMMRegister dst, XMMRegister src,
YMMRegister tmp) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2) &&
CpuFeatures::IsSupported(F16C));
CpuFeatureScope f16c_scope(this, F16C);
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
Operand op = ExternalReferenceAsOperand(
ExternalReference::address_of_wasm_i32x8_int32_overflow_as_float(),
kScratchRegister);
vcvtph2ps(dst, src);
vpxor(tmp, tmp, tmp);
vmaxps(dst, dst, tmp);
vcmpgeps(tmp, dst, op);
vcvttps2dq(dst, dst);
vpxor(dst, dst, tmp);
vpermq(tmp, dst, 0x4E);
vpackusdw(dst, dst, tmp);
}
void MacroAssembler::F16x8Qfma(YMMRegister dst, XMMRegister src1,
XMMRegister src2, XMMRegister src3,
YMMRegister tmp, YMMRegister tmp2) {
CpuFeatureScope fma3_scope(this, FMA3);
CpuFeatureScope f16c_scope(this, F16C);
if (dst.code() == src2.code()) {
vcvtph2ps(dst, dst);
vcvtph2ps(tmp, src1);
vcvtph2ps(tmp2, src3);
vfmadd213ps(dst, tmp, tmp2);
} else if (dst.code() == src3.code()) {
vcvtph2ps(dst, dst);
vcvtph2ps(tmp, src2);
vcvtph2ps(tmp2, src1);
vfmadd231ps(dst, tmp, tmp2);
} else {
vcvtph2ps(dst, src1);
vcvtph2ps(tmp, src2);
vcvtph2ps(tmp2, src3);
vfmadd213ps(dst, tmp, tmp2);
}
vcvtps2ph(dst, dst, 0);
}
void MacroAssembler::F16x8Qfms(YMMRegister dst, XMMRegister src1,
XMMRegister src2, XMMRegister src3,
YMMRegister tmp, YMMRegister tmp2) {
CpuFeatureScope fma3_scope(this, FMA3);
CpuFeatureScope f16c_scope(this, F16C);
if (dst.code() == src2.code()) {
vcvtph2ps(dst, dst);
vcvtph2ps(tmp, src1);
vcvtph2ps(tmp2, src3);
vfnmadd213ps(dst, tmp, tmp2);
} else if (dst.code() == src3.code()) {
vcvtph2ps(dst, dst);
vcvtph2ps(tmp, src2);
vcvtph2ps(tmp2, src1);
vfnmadd231ps(dst, tmp, tmp2);
} else {
vcvtph2ps(dst, src1);
vcvtph2ps(tmp, src2);
vcvtph2ps(tmp2, src3);
vfnmadd213ps(dst, tmp, tmp2);
}
vcvtps2ph(dst, dst, 0);
}
void MacroAssembler::F32x8Qfma(YMMRegister dst, YMMRegister src1,
YMMRegister src2, YMMRegister src3,
YMMRegister tmp) {
QFMA(ps);
}
void MacroAssembler::F32x8Qfms(YMMRegister dst, YMMRegister src1,
YMMRegister src2, YMMRegister src3,
YMMRegister tmp) {
QFMS(ps);
}
void MacroAssembler::F64x4Qfma(YMMRegister dst, YMMRegister src1,
YMMRegister src2, YMMRegister src3,
YMMRegister tmp) {
QFMA(pd);
}
void MacroAssembler::F64x4Qfms(YMMRegister dst, YMMRegister src1,
YMMRegister src2, YMMRegister src3,
YMMRegister tmp) {
QFMS(pd);
}
void MacroAssembler::I32x8DotI8x32I7x32AddS(YMMRegister dst, YMMRegister src1,
YMMRegister src2, YMMRegister src3,
YMMRegister scratch,
YMMRegister splat_reg) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
DCHECK_EQ(dst, src3);
if (CpuFeatures::IsSupported(AVX_VNNI_INT8)) {
CpuFeatureScope avx_vnni_int8_scope(this, AVX_VNNI_INT8);
vpdpbssd(dst, src2, src1);
return;
} else if (CpuFeatures::IsSupported(AVX_VNNI)) {
CpuFeatureScope avx_scope(this, AVX_VNNI);
vpdpbusd(dst, src2, src1);
return;
}
DCHECK_NE(scratch, splat_reg);
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vpcmpeqd(splat_reg, splat_reg, splat_reg);
vpsrlw(splat_reg, splat_reg, uint8_t{15});
vpmaddubsw(scratch, src2, src1);
vpmaddwd(scratch, splat_reg, scratch);
vpaddd(dst, src3, scratch);
}
void MacroAssembler::I32x8TruncF32x8U(YMMRegister dst, YMMRegister src,
YMMRegister scratch1,
YMMRegister scratch2) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx_scope(this, AVX);
CpuFeatureScope avx2_scope(this, AVX2);
vpxor(scratch1, scratch1, scratch1);
vmaxps(dst, src, scratch1);
vpcmpeqd(scratch1, scratch1, scratch1);
vpsrld(scratch1, scratch1, uint8_t{1});
vcvtdq2ps(scratch1, scratch1);
vsubps(scratch2, dst, scratch1);
vcmpleps(scratch1, scratch1, scratch2);
vcvttps2dq(scratch2, scratch2);
vpxor(scratch2, scratch2, scratch1);
vpxor(scratch1, scratch1, scratch1);
vpmaxsd(scratch2, scratch2, scratch1);
vcvttps2dq(dst, dst);
vpaddd(dst, dst, scratch2);
}
void MacroAssembler::Negpd(YMMRegister dst, YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
if (dst == src) {
vpcmpeqq(scratch, scratch, scratch);
vpsllq(scratch, scratch, uint8_t{63});
vpxor(dst, dst, scratch);
} else {
vpcmpeqq(dst, dst, dst);
vpsllq(dst, dst, uint8_t{63});
vpxor(dst, dst, src);
}
}
void MacroAssembler::Negps(YMMRegister dst, YMMRegister src,
YMMRegister scratch) {
ASM_CODE_COMMENT(this);
DCHECK(CpuFeatures::IsSupported(AVX) && CpuFeatures::IsSupported(AVX2));
CpuFeatureScope avx2_scope(this, AVX2);
if (dst == src) {
vpcmpeqd(scratch, scratch, scratch);
vpslld(scratch, scratch, uint8_t{31});
vpxor(dst, dst, scratch);
} else {
vpcmpeqd(dst, dst, dst);
vpslld(dst, dst, uint8_t{31});
vpxor(dst, dst, src);
}
}
void MacroAssembler::SmiTag(Register reg) {
static_assert(kSmiTag == 0);
DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
if (COMPRESS_POINTERS_BOOL) {
DCHECK_EQ(kSmiShift, 1);
addl(reg, reg);
} else {
shlq(reg, Immediate(kSmiShift));
}
#ifdef ENABLE_SLOW_DCHECKS
ClobberDecompressedSmiBits(reg);
#endif
}
void MacroAssembler::SmiTag(Register dst, Register src) {
if (dst != src) {
if (COMPRESS_POINTERS_BOOL) {
movl(dst, src);
} else {
movq(dst, src);
}
}
SmiTag(dst);
}
void MacroAssembler::SmiUntag(Register reg) {
static_assert(kSmiTag == 0);
DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
if (COMPRESS_POINTERS_BOOL) {
sarl(reg, Immediate(kSmiShift));
movsxlq(reg, reg);
} else {
sarq(reg, Immediate(kSmiShift));
}
}
void MacroAssembler::SmiUntagUnsigned(Register reg) {
static_assert(kSmiTag == 0);
DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
if (COMPRESS_POINTERS_BOOL) {
#ifndef V8_ENABLE_MEMORY_CORRUPTION_API
AssertSignBitOfSmiIsZero(reg);
#endif
shrl(reg, Immediate(kSmiShift));
} else {
shrq(reg, Immediate(kSmiShift));
}
}
void MacroAssembler::SmiUntag(Register dst, Register src) {
DCHECK(dst != src);
if (COMPRESS_POINTERS_BOOL) {
movsxlq(dst, src);
} else {
movq(dst, src);
}
static_assert(kSmiTag == 0);
DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
sarq(dst, Immediate(kSmiShift));
}
void MacroAssembler::SmiUntag(Register dst, Operand src) {
if (SmiValuesAre32Bits()) {
movsxlq(dst, Operand(src, kSmiShift / kBitsPerByte));
} else {
DCHECK(SmiValuesAre31Bits());
if (COMPRESS_POINTERS_BOOL) {
movsxlq(dst, src);
} else {
movq(dst, src);
}
sarq(dst, Immediate(kSmiShift));
}
}
void MacroAssembler::SmiUntagUnsigned(Register dst, Operand src) {
if (SmiValuesAre32Bits()) {
movl(dst, Operand(src, kSmiShift / kBitsPerByte));
} else {
DCHECK(SmiValuesAre31Bits());
if (COMPRESS_POINTERS_BOOL) {
movl(dst, src);
#ifndef V8_ENABLE_MEMORY_CORRUPTION_API
AssertSignBitOfSmiIsZero(dst);
#endif
shrl(dst, Immediate(kSmiShift));
} else {
movq(dst, src);
shrq(dst, Immediate(kSmiShift));
}
}
}
void MacroAssembler::SmiToInt32(Register reg) {
AssertSmi(reg);
static_assert(kSmiTag == 0);
DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
if (COMPRESS_POINTERS_BOOL) {
sarl(reg, Immediate(kSmiShift));
} else {
shrq(reg, Immediate(kSmiShift));
}
}
void MacroAssembler::SmiToInt32(Register dst, Register src) {
if (dst != src) {
mov_tagged(dst, src);
}
SmiToInt32(dst);
}
void MacroAssembler::SmiCompare(Register smi1, Register smi2) {
AssertSmi(smi1);
AssertSmi(smi2);
cmp_tagged(smi1, smi2);
}
void MacroAssembler::SmiCompare(Register dst, Tagged<Smi> src) {
AssertSmi(dst);
Cmp(dst, src);
}
void MacroAssembler::Cmp(Register dst, Tagged<Smi> src) {
if (src.value() == 0) {
test_tagged(dst, dst);
} else if (COMPRESS_POINTERS_BOOL) {
cmp_tagged(dst, Immediate(src));
} else {
DCHECK_NE(dst, kScratchRegister);
Register constant_reg = GetSmiConstant(src);
cmp_tagged(dst, constant_reg);
}
}
void MacroAssembler::SmiCompare(Register dst, Operand src) {
AssertSmi(dst);
AssertSmi(src);
cmp_tagged(dst, src);
}
void MacroAssembler::SmiCompare(Operand dst, Register src) {
AssertSmi(dst);
AssertSmi(src);
cmp_tagged(dst, src);
}
void MacroAssembler::SmiCompare(Operand dst, Tagged<Smi> src) {
AssertSmi(dst);
if (SmiValuesAre32Bits()) {
cmpl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(src.value()));
} else {
DCHECK(SmiValuesAre31Bits());
cmpl(dst, Immediate(src));
}
}
void MacroAssembler::Cmp(Operand dst, Tagged<Smi> src) {
Register smi_reg = GetSmiConstant(src);
DCHECK(!dst.AddressUsesRegister(smi_reg));
cmp_tagged(dst, smi_reg);
}
void MacroAssembler::ClobberDecompressedSmiBits(Register src) {
#ifdef V8_COMPRESS_POINTERS
ASM_CODE_COMMENT(this);
static constexpr unsigned int clobber_mask = 0x515151;
static constexpr int rot_to_unused =
64 - kSmiShiftSize - kSmiTagSize - kSmiValueSize;
rolq(src, Immediate(rot_to_unused));
xorq(src, Immediate(clobber_mask));
rorq(src, Immediate(rot_to_unused));
#endif
}
Condition MacroAssembler::CheckSmi(Register src) {
static_assert(kSmiTag == 0);
testb(src, Immediate(kSmiTagMask));
return zero;
}
Condition MacroAssembler::CheckSmi(Operand src) {
static_assert(kSmiTag == 0);
testb(src, Immediate(kSmiTagMask));
return zero;
}
void MacroAssembler::JumpIfSmi(Register src, Label* on_smi,
Label::Distance near_jump) {
Condition smi = CheckSmi(src);
j(smi, on_smi, near_jump);
}
void MacroAssembler::JumpIfNotSmi(Register src, Label* on_not_smi,
Label::Distance near_jump) {
Condition smi = CheckSmi(src);
j(NegateCondition(smi), on_not_smi, near_jump);
}
void MacroAssembler::JumpIfNotSmi(Operand src, Label* on_not_smi,
Label::Distance near_jump) {
Condition smi = CheckSmi(src);
j(NegateCondition(smi), on_not_smi, near_jump);
}
void MacroAssembler::SmiAddConstant(Operand dst, Tagged<Smi> constant) {
if (constant.value() != 0) {
if (SmiValuesAre32Bits()) {
addl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(constant.value()));
} else {
DCHECK(SmiValuesAre31Bits());
if (kTaggedSize == kInt64Size) {
movl(kScratchRegister, dst);
addl(kScratchRegister, Immediate(constant));
movsxlq(kScratchRegister, kScratchRegister);
movq(dst, kScratchRegister);
} else {
DCHECK_EQ(kTaggedSize, kInt32Size);
addl(dst, Immediate(constant));
}
}
}
}
SmiIndex MacroAssembler::SmiToIndex(Register dst, Register src, int shift) {
if (SmiValuesAre32Bits()) {
DCHECK(is_uint6(shift));
if (dst != src) {
movq(dst, src);
}
if (shift < kSmiShift) {
sarq(dst, Immediate(kSmiShift - shift));
} else {
shlq(dst, Immediate(shift - kSmiShift));
}
return SmiIndex(dst, times_1);
} else {
DCHECK(SmiValuesAre31Bits());
movsxlq(dst, src);
if (shift < kSmiShift) {
sarq(dst, Immediate(kSmiShift - shift));
} else if (shift != kSmiShift) {
if (shift - kSmiShift <= static_cast<int>(times_8)) {
return SmiIndex(dst, static_cast<ScaleFactor>(shift - kSmiShift));
}
shlq(dst, Immediate(shift - kSmiShift));
}
return SmiIndex(dst, times_1);
}
}
void MacroAssembler::Switch(Register scratch, Register reg, int case_value_base,
Label** labels, int num_labels) {
Register table = scratch;
Label fallthrough, jump_table;
if (case_value_base != 0) {
subq(reg, Immediate(case_value_base));
}
cmpq(reg, Immediate(num_labels));
j(above_equal, &fallthrough);
leaq(table, MemOperand(&jump_table));
#ifdef V8_ENABLE_CET_IBT
jmp(MemOperand(table, reg, times_8, 0), true);
#else
jmp(MemOperand(table, reg, times_8, 0));
#endif
Align(kSystemPointerSize);
bind(&jump_table);
for (int i = 0; i < num_labels; ++i) {
dq(labels[i]);
}
bind(&fallthrough);
}
void MacroAssembler::Push(Tagged<Smi> source) {
intptr_t smi = static_cast<intptr_t>(source.ptr());
if (is_int32(smi)) {
Push(Immediate(static_cast<int32_t>(smi)));
return;
}
int first_byte_set = base::bits::CountTrailingZeros64(smi) / 8;
int last_byte_set = (63 - base::bits::CountLeadingZeros64(smi)) / 8;
if (first_byte_set == last_byte_set) {
Push(Immediate(0));
movb(Operand(rsp, first_byte_set),
Immediate(static_cast<int8_t>(smi >> (8 * first_byte_set))));
return;
}
Register constant = GetSmiConstant(source);
Push(constant);
}
void MacroAssembler::Move(Register dst, Tagged<Smi> source) {
static_assert(kSmiTag == 0);
int value = source.value();
if (value == 0) {
xorl(dst, dst);
} else if (SmiValuesAre32Bits()) {
Move(dst, source.ptr(), RelocInfo::NO_INFO);
} else {
uint32_t uvalue = static_cast<uint32_t>(source.ptr());
Move(dst, uvalue);
}
}
void MacroAssembler::Move(Operand dst, intptr_t x) {
if (is_int32(x)) {
movq(dst, Immediate(static_cast<int32_t>(x)));
} else {
Move(kScratchRegister, x);
movq(dst, kScratchRegister);
}
}
void MacroAssembler::Move(Register dst, ExternalReference ext) {
if (root_array_available()) {
if (ext.IsIsolateFieldId()) {
leaq(dst, Operand(kRootRegister, ext.offset_from_root_register()));
return;
} else if (options().isolate_independent_code) {
IndirectLoadExternalReference(dst, ext);
return;
}
}
CHECK(!ext.IsIsolateFieldId());
movq(dst, Immediate64(ext.address(), RelocInfo::EXTERNAL_REFERENCE));
}
void MacroAssembler::Move(Register dst, Register src) {
if (dst != src) {
movq(dst, src);
}
}
void MacroAssembler::Move(Register dst, Operand src) { movq(dst, src); }
void MacroAssembler::Move(Register dst, Immediate src) {
if (src.rmode() == RelocInfo::Mode::NO_INFO) {
Move(dst, src.value());
} else {
movl(dst, src);
}
}
void MacroAssembler::Move(XMMRegister dst, XMMRegister src) {
if (dst != src) {
Movaps(dst, src);
}
}
void MacroAssembler::MovePair(Register dst0, Register src0, Register dst1,
Register src1) {
if (dst0 != src1) {
Move(dst0, src0);
Move(dst1, src1);
} else if (dst1 != src0) {
Move(dst1, src1);
Move(dst0, src0);
} else {
xchgq(dst0, dst1);
}
}
void MacroAssembler::MoveNumber(Register dst, double value) {
int32_t smi;
if (DoubleToSmiInteger(value, &smi)) {
Move(dst, Smi::FromInt(smi));
} else {
movq_heap_number(dst, value);
}
}
void MacroAssembler::Move(XMMRegister dst, uint32_t src) {
if (src == 0) {
Xorps(dst, dst);
} else {
unsigned nlz = base::bits::CountLeadingZeros(src);
unsigned ntz = base::bits::CountTrailingZeros(src);
unsigned pop = base::bits::CountPopulation(src);
DCHECK_NE(0u, pop);
if (pop + ntz + nlz == 32) {
Pcmpeqd(dst, dst);
if (ntz) Pslld(dst, static_cast<uint8_t>(ntz + nlz));
if (nlz) Psrld(dst, static_cast<uint8_t>(nlz));
} else {
movl(kScratchRegister, Immediate(src));
Movd(dst, kScratchRegister);
}
}
}
void MacroAssembler::Move(XMMRegister dst, uint64_t src) {
if (src == 0) {
Xorpd(dst, dst);
} else {
unsigned nlz = base::bits::CountLeadingZeros(src);
unsigned ntz = base::bits::CountTrailingZeros(src);
unsigned pop = base::bits::CountPopulation(src);
DCHECK_NE(0u, pop);
if (pop + ntz + nlz == 64) {
Pcmpeqd(dst, dst);
if (ntz) Psllq(dst, static_cast<uint8_t>(ntz + nlz));
if (nlz) Psrlq(dst, static_cast<uint8_t>(nlz));
} else {
uint32_t lower = static_cast<uint32_t>(src);
uint32_t upper = static_cast<uint32_t>(src >> 32);
if (upper == 0) {
Move(dst, lower);
} else {
movq(kScratchRegister, src);
Movq(dst, kScratchRegister);
}
}
}
}
void MacroAssembler::Move(XMMRegister dst, uint64_t high, uint64_t low) {
if (high == low) {
Move(dst, low);
Punpcklqdq(dst, dst);
return;
}
Move(dst, low);
movq(kScratchRegister, high);
Pinsrq(dst, dst, kScratchRegister, uint8_t{1});
}
void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
if (IsSmi(*source)) {
Cmp(dst, Cast<Smi>(*source));
} else if (root_array_available_ && options().isolate_independent_code) {
IndirectLoadConstant(kScratchRegister, Cast<HeapObject>(source));
cmp_tagged(dst, kScratchRegister);
} else if (COMPRESS_POINTERS_BOOL) {
EmbeddedObjectIndex index = AddEmbeddedObject(Cast<HeapObject>(source));
DCHECK(is_uint32(index));
cmpl(dst, Immediate(static_cast<int>(index),
RelocInfo::COMPRESSED_EMBEDDED_OBJECT));
} else {
movq(kScratchRegister,
Immediate64(source.address(), RelocInfo::FULL_EMBEDDED_OBJECT));
cmpq(dst, kScratchRegister);
}
}
void MacroAssembler::Cmp(Operand dst, Handle<Object> source) {
if (IsSmi(*source)) {
Cmp(dst, Cast<Smi>(*source));
} else if (root_array_available_ && options().isolate_independent_code) {
IndirectLoadConstant(kScratchRegister, Cast<HeapObject>(source));
cmp_tagged(dst, kScratchRegister);
} else if (COMPRESS_POINTERS_BOOL) {
EmbeddedObjectIndex index = AddEmbeddedObject(Cast<HeapObject>(source));
DCHECK(is_uint32(index));
cmpl(dst, Immediate(static_cast<int>(index),
RelocInfo::COMPRESSED_EMBEDDED_OBJECT));
} else {
Move(kScratchRegister, Cast<HeapObject>(source),
RelocInfo::FULL_EMBEDDED_OBJECT);
cmp_tagged(dst, kScratchRegister);
}
}
void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
unsigned higher_limit) {
ASM_CODE_COMMENT(this);
DCHECK_LT(lower_limit, higher_limit);
if (lower_limit != 0) {
leal(kScratchRegister, Operand(value, 0u - lower_limit));
cmpl(kScratchRegister, Immediate(higher_limit - lower_limit));
} else {
cmpl(value, Immediate(higher_limit));
}
}
void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
unsigned higher_limit, Label* on_in_range,
Label::Distance near_jump) {
CompareRange(value, lower_limit, higher_limit);
j(below_equal, on_in_range, near_jump);
}
void MacroAssembler::Push(Handle<HeapObject> source) {
Move(kScratchRegister, source);
Push(kScratchRegister);
}
void MacroAssembler::PushArray(Register array, Register size, Register scratch,
PushArrayOrder order) {
DCHECK(!AreAliased(array, size, scratch));
Register counter = scratch;
Label loop, entry;
if (order == PushArrayOrder::kReverse) {
Move(counter, 0);
jmp(&entry);
bind(&loop);
Push(Operand(array, counter, times_system_pointer_size, 0));
incq(counter);
bind(&entry);
cmpq(counter, size);
j(less, &loop, Label::kNear);
} else {
movq(counter, size);
jmp(&entry);
bind(&loop);
Push(Operand(array, counter, times_system_pointer_size, 0));
bind(&entry);
decq(counter);
j(greater_equal, &loop, Label::kNear);
}
}
void MacroAssembler::Move(Register result, Handle<HeapObject> object,
RelocInfo::Mode rmode) {
if (root_array_available_ && options().isolate_independent_code) {
IndirectLoadConstant(result, object);
} else if (RelocInfo::IsCompressedEmbeddedObject(rmode)) {
EmbeddedObjectIndex index = AddEmbeddedObject(object);
DCHECK(is_uint32(index));
movl(result, Immediate(static_cast<int>(index), rmode));
} else {
DCHECK(RelocInfo::IsFullEmbeddedObject(rmode));
movq(result, Immediate64(object.address(), rmode));
}
}
void MacroAssembler::Move(Operand dst, Handle<HeapObject> object,
RelocInfo::Mode rmode) {
Move(kScratchRegister, object, rmode);
movq(dst, kScratchRegister);
}
void MacroAssembler::Drop(int stack_elements) {
if (stack_elements > 0) {
addq(rsp, Immediate(stack_elements * kSystemPointerSize));
}
}
void MacroAssembler::DropUnderReturnAddress(int stack_elements,
Register scratch) {
DCHECK_GT(stack_elements, 0);
if (stack_elements == 1) {
popq(MemOperand(rsp, 0));
return;
}
PopReturnAddressTo(scratch);
Drop(stack_elements);
PushReturnAddressFrom(scratch);
}
void MacroAssembler::DropArguments(Register count) {
leaq(rsp, Operand(rsp, count, times_system_pointer_size, 0));
}
void MacroAssembler::DropArguments(Register count, Register scratch) {
DCHECK(!AreAliased(count, scratch));
PopReturnAddressTo(scratch);
DropArguments(count);
PushReturnAddressFrom(scratch);
}
void MacroAssembler::DropArgumentsAndPushNewReceiver(Register argc,
Register receiver,
Register scratch) {
DCHECK(!AreAliased(argc, receiver, scratch));
PopReturnAddressTo(scratch);
DropArguments(argc);
Push(receiver);
PushReturnAddressFrom(scratch);
}
void MacroAssembler::DropArgumentsAndPushNewReceiver(Register argc,
Operand receiver,
Register scratch) {
DCHECK(!AreAliased(argc, scratch));
DCHECK(!receiver.AddressUsesRegister(scratch));
PopReturnAddressTo(scratch);
DropArguments(argc);
Push(receiver);
PushReturnAddressFrom(scratch);
}
void MacroAssembler::Push(Register src) { pushq(src); }
void MacroAssembler::Push(Operand src) { pushq(src); }
void MacroAssembler::PushQuad(Operand src) { pushq(src); }
void MacroAssembler::Push(Immediate value) { pushq(value); }
void MacroAssembler::PushImm32(int32_t imm32) { pushq_imm32(imm32); }
void MacroAssembler::Pop(Register dst) { popq(dst); }
void MacroAssembler::Pop(Operand dst) { popq(dst); }
void MacroAssembler::PopQuad(Operand dst) { popq(dst); }
void MacroAssembler::Jump(const ExternalReference& reference) {
DCHECK(root_array_available());
jmp(Operand(kRootRegister, RootRegisterOffsetForExternalReferenceTableEntry(
isolate(), reference)));
}
void MacroAssembler::Jump(Operand op) { jmp(op); }
void MacroAssembler::Jump(Operand op, Condition cc) {
Label skip;
j(NegateCondition(cc), &skip, Label::kNear);
Jump(op);
bind(&skip);
}
void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) {
Move(kScratchRegister, destination, rmode);
jmp(kScratchRegister);
}
void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode,
Condition cc) {
Label skip;
j(NegateCondition(cc), &skip, Label::kNear);
Jump(destination, rmode);
bind(&skip);
}
void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
DCHECK_EQ(sandboxing_mode(), code_object->sandboxing_mode());
DCHECK_IMPLIES(options().isolate_independent_code,
Builtins::IsIsolateIndependentBuiltin(*code_object));
Builtin builtin = Builtin::kNoBuiltinId;
if (isolate()->builtins()->IsBuiltinHandle(code_object, &builtin)) {
TailCallBuiltin(builtin);
return;
}
DCHECK(RelocInfo::IsCodeTarget(rmode));
jmp(code_object, rmode);
}
void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode,
Condition cc) {
DCHECK_EQ(sandboxing_mode(), code_object->sandboxing_mode());
DCHECK_IMPLIES(options().isolate_independent_code,
Builtins::IsIsolateIndependentBuiltin(*code_object));
Builtin builtin = Builtin::kNoBuiltinId;
if (isolate()->builtins()->IsBuiltinHandle(code_object, &builtin)) {
TailCallBuiltin(builtin, cc);
return;
}
DCHECK(RelocInfo::IsCodeTarget(rmode));
j(cc, code_object, rmode);
}
void MacroAssembler::Call(ExternalReference ext) {
LoadAddress(kScratchRegister, ext);
call(kScratchRegister);
}
void MacroAssembler::Call(Operand op) {
if (!CpuFeatures::IsSupported(INTEL_ATOM)) {
call(op);
} else {
movq(kScratchRegister, op);
call(kScratchRegister);
}
}
void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
Move(kScratchRegister, destination, rmode);
call(kScratchRegister);
}
void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) {
DCHECK_IMPLIES(options().isolate_independent_code,
Builtins::IsIsolateIndependentBuiltin(*code_object));
Builtin builtin = Builtin::kNoBuiltinId;
if (isolate()->builtins()->IsBuiltinHandle(code_object, &builtin)) {
CallBuiltin(builtin);
return;
}
DCHECK_EQ(sandboxing_mode(), code_object->sandboxing_mode());
DCHECK(RelocInfo::IsCodeTarget(rmode));
call(code_object, rmode);
}
Operand MacroAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
DCHECK(root_array_available());
return Operand(kRootRegister, IsolateData::BuiltinEntrySlotOffset(builtin));
}
Operand MacroAssembler::EntryFromBuiltinIndexAsOperand(Register builtin_index) {
if (SmiValuesAre32Bits()) {
Move(kScratchRegister, builtin_index);
SmiUntagUnsigned(kScratchRegister);
return Operand(kRootRegister, kScratchRegister, times_system_pointer_size,
IsolateData::builtin_entry_table_offset());
} else {
DCHECK(SmiValuesAre31Bits());
return Operand(kRootRegister, builtin_index, times_half_system_pointer_size,
IsolateData::builtin_entry_table_offset());
}
}
void MacroAssembler::CallBuiltinByIndex(Register builtin_index) {
Call(EntryFromBuiltinIndexAsOperand(builtin_index));
}
void MacroAssembler::CallBuiltin(Builtin builtin) {
ASM_CODE_COMMENT_STRING(this, CommentForOffHeapTrampoline("call", builtin));
CodeSandboxingMode previous_mode = SwitchSandboxingModeBeforeCallIfNeeded(
Builtins::SandboxingModeOf(builtin));
switch (options().builtin_call_jump_mode) {
case BuiltinCallJumpMode::kAbsolute:
Call(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET);
break;
case BuiltinCallJumpMode::kPCRelative:
near_call(static_cast<intptr_t>(builtin), RelocInfo::NEAR_BUILTIN_ENTRY);
break;
case BuiltinCallJumpMode::kIndirect:
Call(EntryFromBuiltinAsOperand(builtin));
break;
case BuiltinCallJumpMode::kForMksnapshot: {
Handle<Code> code = isolate()->builtins()->code_handle(builtin);
call(code, RelocInfo::CODE_TARGET);
break;
}
}
SwitchSandboxingModeAfterCallIfNeeded(previous_mode);
}
void MacroAssembler::TailCallBuiltin(Builtin builtin) {
DCHECK_EQ(sandboxing_mode(), Builtins::SandboxingModeOf(builtin));
ASM_CODE_COMMENT_STRING(this,
CommentForOffHeapTrampoline("tail call", builtin));
switch (options().builtin_call_jump_mode) {
case BuiltinCallJumpMode::kAbsolute:
Jump(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET);
break;
case BuiltinCallJumpMode::kPCRelative:
near_jmp(static_cast<intptr_t>(builtin), RelocInfo::NEAR_BUILTIN_ENTRY);
break;
case BuiltinCallJumpMode::kIndirect:
Jump(EntryFromBuiltinAsOperand(builtin));
break;
case BuiltinCallJumpMode::kForMksnapshot: {
Handle<Code> code = isolate()->builtins()->code_handle(builtin);
jmp(code, RelocInfo::CODE_TARGET);
break;
}
}
}
void MacroAssembler::TailCallBuiltin(Builtin builtin, Condition cc) {
DCHECK_EQ(sandboxing_mode(), Builtins::SandboxingModeOf(builtin));
ASM_CODE_COMMENT_STRING(this,
CommentForOffHeapTrampoline("tail call", builtin));
switch (options().builtin_call_jump_mode) {
case BuiltinCallJumpMode::kAbsolute:
Jump(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET, cc);
break;
case BuiltinCallJumpMode::kPCRelative:
near_j(cc, static_cast<intptr_t>(builtin), RelocInfo::NEAR_BUILTIN_ENTRY);
break;
case BuiltinCallJumpMode::kIndirect:
Jump(EntryFromBuiltinAsOperand(builtin), cc);
break;
case BuiltinCallJumpMode::kForMksnapshot: {
Handle<Code> code = isolate()->builtins()->code_handle(builtin);
j(cc, code, RelocInfo::CODE_TARGET);
break;
}
}
}
void MacroAssembler::LoadCodeInstructionStart(Register destination,
Register code_object,
CodeEntrypointTag tag) {
ASM_CODE_COMMENT(this);
#ifdef V8_ENABLE_SANDBOX
LoadCodeEntrypointViaCodePointer(
destination, FieldOperand(code_object, Code::kSelfIndirectPointerOffset),
tag);
#else
movq(destination, FieldOperand(code_object, Code::kInstructionStartOffset));
#endif
}
void MacroAssembler::CallCodeObject(Register code_object,
CodeEntrypointTag tag) {
LoadCodeInstructionStart(code_object, code_object, tag);
call(code_object);
}
void MacroAssembler::JumpCodeObject(Register code_object, CodeEntrypointTag tag,
JumpMode jump_mode) {
LoadCodeInstructionStart(code_object, code_object, tag);
switch (jump_mode) {
case JumpMode::kJump:
jmp(code_object);
return;
case JumpMode::kPushAndReturn:
pushq(code_object);
Ret();
return;
}
}
void MacroAssembler::CallJSFunction(Register function_object,
uint16_t argument_count) {
static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch");
static_assert(kJavaScriptCallDispatchHandleRegister == r15, "ABI mismatch");
movl(r15, FieldOperand(function_object, JSFunction::kDispatchHandleOffset));
LoadEntrypointAndParameterCountFromJSDispatchTable(rcx, rbx, r15);
cmpl(rbx, Immediate(argument_count));
SbxCheck(less_equal, AbortReason::kJSSignatureMismatch);
call(rcx);
}
void MacroAssembler::CallJSDispatchEntry(JSDispatchHandle dispatch_handle,
uint16_t argument_count) {
static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch");
static_assert(kJavaScriptCallDispatchHandleRegister == r15, "ABI mismatch");
movl(kJavaScriptCallDispatchHandleRegister,
Immediate(dispatch_handle.value(), RelocInfo::JS_DISPATCH_HANDLE));
LoadEntrypointFromJSDispatchTable(rcx, kJavaScriptCallDispatchHandleRegister);
CHECK_EQ(argument_count,
IsolateGroup::current()->js_dispatch_table()->GetParameterCount(
dispatch_handle));
call(rcx);
}
void MacroAssembler::JumpJSFunction(Register function_object,
JumpMode jump_mode) {
CHECK(!V8_ENABLE_SANDBOX_BOOL);
UNREACHABLE();
}
#ifdef V8_ENABLE_WEBASSEMBLY
void MacroAssembler::CallWasmCodePointer(Register target,
uint64_t signature_hash,
CallJumpMode call_jump_mode) {
ASM_CODE_COMMENT(this);
Move(kScratchRegister, ExternalReference::wasm_code_pointer_table());
#ifdef V8_ENABLE_SANDBOX
static constexpr int kLeftShift =
base::bits::WhichPowerOfTwo(sizeof(wasm::WasmCodePointerTableEntry));
static constexpr int kNumRelevantBits = base::bits::WhichPowerOfTwo(
wasm::WasmCodePointerTable::kMaxWasmCodePointers);
static constexpr int kNumClearedHighBits = 32 - kNumRelevantBits;
shll(target, Immediate(kNumClearedHighBits));
shrl(target, Immediate(kNumClearedHighBits - kLeftShift));
addq(target, kScratchRegister);
Operand signature_hash_op{target,
wasm::WasmCodePointerTable::kOffsetOfSignatureHash};
if (is_int32(signature_hash)) {
cmpq(signature_hash_op, Immediate(static_cast<int32_t>(signature_hash)));
} else {
Move(kScratchRegister, signature_hash);
cmpq(kScratchRegister, signature_hash_op);
}
Label fail, ok;
j(Condition::kNotEqual, &fail, Label::Distance::kNear);
jmp(&ok, Label::Distance::kNear);
bind(&fail);
Abort(AbortReason::kWasmSignatureMismatch);
bind(&ok);
Operand target_op{target, 0};
#else
static_assert(sizeof(wasm::WasmCodePointerTableEntry) == 8);
Operand target_op{kScratchRegister, target, ScaleFactor::times_8, 0};
#endif
if (call_jump_mode == CallJumpMode::kTailCall) {
jmp(target_op);
} else {
call(target_op);
}
}
void MacroAssembler::CallWasmCodePointerNoSignatureCheck(Register target) {
Move(kScratchRegister, ExternalReference::wasm_code_pointer_table());
#ifdef V8_ENABLE_SANDBOX
static constexpr int kLeftShift =
base::bits::WhichPowerOfTwo(sizeof(wasm::WasmCodePointerTableEntry));
static constexpr int kNumRelevantBits = base::bits::WhichPowerOfTwo(
wasm::WasmCodePointerTable::kMaxWasmCodePointers);
static constexpr int kNumClearedHighBits = 32 - kNumRelevantBits;
static_assert(kNumClearedHighBits == 9);
shll(target, Immediate(kNumClearedHighBits));
shrl(target, Immediate(kNumClearedHighBits - kLeftShift));
call(Operand(kScratchRegister, target, ScaleFactor::times_1, 0));
#else
static_assert(sizeof(wasm::WasmCodePointerTableEntry) == 8);
call(Operand(kScratchRegister, target, ScaleFactor::times_8, 0));
#endif
}
void MacroAssembler::LoadWasmCodePointer(Register dst, Operand src) {
static_assert(sizeof(WasmCodePointer) == 4);
movl(dst, src);
}
#endif
void MacroAssembler::PextrdPreSse41(Register dst, XMMRegister src,
uint8_t imm8) {
if (imm8 == 0) {
Movd(dst, src);
return;
}
DCHECK_EQ(1, imm8);
movq(dst, src);
shrq(dst, Immediate(32));
}
namespace {
template <typename Op>
void PinsrdPreSse41Helper(MacroAssembler* masm, XMMRegister dst, Op src,
uint8_t imm8, uint32_t* load_pc_offset) {
masm->Movd(kScratchDoubleReg, src);
if (load_pc_offset) *load_pc_offset = masm->pc_offset();
if (imm8 == 1) {
masm->punpckldq(dst, kScratchDoubleReg);
} else {
DCHECK_EQ(0, imm8);
masm->Movss(dst, kScratchDoubleReg);
}
}
}
void MacroAssembler::PinsrdPreSse41(XMMRegister dst, Register src, uint8_t imm8,
uint32_t* load_pc_offset) {
PinsrdPreSse41Helper(this, dst, src, imm8, load_pc_offset);
}
void MacroAssembler::PinsrdPreSse41(XMMRegister dst, Operand src, uint8_t imm8,
uint32_t* load_pc_offset) {
PinsrdPreSse41Helper(this, dst, src, imm8, load_pc_offset);
}
void MacroAssembler::Pinsrq(XMMRegister dst, XMMRegister src1, Register src2,
uint8_t imm8, uint32_t* load_pc_offset) {
PinsrHelper(this, &Assembler::vpinsrq, &Assembler::pinsrq, dst, src1, src2,
imm8, load_pc_offset, {SSE4_1});
}
void MacroAssembler::Pinsrq(XMMRegister dst, XMMRegister src1, Operand src2,
uint8_t imm8, uint32_t* load_pc_offset) {
PinsrHelper(this, &Assembler::vpinsrq, &Assembler::pinsrq, dst, src1, src2,
imm8, load_pc_offset, {SSE4_1});
}
void MacroAssembler::Lzcntl(Register dst, Register src) {
if (CpuFeatures::IsSupported(LZCNT)) {
CpuFeatureScope scope(this, LZCNT);
lzcntl(dst, src);
return;
}
Label not_zero_src;
bsrl(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 63);
bind(¬_zero_src);
xorl(dst, Immediate(31));
}
void MacroAssembler::Lzcntl(Register dst, Operand src) {
if (CpuFeatures::IsSupported(LZCNT)) {
CpuFeatureScope scope(this, LZCNT);
lzcntl(dst, src);
return;
}
Label not_zero_src;
bsrl(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 63);
bind(¬_zero_src);
xorl(dst, Immediate(31));
}
void MacroAssembler::Lzcntq(Register dst, Register src) {
if (CpuFeatures::IsSupported(LZCNT)) {
CpuFeatureScope scope(this, LZCNT);
lzcntq(dst, src);
return;
}
Label not_zero_src;
bsrq(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 127);
bind(¬_zero_src);
xorl(dst, Immediate(63));
}
void MacroAssembler::Lzcntq(Register dst, Operand src) {
if (CpuFeatures::IsSupported(LZCNT)) {
CpuFeatureScope scope(this, LZCNT);
lzcntq(dst, src);
return;
}
Label not_zero_src;
bsrq(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 127);
bind(¬_zero_src);
xorl(dst, Immediate(63));
}
void MacroAssembler::Tzcntq(Register dst, Register src) {
if (CpuFeatures::IsSupported(BMI1)) {
CpuFeatureScope scope(this, BMI1);
tzcntq(dst, src);
return;
}
Label not_zero_src;
bsfq(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 64);
bind(¬_zero_src);
}
void MacroAssembler::Tzcntq(Register dst, Operand src) {
if (CpuFeatures::IsSupported(BMI1)) {
CpuFeatureScope scope(this, BMI1);
tzcntq(dst, src);
return;
}
Label not_zero_src;
bsfq(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 64);
bind(¬_zero_src);
}
void MacroAssembler::Tzcntl(Register dst, Register src) {
if (CpuFeatures::IsSupported(BMI1)) {
CpuFeatureScope scope(this, BMI1);
tzcntl(dst, src);
return;
}
Label not_zero_src;
bsfl(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 32);
bind(¬_zero_src);
}
void MacroAssembler::Tzcntl(Register dst, Operand src) {
if (CpuFeatures::IsSupported(BMI1)) {
CpuFeatureScope scope(this, BMI1);
tzcntl(dst, src);
return;
}
Label not_zero_src;
bsfl(dst, src);
j(not_zero, ¬_zero_src, Label::kNear);
Move(dst, 32);
bind(¬_zero_src);
}
void MacroAssembler::Popcntl(Register dst, Register src) {
if (CpuFeatures::IsSupported(POPCNT)) {
CpuFeatureScope scope(this, POPCNT);
popcntl(dst, src);
return;
}
UNREACHABLE();
}
void MacroAssembler::Popcntl(Register dst, Operand src) {
if (CpuFeatures::IsSupported(POPCNT)) {
CpuFeatureScope scope(this, POPCNT);
popcntl(dst, src);
return;
}
UNREACHABLE();
}
void MacroAssembler::Popcntq(Register dst, Register src) {
if (CpuFeatures::IsSupported(POPCNT)) {
CpuFeatureScope scope(this, POPCNT);
popcntq(dst, src);
return;
}
UNREACHABLE();
}
void MacroAssembler::Popcntq(Register dst, Operand src) {
if (CpuFeatures::IsSupported(POPCNT)) {
CpuFeatureScope scope(this, POPCNT);
popcntq(dst, src);
return;
}
UNREACHABLE();
}
void MacroAssembler::PushStackHandler() {
static_assert(StackHandlerConstants::kSize == 2 * kSystemPointerSize);
static_assert(StackHandlerConstants::kNextOffset == 0);
Push(Immediate(0));
ExternalReference handler_address =
ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate());
Push(ExternalReferenceAsOperand(handler_address));
movq(ExternalReferenceAsOperand(handler_address), rsp);
}
void MacroAssembler::PopStackHandler() {
static_assert(StackHandlerConstants::kNextOffset == 0);
ExternalReference handler_address =
ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate());
Pop(ExternalReferenceAsOperand(handler_address));
addq(rsp, Immediate(StackHandlerConstants::kSize - kSystemPointerSize));
}
void MacroAssembler::Ret() { ret(0); }
void MacroAssembler::Ret(int bytes_dropped, Register scratch) {
if (is_uint16(bytes_dropped)) {
ret(bytes_dropped);
} else {
PopReturnAddressTo(scratch);
addq(rsp, Immediate(bytes_dropped));
PushReturnAddressFrom(scratch);
ret(0);
}
}
void MacroAssembler::IncsspqIfSupported(Register number_of_words,
Register scratch) {
CHECK(isolate()->IsGeneratingEmbeddedBuiltins());
DCHECK_NE(number_of_words, scratch);
Label not_supported;
ExternalReference supports_cetss =
ExternalReference::supports_cetss_address();
Operand supports_cetss_operand =
ExternalReferenceAsOperand(supports_cetss, scratch);
cmpb(supports_cetss_operand, Immediate(0));
j(equal, ¬_supported, Label::kNear);
incsspq(number_of_words);
bind(¬_supported);
}
#if V8_STATIC_ROOTS_BOOL
void MacroAssembler::CompareInstanceTypeWithUniqueCompressedMap(
Register map, InstanceType type) {
std::optional<RootIndex> expected =
InstanceTypeChecker::UniqueMapOfInstanceType(type);
CHECK(expected);
Tagged_t expected_ptr = ReadOnlyRootPtr(*expected);
cmp_tagged(map, Immediate(expected_ptr));
}
void MacroAssembler::IsObjectTypeFast(Register object, InstanceType type,
Register compressed_map_scratch) {
ASM_CODE_COMMENT(this);
CHECK(InstanceTypeChecker::UniqueMapOfInstanceType(type));
LoadCompressedMap(compressed_map_scratch, object);
CompareInstanceTypeWithUniqueCompressedMap(compressed_map_scratch, type);
}
#endif
void MacroAssembler::IsObjectType(Register heap_object, InstanceType type,
Register map) {
#if V8_STATIC_ROOTS_BOOL
if (InstanceTypeChecker::UniqueMapOfInstanceType(type)) {
LoadCompressedMap(map, heap_object);
CompareInstanceTypeWithUniqueCompressedMap(map, type);
return;
}
#endif
CmpObjectType(heap_object, type, map);
}
void MacroAssembler::IsObjectTypeInRange(Register heap_object,
InstanceType lower_limit,
InstanceType higher_limit,
Register scratch) {
DCHECK_LT(lower_limit, higher_limit);
#if V8_STATIC_ROOTS_BOOL
if (auto range = InstanceTypeChecker::UniqueMapRangeOfInstanceTypeRange(
lower_limit, higher_limit)) {
LoadCompressedMap(scratch, heap_object);
CompareRange(scratch, range->first, range->second);
return;
}
#endif
LoadMap(scratch, heap_object);
CmpInstanceTypeRange(scratch, scratch, lower_limit, higher_limit);
}
void MacroAssembler::JumpIfJSAnyIsNotPrimitive(Register heap_object,
Register scratch, Label* target,
Label::Distance distance,
Condition cc) {
CHECK(cc == Condition::kUnsignedLessThan ||
cc == Condition::kUnsignedGreaterThanEqual);
if (V8_STATIC_ROOTS_BOOL) {
#ifdef DEBUG
Label ok;
LoadMap(scratch, heap_object);
CmpInstanceTypeRange(scratch, scratch, FIRST_JS_RECEIVER_TYPE,
LAST_JS_RECEIVER_TYPE);
j(Condition::kUnsignedLessThanEqual, &ok, Label::Distance::kNear);
LoadMap(scratch, heap_object);
CmpInstanceTypeRange(scratch, scratch, FIRST_PRIMITIVE_HEAP_OBJECT_TYPE,
LAST_PRIMITIVE_HEAP_OBJECT_TYPE);
j(Condition::kUnsignedLessThanEqual, &ok, Label::Distance::kNear);
Abort(AbortReason::kInvalidReceiver);
bind(&ok);
#endif
LoadCompressedMap(scratch, heap_object);
cmp_tagged(scratch, Immediate(InstanceTypeChecker::kNonJsReceiverMapLimit));
} else {
static_assert(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
CmpObjectType(heap_object, FIRST_JS_RECEIVER_TYPE, scratch);
}
j(cc, target, distance);
}
void MacroAssembler::CmpObjectType(Register heap_object, InstanceType type,
Register map) {
LoadMap(map, heap_object);
CmpInstanceType(map, type);
}
void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
cmpw(FieldOperand(map, Map::kInstanceTypeOffset), Immediate(type));
}
void MacroAssembler::CmpInstanceTypeRange(Register map,
Register instance_type_out,
InstanceType lower_limit,
InstanceType higher_limit) {
DCHECK_LT(lower_limit, higher_limit);
movzxwl(instance_type_out, FieldOperand(map, Map::kInstanceTypeOffset));
CompareRange(instance_type_out, lower_limit, higher_limit);
}
void MacroAssembler::TestCodeIsMarkedForDeoptimization(Register code) {
const int kByteWithDeoptBitOffset = 0 * kByteSize;
const int kByteWithDeoptBitOffsetInBits = kByteWithDeoptBitOffset * 8;
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
static_assert(FIELD_SIZE(Code::kFlagsOffset) * kBitsPerByte == 32);
static_assert(Code::kMarkedForDeoptimizationBit >
kByteWithDeoptBitOffsetInBits);
testb(FieldOperand(code, Code::kFlagsOffset + kByteWithDeoptBitOffset),
Immediate(1 << (Code::kMarkedForDeoptimizationBit -
kByteWithDeoptBitOffsetInBits)));
}
void MacroAssembler::TestCodeIsTurbofanned(Register code) {
testl(FieldOperand(code, Code::kFlagsOffset),
Immediate(1 << Code::kIsTurbofannedBit));
}
Immediate MacroAssembler::ClearedValue() const {
return Immediate(static_cast<int32_t>(i::kClearedWeakValue.ptr()));
}
#ifdef V8_ENABLE_DEBUG_CODE
void MacroAssembler::AssertNotSmi(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
Condition is_smi = CheckSmi(object);
Check(NegateCondition(is_smi), AbortReason::kOperandIsASmi);
}
void MacroAssembler::AssertSmi(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
Condition is_smi = CheckSmi(object);
Check(is_smi, AbortReason::kOperandIsNotASmi);
#ifdef ENABLE_SLOW_DCHECKS
ClobberDecompressedSmiBits(object);
#endif
}
void MacroAssembler::AssertSmi(Operand object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
Condition is_smi = CheckSmi(object);
Check(is_smi, AbortReason::kOperandIsNotASmi);
}
void MacroAssembler::AssertZeroExtended(Register int32_register) {
if (!v8_flags.slow_debug_code) return;
ASM_CODE_COMMENT(this);
DCHECK_NE(int32_register, kScratchRegister);
movl(kScratchRegister, Immediate(kMaxUInt32));
cmpq(int32_register, kScratchRegister);
Check(below_equal, AbortReason::k32BitValueInRegisterIsNotZeroExtended);
}
void MacroAssembler::AssertSignBitOfSmiIsZero(Register smi_register) {
if (!v8_flags.slow_debug_code) return;
ASM_CODE_COMMENT(this);
DCHECK(COMPRESS_POINTERS_BOOL);
constexpr int kSmiShiftBits = kSmiTagSize + kSmiShiftSize;
constexpr Tagged_t kSmiSignBit = Tagged_t{1}
<< (kSmiShiftBits + kSmiValueSize - 1);
testl(smi_register, Immediate(static_cast<uint32_t>(kSmiSignBit)));
Check(zero, AbortReason::kSignBitOfSmiIsNotZero);
}
void MacroAssembler::AssertMap(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsNotAMap);
Push(object);
LoadMap(object, object);
CmpInstanceType(object, MAP_TYPE);
popq(object);
Check(equal, AbortReason::kOperandIsNotAMap);
}
void MacroAssembler::AssertCode(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsNotACode);
Push(object);
LoadMap(object, object);
CmpInstanceType(object, CODE_TYPE);
popq(object);
Check(equal, AbortReason::kOperandIsNotACode);
}
void MacroAssembler::AssertSmiOrHeapObjectInMainCompressionCage(
Register object) {
if (!PointerCompressionIsEnabled()) return;
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
Label ok;
pushq(object);
j(CheckSmi(object), &ok);
shrq(object, Immediate(32));
shlq(object, Immediate(32));
j(zero, &ok);
cmpq(object, kPtrComprCageBaseRegister);
Check(equal, AbortReason::kObjectNotTagged);
bind(&ok);
popq(object);
}
void MacroAssembler::AssertConstructor(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsASmiAndNotAConstructor);
Push(object);
LoadMap(object, object);
testb(FieldOperand(object, Map::kBitFieldOffset),
Immediate(Map::Bits1::IsConstructorBit::kMask));
Pop(object);
Check(not_zero, AbortReason::kOperandIsNotAConstructor);
}
void MacroAssembler::AssertFunction(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsASmiAndNotAFunction);
Push(object);
LoadMap(object, object);
CmpInstanceTypeRange(object, object, FIRST_JS_FUNCTION_TYPE,
LAST_JS_FUNCTION_TYPE);
Pop(object);
Check(below_equal, AbortReason::kOperandIsNotAFunction);
}
void MacroAssembler::AssertCallableFunction(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsASmiAndNotAFunction);
Push(object);
LoadMap(object, object);
CmpInstanceTypeRange(object, object, FIRST_CALLABLE_JS_FUNCTION_TYPE,
LAST_CALLABLE_JS_FUNCTION_TYPE);
Pop(object);
Check(below_equal, AbortReason::kOperandIsNotACallableFunction);
}
void MacroAssembler::AssertBoundFunction(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsASmiAndNotABoundFunction);
Push(object);
IsObjectType(object, JS_BOUND_FUNCTION_TYPE, object);
Pop(object);
Check(equal, AbortReason::kOperandIsNotABoundFunction);
}
void MacroAssembler::AssertGeneratorObject(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
testb(object, Immediate(kSmiTagMask));
Check(not_equal, AbortReason::kOperandIsASmiAndNotAGeneratorObject);
Register map = object;
Push(object);
LoadMap(map, object);
CmpInstanceTypeRange(map, kScratchRegister, FIRST_JS_GENERATOR_OBJECT_TYPE,
LAST_JS_GENERATOR_OBJECT_TYPE);
Pop(object);
Check(below_equal, AbortReason::kOperandIsNotAGeneratorObject);
}
void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
Label done_checking;
AssertNotSmi(object);
Cmp(object, isolate()->factory()->undefined_value());
j(equal, &done_checking);
Register map = object;
Push(object);
LoadMap(map, object);
Cmp(map, isolate()->factory()->allocation_site_map());
Pop(object);
Assert(equal, AbortReason::kExpectedUndefinedOrCell);
bind(&done_checking);
}
void MacroAssembler::AssertJSAny(Register object, Register map_tmp,
AbortReason abort_reason) {
if (!v8_flags.debug_code) return;
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, map_tmp));
Label ok;
Label::Distance dist = DEBUG_BOOL ? Label::kFar : Label::kNear;
JumpIfSmi(object, &ok, dist);
LoadMap(map_tmp, object);
CmpInstanceType(map_tmp, LAST_NAME_TYPE);
j(below_equal, &ok, dist);
CmpInstanceType(map_tmp, FIRST_JS_RECEIVER_TYPE);
j(above_equal, &ok, dist);
CompareRoot(map_tmp, RootIndex::kHeapNumberMap);
j(equal, &ok, dist);
CompareRoot(map_tmp, RootIndex::kBigIntMap);
j(equal, &ok, dist);
CompareRoot(object, RootIndex::kUndefinedValue);
j(equal, &ok, dist);
CompareRoot(object, RootIndex::kTrueValue);
j(equal, &ok, dist);
CompareRoot(object, RootIndex::kFalseValue);
j(equal, &ok, dist);
CompareRoot(object, RootIndex::kNullValue);
j(equal, &ok, dist);
Abort(abort_reason);
bind(&ok);
}
void MacroAssembler::Assert(Condition cc, AbortReason reason) {
if (v8_flags.debug_code) Check(cc, reason);
}
void MacroAssembler::AssertUnreachable(AbortReason reason) {
if (v8_flags.debug_code) Abort(reason);
}
#endif
void MacroAssembler::LoadWeakValue(Register in_out, Label* target_if_cleared) {
cmpl(in_out, Immediate(kClearedWeakHeapObjectLower32));
j(equal, target_if_cleared);
andq(in_out, Immediate(~static_cast<int32_t>(kWeakHeapObjectMask)));
}
void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value) {
DCHECK_GT(value, 0);
if (v8_flags.native_code_counters && counter->Enabled()) {
ASM_CODE_COMMENT(this);
Operand counter_operand =
ExternalReferenceAsOperand(ExternalReference::Create(counter));
if (value == 1) {
incl(counter_operand);
} else {
addl(counter_operand, Immediate(value));
}
}
}
void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value) {
DCHECK_GT(value, 0);
if (v8_flags.native_code_counters && counter->Enabled()) {
ASM_CODE_COMMENT(this);
Operand counter_operand =
ExternalReferenceAsOperand(ExternalReference::Create(counter));
if (value == 1) {
decl(counter_operand);
} else {
subl(counter_operand, Immediate(value));
}
}
}
void MacroAssembler::InvokeFunction(
Register function, Register new_target, Register actual_parameter_count,
InvokeType type, ArgumentAdaptionMode argument_adaption_mode) {
ASM_CODE_COMMENT(this);
DCHECK_EQ(function, rdi);
LoadTaggedField(rsi, FieldOperand(function, JSFunction::kContextOffset));
InvokeFunctionCode(rdi, new_target, actual_parameter_count, type,
argument_adaption_mode);
}
void MacroAssembler::InvokeFunctionCode(
Register function, Register new_target, Register actual_parameter_count,
InvokeType type, ArgumentAdaptionMode argument_adaption_mode) {
ASM_CODE_COMMENT(this);
DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
DCHECK_EQ(function, rdi);
DCHECK_IMPLIES(new_target.is_valid(), new_target == rdx);
Register dispatch_handle = kJavaScriptCallDispatchHandleRegister;
movl(dispatch_handle,
FieldOperand(function, JSFunction::kDispatchHandleOffset));
AssertFunction(function);
Label debug_hook, continue_after_hook;
{
ExternalReference debug_hook_active =
ExternalReference::debug_hook_on_function_call_address(isolate());
Operand debug_hook_active_operand =
ExternalReferenceAsOperand(debug_hook_active);
cmpb(debug_hook_active_operand, Immediate(0));
j(not_equal, &debug_hook);
}
bind(&continue_after_hook);
if (!new_target.is_valid()) {
LoadRoot(rdx, RootIndex::kUndefinedValue);
}
if (argument_adaption_mode == ArgumentAdaptionMode::kAdapt) {
Register expected_parameter_count = rbx;
LoadParameterCountFromJSDispatchTable(expected_parameter_count,
dispatch_handle);
InvokePrologue(expected_parameter_count, actual_parameter_count, type);
}
static_assert(kJavaScriptCallCodeStartRegister == rcx, "ABI mismatch");
LoadEntrypointFromJSDispatchTable(rcx, dispatch_handle);
switch (type) {
case InvokeType::kCall:
call(rcx);
break;
case InvokeType::kJump:
jmp(rcx);
break;
}
Label done;
jmp(&done, Label::kNear);
bind(&debug_hook);
CallDebugOnFunctionCall(function, new_target, dispatch_handle,
actual_parameter_count);
jmp(&continue_after_hook);
bind(&done);
}
Operand MacroAssembler::StackLimitAsOperand(StackLimitKind kind) {
DCHECK(root_array_available());
intptr_t offset = kind == StackLimitKind::kRealStackLimit
? IsolateData::real_jslimit_offset()
: IsolateData::jslimit_offset();
CHECK(is_int32(offset));
return Operand(kRootRegister, static_cast<int32_t>(offset));
}
void MacroAssembler::StackOverflowCheck(
Register num_args, Label* stack_overflow,
Label::Distance stack_overflow_distance) {
ASM_CODE_COMMENT(this);
DCHECK_NE(num_args, kScratchRegister);
movq(kScratchRegister, rsp);
subq(kScratchRegister, StackLimitAsOperand(StackLimitKind::kRealStackLimit));
sarq(kScratchRegister, Immediate(kSystemPointerSizeLog2));
cmpq(kScratchRegister, num_args);
j(less_equal, stack_overflow, stack_overflow_distance);
}
void MacroAssembler::InvokePrologue(Register expected_parameter_count,
Register actual_parameter_count,
InvokeType type) {
ASM_CODE_COMMENT(this);
if (expected_parameter_count == actual_parameter_count) {
Move(rax, actual_parameter_count);
return;
}
Label regular_invoke;
subq(expected_parameter_count, actual_parameter_count);
j(less_equal, ®ular_invoke, Label::kFar);
Label stack_overflow;
StackOverflowCheck(expected_parameter_count, &stack_overflow);
{
Label copy, check;
Register src = r8, dest = rsp, num = r9, current = r11;
movq(src, rsp);
leaq(kScratchRegister,
Operand(expected_parameter_count, times_system_pointer_size, 0));
AllocateStackSpace(kScratchRegister);
int extra_words =
type == InvokeType::kCall ? 0 : kReturnAddressStackSlotCount;
leaq(num, Operand(rax, extra_words));
Move(current, 0);
bind(©);
movq(kScratchRegister,
Operand(src, current, times_system_pointer_size, 0));
movq(Operand(dest, current, times_system_pointer_size, 0),
kScratchRegister);
incq(current);
bind(&check);
cmpq(current, num);
j(less, ©);
leaq(r8, Operand(rsp, num, times_system_pointer_size, 0));
}
LoadRoot(kScratchRegister, RootIndex::kUndefinedValue);
{
Label loop;
bind(&loop);
decq(expected_parameter_count);
movq(Operand(r8, expected_parameter_count, times_system_pointer_size, 0),
kScratchRegister);
j(greater, &loop, Label::kNear);
}
jmp(®ular_invoke);
bind(&stack_overflow);
{
FrameScope frame(
this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
CallRuntime(Runtime::kThrowStackOverflow);
int3();
}
bind(®ular_invoke);
}
void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target,
Register dispatch_handle,
Register actual_parameter_count) {
ASM_CODE_COMMENT(this);
movq(kScratchRegister, Operand(rsp, has_frame() ? 0 : kSystemPointerSize));
FrameScope frame(
this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
static_assert(kJSDispatchHandleShift >= 1);
Push(dispatch_handle);
SmiTag(actual_parameter_count);
Push(actual_parameter_count);
SmiUntag(actual_parameter_count);
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
Push(kScratchRegister);
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
Pop(actual_parameter_count);
SmiUntag(actual_parameter_count);
Pop(dispatch_handle);
}
void MacroAssembler::StubPrologue(StackFrame::Type type) {
ASM_CODE_COMMENT(this);
pushq(rbp);
movq(rbp, rsp);
Push(Immediate(StackFrame::TypeToMarker(type)));
}
void MacroAssembler::Prologue() {
ASM_CODE_COMMENT(this);
pushq(rbp);
movq(rbp, rsp);
Push(kContextRegister);
Push(kJSFunctionRegister);
Push(kJavaScriptCallArgCountRegister);
}
void MacroAssembler::EnterFrame(StackFrame::Type type) {
ASM_CODE_COMMENT(this);
pushq(rbp);
movq(rbp, rsp);
if (!StackFrame::IsJavaScript(type)) {
static_assert(CommonFrameConstants::kContextOrFrameTypeOffset ==
-kSystemPointerSize);
Push(Immediate(StackFrame::TypeToMarker(type)));
}
#if V8_ENABLE_WEBASSEMBLY
if (type == StackFrame::WASM) Push(kWasmImplicitArgRegister);
#endif
}
void MacroAssembler::LeaveFrame(StackFrame::Type type) {
ASM_CODE_COMMENT(this);
if (v8_flags.debug_code && !StackFrame::IsJavaScript(type)) {
cmpq(Operand(rbp, CommonFrameConstants::kContextOrFrameTypeOffset),
Immediate(StackFrame::TypeToMarker(type)));
Check(equal, AbortReason::kStackFrameTypesMustMatch);
}
movq(rsp, rbp);
popq(rbp);
}
#if defined(V8_TARGET_OS_WIN) || defined(V8_TARGET_OS_MACOS)
void MacroAssembler::AllocateStackSpace(Register bytes_scratch) {
ASM_CODE_COMMENT(this);
Label check_offset;
Label touch_next_page;
jmp(&check_offset);
bind(&touch_next_page);
subq(rsp, Immediate(kStackPageSize));
movb(Operand(rsp, 0), Immediate(0));
subq(bytes_scratch, Immediate(kStackPageSize));
bind(&check_offset);
cmpq(bytes_scratch, Immediate(kStackPageSize));
j(greater_equal, &touch_next_page);
subq(rsp, bytes_scratch);
}
void MacroAssembler::AllocateStackSpace(int bytes) {
ASM_CODE_COMMENT(this);
DCHECK_GE(bytes, 0);
while (bytes >= kStackPageSize) {
subq(rsp, Immediate(kStackPageSize));
movb(Operand(rsp, 0), Immediate(0));
bytes -= kStackPageSize;
}
if (bytes == 0) return;
subq(rsp, Immediate(bytes));
}
#endif
void MacroAssembler::EnterExitFrame(int extra_slots,
StackFrame::Type frame_type,
Register c_function) {
ASM_CODE_COMMENT(this);
DCHECK(frame_type == StackFrame::EXIT ||
frame_type == StackFrame::BUILTIN_EXIT ||
frame_type == StackFrame::API_ACCESSOR_EXIT ||
frame_type == StackFrame::API_CALLBACK_EXIT);
DCHECK_EQ(kFPOnStackSize + kPCOnStackSize,
ExitFrameConstants::kCallerSPDisplacement);
DCHECK_EQ(kFPOnStackSize, ExitFrameConstants::kCallerPCOffset);
DCHECK_EQ(0 * kSystemPointerSize, ExitFrameConstants::kCallerFPOffset);
pushq(rbp);
movq(rbp, rsp);
Push(Immediate(StackFrame::TypeToMarker(frame_type)));
DCHECK_EQ(-2 * kSystemPointerSize, ExitFrameConstants::kSPOffset);
Push(Immediate(0));
DCHECK(!AreAliased(rbp, kContextRegister, c_function));
using ER = ExternalReference;
Store(ER::Create(IsolateAddressId::kCEntryFPAddress, isolate()), rbp);
Store(ER::Create(IsolateAddressId::kContextAddress, isolate()),
kContextRegister);
Store(ER::Create(IsolateAddressId::kCFunctionAddress, isolate()), c_function);
#ifdef V8_TARGET_OS_WIN
extra_slots += kWindowsHomeStackSlots;
#endif
AllocateStackSpace(extra_slots * kSystemPointerSize);
AlignStackPointer();
movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
}
void MacroAssembler::LeaveExitFrame() {
ASM_CODE_COMMENT(this);
leave();
ExternalReference context_address =
ExternalReference::Create(IsolateAddressId::kContextAddress, isolate());
Operand context_operand = ExternalReferenceAsOperand(context_address);
movq(rsi, context_operand);
#ifdef DEBUG
Move(context_operand, Context::kNoContext);
#endif
ExternalReference c_entry_fp_address =
ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate());
Operand c_entry_fp_operand = ExternalReferenceAsOperand(c_entry_fp_address);
Move(c_entry_fp_operand, 0);
}
void MacroAssembler::LoadNativeContextSlot(Register dst, int index) {
ASM_CODE_COMMENT(this);
LoadMap(dst, rsi);
LoadTaggedField(
dst,
FieldOperand(dst, Map::kConstructorOrBackPointerOrNativeContextOffset));
LoadTaggedField(dst, Operand(dst, Context::SlotOffset(index)));
}
void MacroAssembler::TryLoadOptimizedOsrCode(Register scratch_and_result,
CodeKind min_opt_level,
Register feedback_vector,
FeedbackSlot slot,
Label* on_result,
Label::Distance distance) {
ASM_CODE_COMMENT(this);
Label fallthrough, on_mark_deopt;
LoadTaggedField(
scratch_and_result,
FieldOperand(feedback_vector,
FeedbackVector::OffsetOfElementAt(slot.ToInt())));
LoadWeakValue(scratch_and_result, &fallthrough);
{
LoadCodePointerField(
scratch_and_result,
FieldOperand(scratch_and_result, CodeWrapper::kCodeOffset),
kScratchRegister);
TestCodeIsMarkedForDeoptimization(scratch_and_result);
if (min_opt_level == CodeKind::TURBOFAN_JS) {
j(not_zero, &on_mark_deopt, Label::Distance::kNear);
TestCodeIsTurbofanned(scratch_and_result);
j(not_zero, on_result, distance);
jmp(&fallthrough);
} else {
DCHECK_EQ(min_opt_level, CodeKind::MAGLEV);
j(equal, on_result, distance);
}
bind(&on_mark_deopt);
StoreTaggedField(
FieldOperand(feedback_vector,
FeedbackVector::OffsetOfElementAt(slot.ToInt())),
ClearedValue());
}
bind(&fallthrough);
Move(scratch_and_result, 0);
}
int MacroAssembler::ArgumentStackSlotsForCFunctionCall(int num_arguments) {
DCHECK_GE(num_arguments, 0);
#ifdef V8_TARGET_OS_WIN
return std::max(num_arguments, kWindowsHomeStackSlots);
#else
return std::max(num_arguments - kRegisterPassedArguments, 0);
#endif
}
void MacroAssembler::PrepareCallCFunction(int num_arguments) {
ASM_CODE_COMMENT(this);
int frame_alignment = base::OS::ActivationFrameAlignment();
DCHECK_NE(frame_alignment, 0);
DCHECK_GE(num_arguments, 0);
movq(kScratchRegister, rsp);
DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
int argument_slots_on_stack =
ArgumentStackSlotsForCFunctionCall(num_arguments);
AllocateStackSpace((argument_slots_on_stack + 1) * kSystemPointerSize);
andq(rsp, Immediate(-frame_alignment));
movq(Operand(rsp, argument_slots_on_stack * kSystemPointerSize),
kScratchRegister);
}
int MacroAssembler::CallCFunction(ExternalReference function, int num_arguments,
SetIsolateDataSlots set_isolate_data_slots,
Label* return_location) {
LoadAddress(rax, function);
return CallCFunction(rax, num_arguments, set_isolate_data_slots,
return_location, CodeSandboxingMode::kUnsandboxed);
}
int MacroAssembler::CallCFunction(Register function, int num_arguments,
SetIsolateDataSlots set_isolate_data_slots,
Label* return_location,
CodeSandboxingMode target_sandboxing_mode) {
ASM_CODE_COMMENT(this);
DCHECK_LE(num_arguments, kMaxCParameters);
DCHECK(has_frame());
if (v8_flags.debug_code) {
CheckStackAlignment();
}
CodeSandboxingMode previous_mode =
SwitchSandboxingModeBeforeCallIfNeeded(target_sandboxing_mode);
Label get_pc;
if (set_isolate_data_slots == SetIsolateDataSlots::kYes) {
DCHECK(!AreAliased(kScratchRegister, function));
leaq(kScratchRegister, Operand(&get_pc, 0));
CHECK(root_array_available());
movq(ExternalReferenceAsOperand(IsolateFieldId::kFastCCallCallerPC),
kScratchRegister);
movq(ExternalReferenceAsOperand(IsolateFieldId::kFastCCallCallerFP), rbp);
}
call(function);
int call_pc_offset = pc_offset();
bind(&get_pc);
if (return_location) bind(return_location);
DCHECK_NE(base::OS::ActivationFrameAlignment(), 0);
DCHECK_GE(num_arguments, 0);
int argument_slots_on_stack =
ArgumentStackSlotsForCFunctionCall(num_arguments);
movq(rsp, Operand(rsp, argument_slots_on_stack * kSystemPointerSize));
if (set_isolate_data_slots == SetIsolateDataSlots::kYes) {
movq(ExternalReferenceAsOperand(IsolateFieldId::kFastCCallCallerFP),
Immediate(0));
}
SwitchSandboxingModeAfterCallIfNeeded(previous_mode);
return call_pc_offset;
}
void MacroAssembler::MemoryChunkHeaderFromObject(Register object,
Register header) {
constexpr intptr_t alignment_mask =
MemoryChunk::GetAlignmentMaskForAssembler();
if (header == object) {
andq(header, Immediate(~alignment_mask));
} else {
movq(header, Immediate(~alignment_mask));
andq(header, object);
}
}
void MacroAssembler::CheckPageFlag(Register object, Register scratch, int mask,
Condition cc, Label* condition_met,
Label::Distance condition_met_distance) {
ASM_CODE_COMMENT(this);
DCHECK(cc == zero || cc == not_zero);
MemoryChunkHeaderFromObject(object, scratch);
if (mask < (1 << kBitsPerByte)) {
testb(Operand(scratch, MemoryChunk::FlagsOffset()),
Immediate(static_cast<uint8_t>(mask)));
} else {
testl(Operand(scratch, MemoryChunk::FlagsOffset()), Immediate(mask));
}
j(cc, condition_met, condition_met_distance);
}
void MacroAssembler::JumpIfMarking(Label* is_marking,
Label::Distance condition_met_distance) {
testb(Operand(kRootRegister, IsolateData::is_marking_flag_offset()),
Immediate(static_cast<uint8_t>(1)));
j(not_zero, is_marking, condition_met_distance);
}
void MacroAssembler::JumpIfNotMarking(Label* not_marking,
Label::Distance condition_met_distance) {
testb(Operand(kRootRegister, IsolateData::is_marking_flag_offset()),
Immediate(static_cast<uint8_t>(1)));
j(zero, not_marking, condition_met_distance);
}
void MacroAssembler::PreCheckSkippedWriteBarrier(Register object,
Register value,
Register scratch, Label* ok) {
ASM_CODE_COMMENT(this);
DCHECK(!AreAliased(object, scratch));
DCHECK(!AreAliased(value, scratch));
leaq(scratch, Operand(object, -kHeapObjectTag));
cmpq(scratch,
Operand(kRootRegister, IsolateData::last_young_allocation_offset()));
j(Condition::equal, ok);
CheckPageFlag(value, scratch, MemoryChunk::kIsInReadOnlyHeapMask, not_zero,
ok);
Label not_ok;
movq(scratch,
Operand(kRootRegister, IsolateData::last_young_allocation_offset()));
cmpq(scratch,
Operand(kRootRegister, IsolateData::new_allocation_info_start_offset()));
j(Condition::kUnsignedLessThan, ¬_ok);
cmpq(scratch, object);
j(Condition::kUnsignedGreaterThanEqual, ¬_ok);
cmpq(object,
Operand(kRootRegister, IsolateData::new_allocation_info_top_offset()));
j(Condition::kUnsignedLessThan, ok);
bind(¬_ok);
}
void MacroAssembler::CheckMarkBit(Register object, Register scratch0,
Register scratch1, Condition cc,
Label* condition_met,
Label::Distance condition_met_distance) {
ASM_CODE_COMMENT(this);
DCHECK(cc == carry || cc == not_carry);
DCHECK(!AreAliased(object, scratch0, scratch1));
MemoryChunkHeaderFromObject(object, scratch0);
#ifdef V8_ENABLE_SANDBOX
movl(scratch0, Operand(scratch0, MemoryChunk::MetadataIndexOffset()));
andl(scratch0,
Immediate(MemoryChunkConstants::kMetadataPointerTableSizeMask));
shll(scratch0, Immediate(kSystemPointerSizeLog2));
LoadAddress(scratch1,
ExternalReference::memory_chunk_metadata_table_address());
movq(scratch0, Operand(scratch1, scratch0, times_1, 0));
#else
movq(scratch0, Operand(scratch0, MemoryChunk::MetadataOffset()));
#endif
if (v8_flags.slow_debug_code) {
Push(object);
movq(scratch1, Operand(scratch0, MemoryChunkMetadata::AreaStartOffset()));
MemoryChunkHeaderFromObject(scratch1, scratch1);
MemoryChunkHeaderFromObject(object, object);
cmpq(object, scratch1);
Check(equal, AbortReason::kMetadataAreaStartDoesNotMatch);
Pop(object);
}
addq(scratch0, Immediate(MutablePageMetadata::MarkingBitmapOffset()));
movq(scratch1, object);
andq(scratch1, Immediate(MemoryChunk::GetAlignmentMaskForAssembler()));
shrq(scratch1, Immediate(kTaggedSizeLog2 + MarkingBitmap::kBitsPerCellLog2));
shlq(scratch1, Immediate(kBitsPerByteLog2));
addq(scratch0, scratch1);
movq(scratch1, object);
andq(scratch1, Immediate(MemoryChunk::GetAlignmentMaskForAssembler()));
shrq(scratch1, Immediate(kTaggedSizeLog2));
andq(scratch1, Immediate(MarkingBitmap::kBitIndexMask));
btq(Operand(scratch0, 0), scratch1);
j(cc, condition_met, condition_met_distance);
}
void MacroAssembler::ComputeCodeStartAddress(Register dst) {
Label current;
bind(¤t);
int pc = pc_offset();
leaq(dst, Operand(¤t, -pc));
}
void MacroAssembler::AssertNotDeoptimized(Register scratch) {
int offset = InstructionStream::kCodeOffset - InstructionStream::kHeaderSize;
LoadProtectedPointerField(scratch,
Operand(kJavaScriptCallCodeStartRegister, offset));
TestCodeIsMarkedForDeoptimization(scratch);
Assert(zero, AbortReason::kInvalidDeoptimizedCode);
}
void MacroAssembler::CallForDeoptimization(Builtin target, int, Label* exit,
DeoptimizeKind kind, Label* ret,
Label*) {
ASM_CODE_COMMENT(this);
call(EntryFromBuiltinAsOperand(target));
DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
(kind == DeoptimizeKind::kLazy ||
kind == DeoptimizeKind::kLazyAfterFastCall)
? Deoptimizer::kLazyDeoptExitSize
: Deoptimizer::kEagerDeoptExitSize);
}
void MacroAssembler::Trap() { int3(); }
void MacroAssembler::DebugBreak() { int3(); }
void CallApiFunctionAndReturn(MacroAssembler* masm, bool with_profiling,
Register function_address,
ExternalReference thunk_ref, Register thunk_arg,
int slots_to_drop_on_return,
MemOperand* argc_operand,
MemOperand return_value_operand) {
ASM_CODE_COMMENT(masm);
Label propagate_exception;
Label delete_allocated_handles;
Label leave_exit_frame;
using ER = ExternalReference;
Isolate* isolate = masm->isolate();
MemOperand next_mem_op = __ ExternalReferenceAsOperand(
ER::handle_scope_next_address(isolate), no_reg);
MemOperand limit_mem_op = __ ExternalReferenceAsOperand(
ER::handle_scope_limit_address(isolate), no_reg);
MemOperand level_mem_op = __ ExternalReferenceAsOperand(
ER::handle_scope_level_address(isolate), no_reg);
Register return_value = rax;
Register scratch = kCArgRegs[3];
Register prev_next_address_reg = r12;
Register prev_limit_reg = r15;
DCHECK(!AreAliased(kCArgRegs[0], kCArgRegs[1],
return_value, scratch, kScratchRegister,
prev_next_address_reg, prev_limit_reg));
DCHECK(!AreAliased(function_address,
scratch, kScratchRegister, prev_next_address_reg,
prev_limit_reg));
DCHECK(!AreAliased(thunk_arg,
scratch, kScratchRegister, prev_next_address_reg,
prev_limit_reg));
{
ASM_CODE_COMMENT_STRING(masm,
"Allocate HandleScope in callee-save registers.");
__ movq(prev_next_address_reg, next_mem_op);
__ movq(prev_limit_reg, limit_mem_op);
__ addl(level_mem_op, Immediate(1));
}
DCHECK_EQ(__ sandboxing_mode(), CodeSandboxingMode::kSandboxed);
__ ExitSandbox();
Label profiler_or_side_effects_check_enabled, done_api_call;
if (with_profiling) {
__ RecordComment("Check if profiler or side effects check is enabled");
__ cmpb(__ ExternalReferenceAsOperand(IsolateFieldId::kExecutionMode),
Immediate(0));
__ j(not_zero, &profiler_or_side_effects_check_enabled);
#ifdef V8_RUNTIME_CALL_STATS
__ RecordComment("Check if RCS is enabled");
__ Move(scratch, ER::address_of_runtime_stats_flag());
__ cmpl(Operand(scratch, 0), Immediate(0));
__ j(not_zero, &profiler_or_side_effects_check_enabled);
#endif
}
__ RecordComment("Call the api function directly.");
__ call(function_address);
__ bind(&done_api_call);
__ RecordComment("Load the value from ReturnValue");
__ movq(return_value, return_value_operand);
{
ASM_CODE_COMMENT_STRING(
masm,
"No more valid handles (the result handle was the last one)."
"Restore previous handle scope.");
__ subl(level_mem_op, Immediate(1));
__ Assert(above_equal, AbortReason::kInvalidHandleScopeLevel);
__ movq(next_mem_op, prev_next_address_reg);
__ cmpq(prev_limit_reg, limit_mem_op);
__ j(not_equal, &delete_allocated_handles);
}
__ RecordComment("Leave the API exit frame.");
__ bind(&leave_exit_frame);
Register argc_reg = prev_limit_reg;
if (argc_operand != nullptr) {
__ movq(argc_reg, *argc_operand);
}
__ LeaveExitFrame();
__ EnterSandbox();
{
ASM_CODE_COMMENT_STRING(masm,
"Check if the function scheduled an exception.");
__ CompareRoot(
__ ExternalReferenceAsOperand(ER::exception_address(isolate), no_reg),
RootIndex::kTheHoleValue);
__ j(not_equal, &propagate_exception);
}
__ AssertJSAny(return_value, scratch,
AbortReason::kAPICallReturnedInvalidObject);
if (argc_operand == nullptr) {
DCHECK_NE(slots_to_drop_on_return, 0);
__ Ret(slots_to_drop_on_return * kSystemPointerSize, scratch);
} else {
__ PopReturnAddressTo(scratch);
__ leaq(rsp, Operand(rsp, argc_reg, times_system_pointer_size,
slots_to_drop_on_return * kSystemPointerSize));
__ PushReturnAddressFrom(scratch);
__ ret(0);
}
if (with_profiling) {
ASM_CODE_COMMENT_STRING(masm, "Call the api function via thunk wrapper.");
__ bind(&profiler_or_side_effects_check_enabled);
if (thunk_arg.is_valid()) {
MemOperand thunk_arg_mem_op = __ ExternalReferenceAsOperand(
IsolateFieldId::kApiCallbackThunkArgument);
__ movq(thunk_arg_mem_op, thunk_arg);
}
__ Call(thunk_ref);
__ jmp(&done_api_call);
}
__ RecordComment("An exception was thrown. Propagate it.");
__ bind(&propagate_exception);
__ TailCallRuntime(Runtime::kPropagateException);
{
ASM_CODE_COMMENT_STRING(
masm, "HandleScope limit has changed. Delete allocated extensions.");
__ bind(&delete_allocated_handles);
__ movq(limit_mem_op, prev_limit_reg);
Register saved_result = prev_limit_reg;
__ movq(saved_result, return_value);
__ LoadAddress(kCArgRegs[0], ER::isolate_address());
__ Call(ER::delete_handle_scope_extensions());
__ movq(return_value, saved_result);
__ jmp(&leave_exit_frame);
}
}
}
}
#undef __
#endif