%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/maglev/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/maglev/maglev-assembler-inl.h |
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_MAGLEV_MAGLEV_ASSEMBLER_INL_H_
#define V8_MAGLEV_MAGLEV_ASSEMBLER_INL_H_
#include <algorithm>
#include <type_traits>
#include "src/base/iterator.h"
#include "src/base/template-utils.h"
#include "src/codegen/machine-type.h"
#include "src/maglev/maglev-assembler.h"
#ifdef V8_TARGET_ARCH_ARM
#include "src/maglev/arm/maglev-assembler-arm-inl.h"
#elif V8_TARGET_ARCH_ARM64
#include "src/maglev/arm64/maglev-assembler-arm64-inl.h"
#elif V8_TARGET_ARCH_X64
#include "src/maglev/x64/maglev-assembler-x64-inl.h"
#else
#error "Maglev does not supported this architecture."
#endif
namespace v8 {
namespace internal {
namespace maglev {
namespace detail {
// Base case provides an error.
template <typename T, typename Enable = void>
struct CopyForDeferredHelper {
template <typename U>
struct No_Copy_Helper_Implemented_For_Type;
static void Copy(MaglevCompilationInfo* compilation_info,
No_Copy_Helper_Implemented_For_Type<T>);
};
// Helper for copies by value.
template <typename T, typename Enable = void>
struct CopyForDeferredByValue {
static T Copy(MaglevCompilationInfo* compilation_info, T node) {
return node;
}
};
// Node pointers are copied by value.
template <typename T>
struct CopyForDeferredHelper<
T*, typename std::enable_if<std::is_base_of<NodeBase, T>::value>::type>
: public CopyForDeferredByValue<T*> {};
// Arithmetic values and enums are copied by value.
template <typename T>
struct CopyForDeferredHelper<
T, typename std::enable_if<std::is_arithmetic<T>::value>::type>
: public CopyForDeferredByValue<T> {};
template <typename T>
struct CopyForDeferredHelper<
T, typename std::enable_if<std::is_enum<T>::value>::type>
: public CopyForDeferredByValue<T> {};
// MaglevCompilationInfos are copied by value.
template <>
struct CopyForDeferredHelper<MaglevCompilationInfo*>
: public CopyForDeferredByValue<MaglevCompilationInfo*> {};
// Machine registers are copied by value.
template <>
struct CopyForDeferredHelper<Register>
: public CopyForDeferredByValue<Register> {};
template <>
struct CopyForDeferredHelper<DoubleRegister>
: public CopyForDeferredByValue<DoubleRegister> {};
// Bytecode offsets are copied by value.
template <>
struct CopyForDeferredHelper<BytecodeOffset>
: public CopyForDeferredByValue<BytecodeOffset> {};
// EagerDeoptInfo pointers are copied by value.
template <>
struct CopyForDeferredHelper<EagerDeoptInfo*>
: public CopyForDeferredByValue<EagerDeoptInfo*> {};
// LazyDeoptInfo pointers are copied by value.
template <>
struct CopyForDeferredHelper<LazyDeoptInfo*>
: public CopyForDeferredByValue<LazyDeoptInfo*> {};
// ZoneLabelRef is copied by value.
template <>
struct CopyForDeferredHelper<ZoneLabelRef>
: public CopyForDeferredByValue<ZoneLabelRef> {};
// MapCompare is copied by value.
template <>
struct CopyForDeferredHelper<MapCompare>
: public CopyForDeferredByValue<MapCompare> {};
// RegList are copied by value.
template <>
struct CopyForDeferredHelper<RegList> : public CopyForDeferredByValue<RegList> {
};
// Register snapshots are copied by value.
template <>
struct CopyForDeferredHelper<RegisterSnapshot>
: public CopyForDeferredByValue<RegisterSnapshot> {};
// Feedback slots are copied by value.
template <>
struct CopyForDeferredHelper<FeedbackSlot>
: public CopyForDeferredByValue<FeedbackSlot> {};
// Heap Refs are copied by value.
template <typename T>
struct CopyForDeferredHelper<T, typename std::enable_if<std::is_base_of<
compiler::ObjectRef, T>::value>::type>
: public CopyForDeferredByValue<T> {};
template <typename T>
T CopyForDeferred(MaglevCompilationInfo* compilation_info, T&& value) {
return CopyForDeferredHelper<T>::Copy(compilation_info,
std::forward<T>(value));
}
template <typename T>
T CopyForDeferred(MaglevCompilationInfo* compilation_info, T& value) {
return CopyForDeferredHelper<T>::Copy(compilation_info, value);
}
template <typename T>
T CopyForDeferred(MaglevCompilationInfo* compilation_info, const T& value) {
return CopyForDeferredHelper<T>::Copy(compilation_info, value);
}
template <typename Function>
struct FunctionArgumentsTupleHelper
: public FunctionArgumentsTupleHelper<decltype(&Function::operator())> {};
template <typename C, typename R, typename... A>
struct FunctionArgumentsTupleHelper<R (C::*)(A...) const> {
using FunctionPointer = R (*)(A...);
using Tuple = std::tuple<A...>;
static constexpr size_t kSize = sizeof...(A);
};
template <typename R, typename... A>
struct FunctionArgumentsTupleHelper<R (&)(A...)> {
using FunctionPointer = R (*)(A...);
using Tuple = std::tuple<A...>;
static constexpr size_t kSize = sizeof...(A);
};
template <typename T>
struct StripFirstTupleArg;
template <typename T1, typename... T>
struct StripFirstTupleArg<std::tuple<T1, T...>> {
using Stripped = std::tuple<T...>;
};
template <typename Function>
class DeferredCodeInfoImpl final : public DeferredCodeInfo {
public:
using FunctionPointer =
typename FunctionArgumentsTupleHelper<Function>::FunctionPointer;
using Tuple = typename StripFirstTupleArg<
typename FunctionArgumentsTupleHelper<Function>::Tuple>::Stripped;
template <typename... InArgs>
explicit DeferredCodeInfoImpl(MaglevCompilationInfo* compilation_info,
RegList general_temporaries,
DoubleRegList double_temporaries,
FunctionPointer function, InArgs&&... args)
: function(function),
args(CopyForDeferred(compilation_info, std::forward<InArgs>(args))...),
general_temporaries_(general_temporaries),
double_temporaries_(double_temporaries) {}
DeferredCodeInfoImpl(DeferredCodeInfoImpl&&) = delete;
DeferredCodeInfoImpl(const DeferredCodeInfoImpl&) = delete;
void Generate(MaglevAssembler* masm) override {
MaglevAssembler::ScratchRegisterScope scratch_scope(masm);
scratch_scope.SetAvailable(general_temporaries_);
scratch_scope.SetAvailableDouble(double_temporaries_);
#ifdef DEBUG
masm->set_allow_call(allow_call_);
masm->set_allow_deferred_call(allow_call_);
masm->set_allow_allocate(allow_allocate_);
#endif // DEBUG
std::apply(function,
std::tuple_cat(std::make_tuple(masm), std::move(args)));
#ifdef DEBUG
masm->set_allow_call(false);
masm->set_allow_deferred_call(false);
masm->set_allow_allocate(false);
#endif // DEBUG
}
#ifdef DEBUG
void set_allow_call(bool value) { allow_call_ = value; }
void set_allow_allocate(bool value) { allow_allocate_ = value; }
#endif // DEBUG
private:
FunctionPointer function;
Tuple args;
RegList general_temporaries_;
DoubleRegList double_temporaries_;
#ifdef DEBUG
bool allow_call_ = false;
bool allow_allocate_ = false;
#endif // DEBUG
};
} // namespace detail
template <typename Function, typename... Args>
inline Label* MaglevAssembler::MakeDeferredCode(Function&& deferred_code_gen,
Args&&... args) {
using FunctionPointer =
typename detail::FunctionArgumentsTupleHelper<Function>::FunctionPointer;
static_assert(
std::is_invocable_v<FunctionPointer, MaglevAssembler*,
decltype(detail::CopyForDeferred(
std::declval<MaglevCompilationInfo*>(),
std::declval<Args>()))...>,
"Parameters of deferred_code_gen function should match arguments into "
"MakeDeferredCode");
ScratchRegisterScope scratch_scope(this);
using DeferredCodeInfoT = detail::DeferredCodeInfoImpl<Function>;
DeferredCodeInfoT* deferred_code =
compilation_info()->zone()->New<DeferredCodeInfoT>(
compilation_info(), scratch_scope.Available(),
scratch_scope.AvailableDouble(), deferred_code_gen,
std::forward<Args>(args)...);
#ifdef DEBUG
deferred_code->set_allow_call(allow_deferred_call_);
deferred_code->set_allow_allocate(allow_allocate_);
#endif // DEBUG
code_gen_state()->PushDeferredCode(deferred_code);
return &deferred_code->deferred_code_label;
}
// Note this doesn't take capturing lambdas by design, since state may
// change until `deferred_code_gen` is actually executed. Use either a
// non-capturing lambda, or a plain function pointer.
template <typename Function, typename... Args>
inline void MaglevAssembler::JumpToDeferredIf(Condition cond,
Function&& deferred_code_gen,
Args&&... args) {
if (v8_flags.code_comments) {
RecordComment("-- Jump to deferred code");
}
JumpIf(cond, MakeDeferredCode<Function, Args...>(
std::forward<Function>(deferred_code_gen),
std::forward<Args>(args)...));
}
inline void MaglevAssembler::SmiToDouble(DoubleRegister result, Register smi) {
AssertSmi(smi);
SmiUntag(smi);
Int32ToDouble(result, smi);
}
inline void MaglevAssembler::Branch(Condition condition, BasicBlock* if_true,
BasicBlock* if_false,
BasicBlock* next_block) {
Branch(condition, if_true->label(), Label::kFar, if_true == next_block,
if_false->label(), Label::kFar, if_false == next_block);
}
inline void MaglevAssembler::Branch(Condition condition, Label* if_true,
Label::Distance true_distance,
bool fallthrough_when_true, Label* if_false,
Label::Distance false_distance,
bool fallthrough_when_false) {
if (fallthrough_when_false) {
if (fallthrough_when_true) {
// If both paths are a fallthrough, do nothing.
DCHECK_EQ(if_true, if_false);
return;
}
// Jump over the false block if true, otherwise fall through into it.
JumpIf(condition, if_true, true_distance);
} else {
// Jump to the false block if true.
JumpIf(NegateCondition(condition), if_false, false_distance);
// Jump to the true block if it's not the next block.
if (!fallthrough_when_true) {
Jump(if_true, true_distance);
}
}
}
inline void MaglevAssembler::LoadTaggedField(Register result,
MemOperand operand) {
MacroAssembler::LoadTaggedField(result, operand);
}
inline void MaglevAssembler::LoadTaggedField(Register result, Register object,
int offset) {
MacroAssembler::LoadTaggedField(result, FieldMemOperand(object, offset));
}
inline void MaglevAssembler::LoadTaggedFieldWithoutDecompressing(
Register result, Register object, int offset) {
MacroAssembler::LoadTaggedFieldWithoutDecompressing(
result, FieldMemOperand(object, offset));
}
inline void MaglevAssembler::LoadTaggedSignedField(Register result,
MemOperand operand) {
MacroAssembler::LoadTaggedField(result, operand);
}
inline void MaglevAssembler::LoadTaggedSignedField(Register result,
Register object,
int offset) {
MacroAssembler::LoadTaggedField(result, FieldMemOperand(object, offset));
}
inline void MaglevAssembler::LoadAndUntagTaggedSignedField(Register result,
Register object,
int offset) {
MacroAssembler::SmiUntagField(result, FieldMemOperand(object, offset));
}
namespace detail {
#ifdef DEBUG
inline bool ClobberedBy(RegList written_registers, Register reg) {
return written_registers.has(reg);
}
inline bool ClobberedBy(RegList written_registers, DoubleRegister reg) {
return false;
}
inline bool ClobberedBy(RegList written_registers, Handle<Object> handle) {
return false;
}
inline bool ClobberedBy(RegList written_registers, Tagged<Smi> smi) {
return false;
}
inline bool ClobberedBy(RegList written_registers, Tagged<TaggedIndex> index) {
return false;
}
inline bool ClobberedBy(RegList written_registers, int32_t imm) {
return false;
}
inline bool ClobberedBy(RegList written_registers, RootIndex index) {
return false;
}
inline bool ClobberedBy(RegList written_registers, const Input& input) {
if (!input.IsGeneralRegister()) return false;
return ClobberedBy(written_registers, input.AssignedGeneralRegister());
}
inline bool ClobberedBy(DoubleRegList written_registers, Register reg) {
return false;
}
inline bool ClobberedBy(DoubleRegList written_registers, DoubleRegister reg) {
return written_registers.has(reg);
}
inline bool ClobberedBy(DoubleRegList written_registers,
Handle<Object> handle) {
return false;
}
inline bool ClobberedBy(DoubleRegList written_registers, Tagged<Smi> smi) {
return false;
}
inline bool ClobberedBy(DoubleRegList written_registers,
Tagged<TaggedIndex> index) {
return false;
}
inline bool ClobberedBy(DoubleRegList written_registers, int32_t imm) {
return false;
}
inline bool ClobberedBy(DoubleRegList written_registers, RootIndex index) {
return false;
}
inline bool ClobberedBy(DoubleRegList written_registers, const Input& input) {
if (!input.IsDoubleRegister()) return false;
return ClobberedBy(written_registers, input.AssignedDoubleRegister());
}
// We don't know what's inside machine registers or operands, so assume they
// match.
inline bool MachineTypeMatches(MachineType type, Register reg) {
return !IsFloatingPoint(type.representation());
}
inline bool MachineTypeMatches(MachineType type, DoubleRegister reg) {
return IsFloatingPoint(type.representation());
}
inline bool MachineTypeMatches(MachineType type, MemOperand reg) {
return true;
}
inline bool MachineTypeMatches(MachineType type, Handle<HeapObject> handle) {
return type.IsTagged() && !type.IsTaggedSigned();
}
inline bool MachineTypeMatches(MachineType type, Tagged<Smi> smi) {
return type.IsTagged() && !type.IsTaggedPointer();
}
inline bool MachineTypeMatches(MachineType type, Tagged<TaggedIndex> index) {
// TaggedIndex doesn't have a separate type, so check for the same type as for
// Smis.
return type.IsTagged() && !type.IsTaggedPointer();
}
inline bool MachineTypeMatches(MachineType type, int32_t imm) {
// 32-bit immediates can be used for 64-bit params -- they'll be
// zero-extended.
return type.representation() == MachineRepresentation::kWord32 ||
type.representation() == MachineRepresentation::kWord64;
}
inline bool MachineTypeMatches(MachineType type, RootIndex index) {
return type.IsTagged() && !type.IsTaggedSigned();
}
inline bool MachineTypeMatches(MachineType type, const Input& input) {
if (type.representation() == input.node()->GetMachineRepresentation()) {
return true;
}
if (type.IsTagged()) {
return input.node()->is_tagged();
}
return false;
}
template <typename Descriptor, typename Arg>
void CheckArg(MaglevAssembler* masm, Arg& arg, int& i) {
if (i >= Descriptor::GetParameterCount()) {
CHECK(Descriptor::AllowVarArgs());
}
CHECK(MachineTypeMatches(Descriptor::GetParameterType(i), arg));
++i;
}
template <typename Descriptor, typename Iterator>
void CheckArg(MaglevAssembler* masm,
const base::iterator_range<Iterator>& range, int& i) {
for (auto it = range.begin(), end = range.end(); it != end; ++it, ++i) {
if (i >= Descriptor::GetParameterCount()) {
CHECK(Descriptor::AllowVarArgs());
}
CHECK(MachineTypeMatches(Descriptor::GetParameterType(i), *it));
}
}
template <typename Descriptor, typename... Args>
void CheckArgs(MaglevAssembler* masm, const std::tuple<Args...>& args) {
int i = 0;
base::tuple_for_each(args,
[&](auto&& arg) { CheckArg<Descriptor>(masm, arg, i); });
if (Descriptor::AllowVarArgs()) {
CHECK_GE(i, Descriptor::GetParameterCount());
} else {
CHECK_EQ(i, Descriptor::GetParameterCount());
}
}
#else // DEBUG
template <typename Descriptor, typename... Args>
void CheckArgs(Args&&... args) {}
#endif // DEBUG
template <typename Descriptor, typename... Args>
void PushArgumentsForBuiltin(MaglevAssembler* masm, std::tuple<Args...> args) {
std::apply(
[&](auto&&... stack_args) {
if (Descriptor::kStackArgumentOrder == StackArgumentOrder::kDefault) {
masm->Push(std::forward<decltype(stack_args)>(stack_args)...);
} else {
masm->PushReverse(std::forward<decltype(stack_args)>(stack_args)...);
}
},
args);
}
template <typename Descriptor>
void PushArgumentsForBuiltin(MaglevAssembler* masm, std::tuple<> empty_args) {}
template <Builtin kBuiltin, typename... Args>
void MoveArgumentsForBuiltin(MaglevAssembler* masm, Args&&... args) {
using Descriptor = typename CallInterfaceDescriptorFor<kBuiltin>::type;
// Put the args into a tuple for easier manipulation.
std::tuple<Args&&...> args_tuple{std::forward<Args>(args)...};
// If there is a context, the first argument is the context parameter. Use
// the remaining args as the actual arguments. We pass the context first
// instead of last to avoid ambiguity around dealing with on-stack
// arguments.
constexpr size_t context_args = Descriptor::HasContextParameter() ? 1 : 0;
static_assert(context_args <= std::tuple_size_v<decltype(args_tuple)>,
"Not enough arguments passed in to builtin (are you missing a "
"context argument?)");
auto args_tuple_without_context = base::tuple_drop<context_args>(args_tuple);
CheckArgs<Descriptor>(masm, args_tuple_without_context);
// Split args into register and stack args.
static_assert(Descriptor::GetRegisterParameterCount() <=
std::tuple_size_v<decltype(args_tuple_without_context)>,
"Not enough arguments passed in to builtin (are you missing a "
"context argument?)");
auto register_args =
base::tuple_head<Descriptor::GetRegisterParameterCount()>(
args_tuple_without_context);
auto stack_args = base::tuple_drop<Descriptor::GetRegisterParameterCount()>(
args_tuple_without_context);
// Split stack args into fixed and variable.
static_assert(
Descriptor::GetStackParameterCount() <=
std::tuple_size_v<decltype(stack_args)>,
"Not enough stack arguments passed in to builtin (are you missing a "
"context argument?)");
auto fixed_stack_args =
base::tuple_head<Descriptor::GetStackParameterCount()>(stack_args);
auto vararg_stack_args =
base::tuple_drop<Descriptor::GetStackParameterCount()>(stack_args);
if constexpr (!Descriptor::AllowVarArgs()) {
static_assert(std::tuple_size_v<decltype(vararg_stack_args)> == 0,
"Too many arguments passed in to builtin that expects no "
"vararg stack arguments");
}
// First push stack arguments (if any), since some of these may be in
// registers and we don't want to clobber them. This supports any thing
// `masm->Push` supports, including iterator ranges, so the tuple size may be
// smaller than the number of arguments actually pushed. We push fixed and
// vararg stack arguments separately, so that there's an appropriate amount
// of padding between them.
if (Descriptor::kStackArgumentOrder == StackArgumentOrder::kDefault) {
PushArgumentsForBuiltin<Descriptor>(
masm, std::forward<decltype(fixed_stack_args)>(fixed_stack_args));
PushArgumentsForBuiltin<Descriptor>(
masm, std::forward<decltype(vararg_stack_args)>(vararg_stack_args));
} else {
PushArgumentsForBuiltin<Descriptor>(
masm, std::forward<decltype(vararg_stack_args)>(vararg_stack_args));
PushArgumentsForBuiltin<Descriptor>(
masm, std::forward<decltype(fixed_stack_args)>(fixed_stack_args));
}
// Then, set register arguments.
// TODO(leszeks): Use the parallel move helper to do register moves, instead
// of detecting clobbering.
#ifdef DEBUG
RegList written_registers = {};
DoubleRegList written_double_registers = {};
#endif // DEBUG
base::tuple_for_each_with_index(register_args, [&](auto&& arg, auto index) {
using Arg = decltype(arg);
static_assert(index < Descriptor::GetRegisterParameterCount());
// Make sure the argument wasn't clobbered by any previous write.
DCHECK(!ClobberedBy(written_registers, arg));
DCHECK(!ClobberedBy(written_double_registers, arg));
static constexpr bool use_double_register =
IsFloatingPoint(Descriptor::GetParameterType(index).representation());
if constexpr (use_double_register) {
DoubleRegister target = Descriptor::GetDoubleRegisterParameter(index);
if constexpr (std::is_same_v<Input, std::decay_t<Arg>>) {
DCHECK_EQ(target, arg.AssignedDoubleRegister());
USE(target);
} else {
masm->Move(target, std::forward<Arg>(arg));
}
#ifdef DEBUG
written_double_registers.set(target);
#endif // DEBUG
} else {
Register target = Descriptor::GetRegisterParameter(index);
if constexpr (std::is_same_v<Input, std::decay_t<Arg>>) {
DCHECK_EQ(target, arg.AssignedGeneralRegister());
USE(target);
} else {
masm->Move(target, std::forward<Arg>(arg));
}
#ifdef DEBUG
written_registers.set(target);
#endif // DEBUG
}
// TODO(leszeks): Support iterator range for register args.
});
// Set the context last (to avoid clobbering).
if constexpr (Descriptor::HasContextParameter()) {
auto&& context = std::get<0>(args_tuple);
DCHECK(!ClobberedBy(written_registers, context));
DCHECK(!ClobberedBy(written_double_registers, context));
DCHECK(MachineTypeMatches(MachineType::AnyTagged(), context));
if constexpr (std::is_same_v<Input, std::decay_t<decltype(context)>>) {
DCHECK_EQ(Descriptor::ContextRegister(),
context.AssignedGeneralRegister());
} else {
// Don't allow raw Register here, force materialisation from a constant.
// This is because setting parameters could have clobbered the register.
// TODO(leszeks): Include the context register in the parallel moves
// described above.
static_assert(!std::is_same_v<Register, std::decay_t<decltype(context)>>);
masm->Move(Descriptor::ContextRegister(), context);
}
}
}
} // namespace detail
inline void MaglevAssembler::CallBuiltin(Builtin builtin) {
// Special case allowing calls to DoubleToI, which takes care to preserve all
// registers and therefore doesn't require special spill handling.
DCHECK(allow_call() || builtin == Builtin::kDoubleToI);
// Temporaries have to be reset before calling CallBuiltin, in case it uses
// temporaries that alias register parameters.
ScratchRegisterScope reset_temps(this);
reset_temps.ResetToDefault();
// Make sure that none of the register parameters alias the default
// temporaries.
#ifdef DEBUG
CallInterfaceDescriptor descriptor =
Builtins::CallInterfaceDescriptorFor(builtin);
for (int i = 0; i < descriptor.GetRegisterParameterCount(); ++i) {
DCHECK(!reset_temps.Available().has(descriptor.GetRegisterParameter(i)));
}
#endif
MacroAssembler::CallBuiltin(builtin);
}
template <Builtin kBuiltin, typename... Args>
inline void MaglevAssembler::CallBuiltin(Args&&... args) {
ASM_CODE_COMMENT(this);
detail::MoveArgumentsForBuiltin<kBuiltin>(this, std::forward<Args>(args)...);
CallBuiltin(kBuiltin);
}
inline void MaglevAssembler::CallRuntime(Runtime::FunctionId fid) {
DCHECK(allow_call());
// Temporaries have to be reset before calling CallRuntime, in case it uses
// temporaries that alias register parameters.
ScratchRegisterScope reset_temps(this);
reset_temps.ResetToDefault();
MacroAssembler::CallRuntime(fid);
}
inline void MaglevAssembler::CallRuntime(Runtime::FunctionId fid,
int num_args) {
DCHECK(allow_call());
// Temporaries have to be reset before calling CallRuntime, in case it uses
// temporaries that alias register parameters.
ScratchRegisterScope reset_temps(this);
reset_temps.ResetToDefault();
MacroAssembler::CallRuntime(fid, num_args);
}
inline void MaglevAssembler::SetMapAsRoot(Register object, RootIndex map) {
ScratchRegisterScope temps(this);
Register scratch = temps.GetDefaultScratchRegister();
LoadTaggedRoot(scratch, map);
StoreTaggedFieldNoWriteBarrier(object, HeapObject::kMapOffset, scratch);
}
inline void MaglevAssembler::SmiTagInt32AndJumpIfFail(
Register dst, Register src, Label* fail, Label::Distance distance) {
SmiTagInt32AndSetFlags(dst, src);
if (!SmiValuesAre32Bits()) {
JumpIf(kOverflow, fail, distance);
}
}
inline void MaglevAssembler::SmiTagInt32AndJumpIfFail(
Register reg, Label* fail, Label::Distance distance) {
SmiTagInt32AndJumpIfFail(reg, reg, fail, distance);
}
inline void MaglevAssembler::SmiTagInt32AndJumpIfSuccess(
Register dst, Register src, Label* success, Label::Distance distance) {
SmiTagInt32AndSetFlags(dst, src);
if (!SmiValuesAre32Bits()) {
JumpIf(kNoOverflow, success, distance);
} else {
jmp(success);
}
}
inline void MaglevAssembler::SmiTagInt32AndJumpIfSuccess(
Register reg, Label* success, Label::Distance distance) {
SmiTagInt32AndJumpIfSuccess(reg, reg, success, distance);
}
inline void MaglevAssembler::UncheckedSmiTagInt32(Register dst, Register src) {
SmiTagInt32AndSetFlags(dst, src);
Assert(kNoOverflow, AbortReason::kInputDoesNotFitSmi);
}
inline void MaglevAssembler::UncheckedSmiTagInt32(Register reg) {
UncheckedSmiTagInt32(reg, reg);
}
inline void MaglevAssembler::SmiTagUint32AndJumpIfFail(
Register dst, Register src, Label* fail, Label::Distance distance) {
// Perform an unsigned comparison against Smi::kMaxValue.
CompareInt32AndJumpIf(src, Smi::kMaxValue, kUnsignedGreaterThan, fail,
distance);
SmiTagInt32AndSetFlags(dst, src);
Assert(kNoOverflow, AbortReason::kInputDoesNotFitSmi);
}
inline void MaglevAssembler::SmiTagUint32AndJumpIfFail(
Register reg, Label* fail, Label::Distance distance) {
SmiTagUint32AndJumpIfFail(reg, reg, fail, distance);
}
inline void MaglevAssembler::SmiTagUint32AndJumpIfSuccess(
Register dst, Register src, Label* success, Label::Distance distance) {
Label fail;
SmiTagUint32AndJumpIfFail(dst, src, &fail, Label::Distance::kNear);
Jump(success, distance);
bind(&fail);
}
inline void MaglevAssembler::SmiTagUint32AndJumpIfSuccess(
Register reg, Label* success, Label::Distance distance) {
SmiTagUint32AndJumpIfSuccess(reg, reg, success, distance);
}
inline void MaglevAssembler::UncheckedSmiTagUint32(Register dst, Register src) {
if (v8_flags.debug_code) {
// Perform an unsigned comparison against Smi::kMaxValue.
CompareInt32AndAssert(src, Smi::kMaxValue, kUnsignedLessThanEqual,
AbortReason::kInputDoesNotFitSmi);
}
SmiTagInt32AndSetFlags(dst, src);
Assert(kNoOverflow, AbortReason::kInputDoesNotFitSmi);
}
inline void MaglevAssembler::UncheckedSmiTagUint32(Register reg) {
UncheckedSmiTagUint32(reg, reg);
}
inline void MaglevAssembler::SmiAddConstant(Register reg, int value,
Label* fail,
Label::Distance distance) {
return SmiAddConstant(reg, reg, value, fail, distance);
}
inline void MaglevAssembler::SmiSubConstant(Register reg, int value,
Label* fail,
Label::Distance distance) {
return SmiSubConstant(reg, reg, value, fail, distance);
}
inline void MaglevAssembler::StringLength(Register result, Register string) {
if (v8_flags.debug_code) {
// Check if {string} is a string.
ScratchRegisterScope temps(this);
Register scratch = temps.GetDefaultScratchRegister();
AssertNotSmi(string);
LoadMap(scratch, string);
CompareInstanceTypeRange(scratch, scratch, FIRST_STRING_TYPE,
LAST_STRING_TYPE);
Check(kUnsignedLessThanEqual, AbortReason::kUnexpectedValue);
}
LoadSignedField(result, FieldMemOperand(string, String::kLengthOffset),
sizeof(int32_t));
}
} // namespace maglev
} // namespace internal
} // namespace v8
#endif // V8_MAGLEV_MAGLEV_ASSEMBLER_INL_H_