// Copyright 2015 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_COMPILER_CODE_ASSEMBLER_H_
|
#define V8_COMPILER_CODE_ASSEMBLER_H_
|
|
#include <map>
|
#include <memory>
|
|
// Clients of this interface shouldn't depend on lots of compiler internals.
|
// Do not include anything from src/compiler here!
|
#include "src/allocation.h"
|
#include "src/base/macros.h"
|
#include "src/builtins/builtins.h"
|
#include "src/code-factory.h"
|
#include "src/globals.h"
|
#include "src/heap/heap.h"
|
#include "src/machine-type.h"
|
#include "src/objects.h"
|
#include "src/objects/data-handler.h"
|
#include "src/objects/map.h"
|
#include "src/objects/maybe-object.h"
|
#include "src/runtime/runtime.h"
|
#include "src/zone/zone-containers.h"
|
|
namespace v8 {
|
namespace internal {
|
|
class Callable;
|
class CallInterfaceDescriptor;
|
class Isolate;
|
class JSCollection;
|
class JSRegExpStringIterator;
|
class JSWeakCollection;
|
class JSWeakMap;
|
class JSWeakSet;
|
class MaybeObject;
|
class PromiseCapability;
|
class PromiseFulfillReactionJobTask;
|
class PromiseReaction;
|
class PromiseReactionJobTask;
|
class PromiseRejectReactionJobTask;
|
class InterpreterData;
|
class Factory;
|
class Zone;
|
|
template <typename T>
|
class Signature;
|
|
struct UntaggedT {};
|
|
struct IntegralT : UntaggedT {};
|
|
struct WordT : IntegralT {
|
static const MachineRepresentation kMachineRepresentation =
|
(kPointerSize == 4) ? MachineRepresentation::kWord32
|
: MachineRepresentation::kWord64;
|
};
|
|
struct RawPtrT : WordT {
|
static constexpr MachineType kMachineType = MachineType::Pointer();
|
};
|
|
template <class To>
|
struct RawPtr : RawPtrT {};
|
|
struct Word32T : IntegralT {
|
static const MachineRepresentation kMachineRepresentation =
|
MachineRepresentation::kWord32;
|
};
|
struct Int32T : Word32T {
|
static constexpr MachineType kMachineType = MachineType::Int32();
|
};
|
struct Uint32T : Word32T {
|
static constexpr MachineType kMachineType = MachineType::Uint32();
|
};
|
|
struct Word64T : IntegralT {
|
static const MachineRepresentation kMachineRepresentation =
|
MachineRepresentation::kWord64;
|
};
|
struct Int64T : Word64T {
|
static constexpr MachineType kMachineType = MachineType::Int64();
|
};
|
struct Uint64T : Word64T {
|
static constexpr MachineType kMachineType = MachineType::Uint64();
|
};
|
|
struct IntPtrT : WordT {
|
static constexpr MachineType kMachineType = MachineType::IntPtr();
|
};
|
struct UintPtrT : WordT {
|
static constexpr MachineType kMachineType = MachineType::UintPtr();
|
};
|
|
struct Float32T : UntaggedT {
|
static const MachineRepresentation kMachineRepresentation =
|
MachineRepresentation::kFloat32;
|
static constexpr MachineType kMachineType = MachineType::Float32();
|
};
|
|
struct Float64T : UntaggedT {
|
static const MachineRepresentation kMachineRepresentation =
|
MachineRepresentation::kFloat64;
|
static constexpr MachineType kMachineType = MachineType::Float64();
|
};
|
|
// Result of a comparison operation.
|
struct BoolT : Word32T {};
|
|
// Value type of a Turbofan node with two results.
|
template <class T1, class T2>
|
struct PairT {};
|
|
inline constexpr MachineType CommonMachineType(MachineType type1,
|
MachineType type2) {
|
return (type1 == type2) ? type1
|
: ((type1.IsTagged() && type2.IsTagged())
|
? MachineType::AnyTagged()
|
: MachineType::None());
|
}
|
|
template <class Type, class Enable = void>
|
struct MachineTypeOf {
|
static constexpr MachineType value = Type::kMachineType;
|
};
|
|
template <class Type, class Enable>
|
constexpr MachineType MachineTypeOf<Type, Enable>::value;
|
|
template <>
|
struct MachineTypeOf<Object> {
|
static constexpr MachineType value = MachineType::AnyTagged();
|
};
|
template <>
|
struct MachineTypeOf<MaybeObject> {
|
static constexpr MachineType value = MachineType::AnyTagged();
|
};
|
template <>
|
struct MachineTypeOf<Smi> {
|
static constexpr MachineType value = MachineType::TaggedSigned();
|
};
|
template <class HeapObjectSubtype>
|
struct MachineTypeOf<HeapObjectSubtype,
|
typename std::enable_if<std::is_base_of<
|
HeapObject, HeapObjectSubtype>::value>::type> {
|
static constexpr MachineType value = MachineType::TaggedPointer();
|
};
|
|
template <class HeapObjectSubtype>
|
constexpr MachineType MachineTypeOf<
|
HeapObjectSubtype, typename std::enable_if<std::is_base_of<
|
HeapObject, HeapObjectSubtype>::value>::type>::value;
|
|
template <class Type, class Enable = void>
|
struct MachineRepresentationOf {
|
static const MachineRepresentation value = Type::kMachineRepresentation;
|
};
|
template <class T>
|
struct MachineRepresentationOf<
|
T, typename std::enable_if<std::is_base_of<Object, T>::value>::type> {
|
static const MachineRepresentation value =
|
MachineTypeOf<T>::value.representation();
|
};
|
template <class T>
|
struct MachineRepresentationOf<
|
T, typename std::enable_if<std::is_base_of<MaybeObject, T>::value>::type> {
|
static const MachineRepresentation value =
|
MachineTypeOf<T>::value.representation();
|
};
|
|
template <class T>
|
struct is_valid_type_tag {
|
static const bool value = std::is_base_of<Object, T>::value ||
|
std::is_base_of<UntaggedT, T>::value ||
|
std::is_base_of<MaybeObject, T>::value ||
|
std::is_same<ExternalReference, T>::value;
|
static const bool is_tagged = std::is_base_of<Object, T>::value ||
|
std::is_base_of<MaybeObject, T>::value;
|
};
|
|
template <class T1, class T2>
|
struct is_valid_type_tag<PairT<T1, T2>> {
|
static const bool value =
|
is_valid_type_tag<T1>::value && is_valid_type_tag<T2>::value;
|
static const bool is_tagged = false;
|
};
|
|
template <class T1, class T2>
|
struct UnionT;
|
|
template <class T1, class T2>
|
struct is_valid_type_tag<UnionT<T1, T2>> {
|
static const bool is_tagged =
|
is_valid_type_tag<T1>::is_tagged && is_valid_type_tag<T2>::is_tagged;
|
static const bool value = is_tagged;
|
};
|
|
template <class T1, class T2>
|
struct UnionT {
|
static constexpr MachineType kMachineType =
|
CommonMachineType(MachineTypeOf<T1>::value, MachineTypeOf<T2>::value);
|
static const MachineRepresentation kMachineRepresentation =
|
kMachineType.representation();
|
static_assert(kMachineRepresentation != MachineRepresentation::kNone,
|
"no common representation");
|
static_assert(is_valid_type_tag<T1>::is_tagged &&
|
is_valid_type_tag<T2>::is_tagged,
|
"union types are only possible for tagged values");
|
};
|
|
using Number = UnionT<Smi, HeapNumber>;
|
using Numeric = UnionT<Number, BigInt>;
|
|
#define ENUM_ELEMENT(Name) k##Name,
|
#define ENUM_STRUCT_ELEMENT(NAME, Name, name) k##Name,
|
enum class ObjectType {
|
kObject,
|
OBJECT_TYPE_LIST(ENUM_ELEMENT) HEAP_OBJECT_TYPE_LIST(ENUM_ELEMENT)
|
STRUCT_LIST(ENUM_STRUCT_ELEMENT)
|
};
|
#undef ENUM_ELEMENT
|
#undef ENUM_STRUCT_ELEMENT
|
|
class AccessCheckNeeded;
|
class BigIntWrapper;
|
class ClassBoilerplate;
|
class BooleanWrapper;
|
class CompilationCacheTable;
|
class Constructor;
|
class Filler;
|
class InternalizedString;
|
class JSArgumentsObject;
|
class JSContextExtensionObject;
|
class JSError;
|
class JSSloppyArgumentsObject;
|
class MapCache;
|
class MutableHeapNumber;
|
class NativeContext;
|
class NumberWrapper;
|
class ScriptWrapper;
|
class SloppyArgumentsElements;
|
class StringWrapper;
|
class SymbolWrapper;
|
class Undetectable;
|
class UniqueName;
|
class WasmExportedFunctionData;
|
class WasmGlobalObject;
|
class WasmMemoryObject;
|
class WasmModuleObject;
|
class WasmTableObject;
|
|
template <class T>
|
struct ObjectTypeOf {};
|
|
#define OBJECT_TYPE_CASE(Name) \
|
template <> \
|
struct ObjectTypeOf<Name> { \
|
static const ObjectType value = ObjectType::k##Name; \
|
};
|
#define OBJECT_TYPE_STRUCT_CASE(NAME, Name, name) \
|
template <> \
|
struct ObjectTypeOf<Name> { \
|
static const ObjectType value = ObjectType::k##Name; \
|
};
|
#define OBJECT_TYPE_TEMPLATE_CASE(Name) \
|
template <class... Args> \
|
struct ObjectTypeOf<Name<Args...>> { \
|
static const ObjectType value = ObjectType::k##Name; \
|
};
|
OBJECT_TYPE_CASE(Object)
|
OBJECT_TYPE_LIST(OBJECT_TYPE_CASE)
|
HEAP_OBJECT_ORDINARY_TYPE_LIST(OBJECT_TYPE_CASE)
|
STRUCT_LIST(OBJECT_TYPE_STRUCT_CASE)
|
HEAP_OBJECT_TEMPLATE_TYPE_LIST(OBJECT_TYPE_TEMPLATE_CASE)
|
#undef OBJECT_TYPE_CASE
|
#undef OBJECT_TYPE_STRUCT_CASE
|
#undef OBJECT_TYPE_TEMPLATE_CASE
|
|
Smi* CheckObjectType(Object* value, Smi* type, String* location);
|
|
namespace compiler {
|
|
class CallDescriptor;
|
class CodeAssemblerLabel;
|
class CodeAssemblerVariable;
|
template <class T>
|
class TypedCodeAssemblerVariable;
|
class CodeAssemblerState;
|
class Node;
|
class RawMachineAssembler;
|
class RawMachineLabel;
|
|
typedef ZoneVector<CodeAssemblerVariable*> CodeAssemblerVariableList;
|
|
typedef std::function<void()> CodeAssemblerCallback;
|
|
template <class T, class U>
|
struct is_subtype {
|
static const bool value = std::is_base_of<U, T>::value;
|
};
|
template <class T1, class T2, class U>
|
struct is_subtype<UnionT<T1, T2>, U> {
|
static const bool value =
|
is_subtype<T1, U>::value && is_subtype<T2, U>::value;
|
};
|
template <class T, class U1, class U2>
|
struct is_subtype<T, UnionT<U1, U2>> {
|
static const bool value =
|
is_subtype<T, U1>::value || is_subtype<T, U2>::value;
|
};
|
template <class T1, class T2, class U1, class U2>
|
struct is_subtype<UnionT<T1, T2>, UnionT<U1, U2>> {
|
static const bool value =
|
(is_subtype<T1, U1>::value || is_subtype<T1, U2>::value) &&
|
(is_subtype<T2, U1>::value || is_subtype<T2, U2>::value);
|
};
|
|
template <class T, class U>
|
struct types_have_common_values {
|
static const bool value = is_subtype<T, U>::value || is_subtype<U, T>::value;
|
};
|
template <class U>
|
struct types_have_common_values<Uint32T, U> {
|
static const bool value = types_have_common_values<Word32T, U>::value;
|
};
|
template <class U>
|
struct types_have_common_values<Int32T, U> {
|
static const bool value = types_have_common_values<Word32T, U>::value;
|
};
|
template <class U>
|
struct types_have_common_values<Uint64T, U> {
|
static const bool value = types_have_common_values<Word64T, U>::value;
|
};
|
template <class U>
|
struct types_have_common_values<Int64T, U> {
|
static const bool value = types_have_common_values<Word64T, U>::value;
|
};
|
template <class U>
|
struct types_have_common_values<IntPtrT, U> {
|
static const bool value = types_have_common_values<WordT, U>::value;
|
};
|
template <class U>
|
struct types_have_common_values<UintPtrT, U> {
|
static const bool value = types_have_common_values<WordT, U>::value;
|
};
|
template <class T1, class T2, class U>
|
struct types_have_common_values<UnionT<T1, T2>, U> {
|
static const bool value = types_have_common_values<T1, U>::value ||
|
types_have_common_values<T2, U>::value;
|
};
|
|
template <class T, class U1, class U2>
|
struct types_have_common_values<T, UnionT<U1, U2>> {
|
static const bool value = types_have_common_values<T, U1>::value ||
|
types_have_common_values<T, U2>::value;
|
};
|
template <class T1, class T2, class U1, class U2>
|
struct types_have_common_values<UnionT<T1, T2>, UnionT<U1, U2>> {
|
static const bool value = types_have_common_values<T1, U1>::value ||
|
types_have_common_values<T1, U2>::value ||
|
types_have_common_values<T2, U1>::value ||
|
types_have_common_values<T2, U2>::value;
|
};
|
|
template <class T>
|
struct types_have_common_values<T, MaybeObject> {
|
static const bool value = types_have_common_values<T, Object>::value;
|
};
|
|
template <class T>
|
struct types_have_common_values<MaybeObject, T> {
|
static const bool value = types_have_common_values<Object, T>::value;
|
};
|
|
// TNode<T> is an SSA value with the static type tag T, which is one of the
|
// following:
|
// - a subclass of internal::Object represents a tagged type
|
// - a subclass of internal::UntaggedT represents an untagged type
|
// - ExternalReference
|
// - PairT<T1, T2> for an operation returning two values, with types T1
|
// and T2
|
// - UnionT<T1, T2> represents either a value of type T1 or of type T2.
|
template <class T>
|
class TNode {
|
public:
|
static_assert(is_valid_type_tag<T>::value, "invalid type tag");
|
|
template <class U,
|
typename std::enable_if<is_subtype<U, T>::value, int>::type = 0>
|
TNode(const TNode<U>& other) : node_(other) {}
|
TNode() : node_(nullptr) {}
|
|
TNode operator=(TNode other) {
|
DCHECK_NOT_NULL(other.node_);
|
node_ = other.node_;
|
return *this;
|
}
|
|
operator compiler::Node*() const { return node_; }
|
|
static TNode UncheckedCast(compiler::Node* node) { return TNode(node); }
|
|
protected:
|
explicit TNode(compiler::Node* node) : node_(node) {}
|
|
private:
|
compiler::Node* node_;
|
};
|
|
// SloppyTNode<T> is a variant of TNode<T> and allows implicit casts from
|
// Node*. It is intended for function arguments as long as some call sites
|
// still use untyped Node* arguments.
|
// TODO(tebbi): Delete this class once transition is finished.
|
template <class T>
|
class SloppyTNode : public TNode<T> {
|
public:
|
SloppyTNode(compiler::Node* node) // NOLINT(runtime/explicit)
|
: TNode<T>(node) {}
|
template <class U, typename std::enable_if<is_subtype<U, T>::value,
|
int>::type = 0>
|
SloppyTNode(const TNode<U>& other) // NOLINT(runtime/explicit)
|
: TNode<T>(other) {}
|
};
|
|
// This macro alias allows to use PairT<T1, T2> as a macro argument.
|
#define PAIR_TYPE(T1, T2) PairT<T1, T2>
|
|
#define CODE_ASSEMBLER_COMPARE_BINARY_OP_LIST(V) \
|
V(Float32Equal, BoolT, Float32T, Float32T) \
|
V(Float32LessThan, BoolT, Float32T, Float32T) \
|
V(Float32LessThanOrEqual, BoolT, Float32T, Float32T) \
|
V(Float32GreaterThan, BoolT, Float32T, Float32T) \
|
V(Float32GreaterThanOrEqual, BoolT, Float32T, Float32T) \
|
V(Float64Equal, BoolT, Float64T, Float64T) \
|
V(Float64NotEqual, BoolT, Float64T, Float64T) \
|
V(Float64LessThan, BoolT, Float64T, Float64T) \
|
V(Float64LessThanOrEqual, BoolT, Float64T, Float64T) \
|
V(Float64GreaterThan, BoolT, Float64T, Float64T) \
|
V(Float64GreaterThanOrEqual, BoolT, Float64T, Float64T) \
|
/* Use Word32Equal if you need Int32Equal */ \
|
V(Int32GreaterThan, BoolT, Word32T, Word32T) \
|
V(Int32GreaterThanOrEqual, BoolT, Word32T, Word32T) \
|
V(Int32LessThan, BoolT, Word32T, Word32T) \
|
V(Int32LessThanOrEqual, BoolT, Word32T, Word32T) \
|
/* Use WordEqual if you need IntPtrEqual */ \
|
V(IntPtrLessThan, BoolT, WordT, WordT) \
|
V(IntPtrLessThanOrEqual, BoolT, WordT, WordT) \
|
V(IntPtrGreaterThan, BoolT, WordT, WordT) \
|
V(IntPtrGreaterThanOrEqual, BoolT, WordT, WordT) \
|
/* Use Word32Equal if you need Uint32Equal */ \
|
V(Uint32LessThan, BoolT, Word32T, Word32T) \
|
V(Uint32LessThanOrEqual, BoolT, Word32T, Word32T) \
|
V(Uint32GreaterThan, BoolT, Word32T, Word32T) \
|
V(Uint32GreaterThanOrEqual, BoolT, Word32T, Word32T) \
|
/* Use WordEqual if you need UintPtrEqual */ \
|
V(UintPtrLessThan, BoolT, WordT, WordT) \
|
V(UintPtrLessThanOrEqual, BoolT, WordT, WordT) \
|
V(UintPtrGreaterThan, BoolT, WordT, WordT) \
|
V(UintPtrGreaterThanOrEqual, BoolT, WordT, WordT)
|
|
#define CODE_ASSEMBLER_BINARY_OP_LIST(V) \
|
CODE_ASSEMBLER_COMPARE_BINARY_OP_LIST(V) \
|
V(Float64Add, Float64T, Float64T, Float64T) \
|
V(Float64Sub, Float64T, Float64T, Float64T) \
|
V(Float64Mul, Float64T, Float64T, Float64T) \
|
V(Float64Div, Float64T, Float64T, Float64T) \
|
V(Float64Mod, Float64T, Float64T, Float64T) \
|
V(Float64Atan2, Float64T, Float64T, Float64T) \
|
V(Float64Pow, Float64T, Float64T, Float64T) \
|
V(Float64Max, Float64T, Float64T, Float64T) \
|
V(Float64Min, Float64T, Float64T, Float64T) \
|
V(Float64InsertLowWord32, Float64T, Float64T, Word32T) \
|
V(Float64InsertHighWord32, Float64T, Float64T, Word32T) \
|
V(IntPtrAddWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \
|
V(IntPtrSubWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \
|
V(Int32Add, Word32T, Word32T, Word32T) \
|
V(Int32AddWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \
|
V(Int32Sub, Word32T, Word32T, Word32T) \
|
V(Int32SubWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \
|
V(Int32Mul, Word32T, Word32T, Word32T) \
|
V(Int32MulWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \
|
V(Int32Div, Int32T, Int32T, Int32T) \
|
V(Int32Mod, Int32T, Int32T, Int32T) \
|
V(WordRor, WordT, WordT, IntegralT) \
|
V(Word32Ror, Word32T, Word32T, Word32T) \
|
V(Word64Ror, Word64T, Word64T, Word64T)
|
|
TNode<Float64T> Float64Add(TNode<Float64T> a, TNode<Float64T> b);
|
|
#define CODE_ASSEMBLER_UNARY_OP_LIST(V) \
|
V(Float64Abs, Float64T, Float64T) \
|
V(Float64Acos, Float64T, Float64T) \
|
V(Float64Acosh, Float64T, Float64T) \
|
V(Float64Asin, Float64T, Float64T) \
|
V(Float64Asinh, Float64T, Float64T) \
|
V(Float64Atan, Float64T, Float64T) \
|
V(Float64Atanh, Float64T, Float64T) \
|
V(Float64Cos, Float64T, Float64T) \
|
V(Float64Cosh, Float64T, Float64T) \
|
V(Float64Exp, Float64T, Float64T) \
|
V(Float64Expm1, Float64T, Float64T) \
|
V(Float64Log, Float64T, Float64T) \
|
V(Float64Log1p, Float64T, Float64T) \
|
V(Float64Log2, Float64T, Float64T) \
|
V(Float64Log10, Float64T, Float64T) \
|
V(Float64Cbrt, Float64T, Float64T) \
|
V(Float64Neg, Float64T, Float64T) \
|
V(Float64Sin, Float64T, Float64T) \
|
V(Float64Sinh, Float64T, Float64T) \
|
V(Float64Sqrt, Float64T, Float64T) \
|
V(Float64Tan, Float64T, Float64T) \
|
V(Float64Tanh, Float64T, Float64T) \
|
V(Float64ExtractLowWord32, Word32T, Float64T) \
|
V(Float64ExtractHighWord32, Word32T, Float64T) \
|
V(BitcastTaggedToWord, IntPtrT, Object) \
|
V(BitcastMaybeObjectToWord, IntPtrT, MaybeObject) \
|
V(BitcastWordToTagged, Object, WordT) \
|
V(BitcastWordToTaggedSigned, Smi, WordT) \
|
V(TruncateFloat64ToFloat32, Float32T, Float64T) \
|
V(TruncateFloat64ToWord32, Word32T, Float64T) \
|
V(TruncateInt64ToInt32, Int32T, Int64T) \
|
V(ChangeFloat32ToFloat64, Float64T, Float32T) \
|
V(ChangeFloat64ToUint32, Uint32T, Float64T) \
|
V(ChangeFloat64ToUint64, Uint64T, Float64T) \
|
V(ChangeInt32ToFloat64, Float64T, Int32T) \
|
V(ChangeInt32ToInt64, Int64T, Int32T) \
|
V(ChangeUint32ToFloat64, Float64T, Word32T) \
|
V(ChangeUint32ToUint64, Uint64T, Word32T) \
|
V(BitcastInt32ToFloat32, Float32T, Word32T) \
|
V(BitcastFloat32ToInt32, Word32T, Float32T) \
|
V(RoundFloat64ToInt32, Int32T, Float64T) \
|
V(RoundInt32ToFloat32, Int32T, Float32T) \
|
V(Float64SilenceNaN, Float64T, Float64T) \
|
V(Float64RoundDown, Float64T, Float64T) \
|
V(Float64RoundUp, Float64T, Float64T) \
|
V(Float64RoundTiesEven, Float64T, Float64T) \
|
V(Float64RoundTruncate, Float64T, Float64T) \
|
V(Word32Clz, Int32T, Word32T) \
|
V(Word32BitwiseNot, Word32T, Word32T) \
|
V(WordNot, WordT, WordT) \
|
V(Int32AbsWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T) \
|
V(Int64AbsWithOverflow, PAIR_TYPE(Int64T, BoolT), Int64T) \
|
V(IntPtrAbsWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT) \
|
V(Word32BinaryNot, BoolT, Word32T)
|
|
// A "public" interface used by components outside of compiler directory to
|
// create code objects with TurboFan's backend. This class is mostly a thin
|
// shim around the RawMachineAssembler, and its primary job is to ensure that
|
// the innards of the RawMachineAssembler and other compiler implementation
|
// details don't leak outside of the the compiler directory..
|
//
|
// V8 components that need to generate low-level code using this interface
|
// should include this header--and this header only--from the compiler
|
// directory (this is actually enforced). Since all interesting data
|
// structures are forward declared, it's not possible for clients to peek
|
// inside the compiler internals.
|
//
|
// In addition to providing isolation between TurboFan and code generation
|
// clients, CodeAssembler also provides an abstraction for creating variables
|
// and enhanced Label functionality to merge variable values along paths where
|
// they have differing values, including loops.
|
//
|
// The CodeAssembler itself is stateless (and instances are expected to be
|
// temporary-scoped and short-lived); all its state is encapsulated into
|
// a CodeAssemblerState instance.
|
class V8_EXPORT_PRIVATE CodeAssembler {
|
public:
|
explicit CodeAssembler(CodeAssemblerState* state) : state_(state) {}
|
~CodeAssembler();
|
|
static Handle<Code> GenerateCode(CodeAssemblerState* state,
|
const AssemblerOptions& options);
|
|
bool Is64() const;
|
bool IsFloat64RoundUpSupported() const;
|
bool IsFloat64RoundDownSupported() const;
|
bool IsFloat64RoundTiesEvenSupported() const;
|
bool IsFloat64RoundTruncateSupported() const;
|
bool IsInt32AbsWithOverflowSupported() const;
|
bool IsInt64AbsWithOverflowSupported() const;
|
bool IsIntPtrAbsWithOverflowSupported() const;
|
|
// Shortened aliases for use in CodeAssembler subclasses.
|
using Label = CodeAssemblerLabel;
|
using Variable = CodeAssemblerVariable;
|
template <class T>
|
using TVariable = TypedCodeAssemblerVariable<T>;
|
using VariableList = CodeAssemblerVariableList;
|
|
// ===========================================================================
|
// Base Assembler
|
// ===========================================================================
|
|
template <class PreviousType, bool FromTyped>
|
class CheckedNode {
|
public:
|
#ifdef DEBUG
|
CheckedNode(Node* node, CodeAssembler* code_assembler, const char* location)
|
: node_(node), code_assembler_(code_assembler), location_(location) {}
|
#else
|
CheckedNode(compiler::Node* node, CodeAssembler*, const char*)
|
: node_(node) {}
|
#endif
|
|
template <class A>
|
operator TNode<A>() {
|
static_assert(
|
!std::is_same<A, MaybeObject>::value,
|
"Can't cast to MaybeObject, use explicit conversion functions. ");
|
|
static_assert(types_have_common_values<A, PreviousType>::value,
|
"Incompatible types: this cast can never succeed.");
|
static_assert(std::is_convertible<TNode<A>, TNode<Object>>::value,
|
"Coercion to untagged values cannot be "
|
"checked.");
|
static_assert(
|
!FromTyped ||
|
!std::is_convertible<TNode<PreviousType>, TNode<A>>::value,
|
"Unnecessary CAST: types are convertible.");
|
#ifdef DEBUG
|
if (FLAG_debug_code) {
|
if (std::is_same<PreviousType, MaybeObject>::value) {
|
code_assembler_->GenerateCheckMaybeObjectIsObject(node_, location_);
|
}
|
Node* function = code_assembler_->ExternalConstant(
|
ExternalReference::check_object_type());
|
code_assembler_->CallCFunction3(
|
MachineType::AnyTagged(), MachineType::AnyTagged(),
|
MachineType::TaggedSigned(), MachineType::AnyTagged(), function,
|
node_,
|
code_assembler_->SmiConstant(
|
static_cast<int>(ObjectTypeOf<A>::value)),
|
code_assembler_->StringConstant(location_));
|
}
|
#endif
|
return TNode<A>::UncheckedCast(node_);
|
}
|
|
template <class A>
|
operator SloppyTNode<A>() {
|
return implicit_cast<TNode<A>>(*this);
|
}
|
|
Node* node() const { return node_; }
|
|
private:
|
Node* node_;
|
#ifdef DEBUG
|
CodeAssembler* code_assembler_;
|
const char* location_;
|
#endif
|
};
|
|
template <class T>
|
TNode<T> UncheckedCast(Node* value) {
|
return TNode<T>::UncheckedCast(value);
|
}
|
template <class T, class U>
|
TNode<T> UncheckedCast(TNode<U> value) {
|
static_assert(types_have_common_values<T, U>::value,
|
"Incompatible types: this cast can never succeed.");
|
return TNode<T>::UncheckedCast(value);
|
}
|
|
// ReinterpretCast<T>(v) has the power to cast even when the type of v is
|
// unrelated to T. Use with care.
|
template <class T>
|
TNode<T> ReinterpretCast(Node* value) {
|
return TNode<T>::UncheckedCast(value);
|
}
|
|
CheckedNode<Object, false> Cast(Node* value, const char* location = "") {
|
return {value, this, location};
|
}
|
|
template <class T>
|
CheckedNode<T, true> Cast(TNode<T> value, const char* location = "") {
|
return {value, this, location};
|
}
|
|
#ifdef DEBUG
|
#define STRINGIFY(x) #x
|
#define TO_STRING_LITERAL(x) STRINGIFY(x)
|
#define CAST(x) \
|
Cast(x, "CAST(" #x ") at " __FILE__ ":" TO_STRING_LITERAL(__LINE__))
|
#else
|
#define CAST(x) Cast(x)
|
#endif
|
|
#ifdef DEBUG
|
void GenerateCheckMaybeObjectIsObject(Node* node, const char* location);
|
#endif
|
|
// Constants.
|
TNode<Int32T> Int32Constant(int32_t value);
|
TNode<Int64T> Int64Constant(int64_t value);
|
TNode<IntPtrT> IntPtrConstant(intptr_t value);
|
TNode<Number> NumberConstant(double value);
|
TNode<Smi> SmiConstant(Smi* value);
|
TNode<Smi> SmiConstant(int value);
|
template <typename E,
|
typename = typename std::enable_if<std::is_enum<E>::value>::type>
|
TNode<Smi> SmiConstant(E value) {
|
STATIC_ASSERT(sizeof(E) <= sizeof(int));
|
return SmiConstant(static_cast<int>(value));
|
}
|
TNode<HeapObject> UntypedHeapConstant(Handle<HeapObject> object);
|
template <class Type>
|
TNode<Type> HeapConstant(Handle<Type> object) {
|
return UncheckedCast<Type>(UntypedHeapConstant(object));
|
}
|
TNode<String> StringConstant(const char* str);
|
TNode<Oddball> BooleanConstant(bool value);
|
TNode<ExternalReference> ExternalConstant(ExternalReference address);
|
TNode<Float64T> Float64Constant(double value);
|
TNode<HeapNumber> NaNConstant();
|
TNode<BoolT> Int32TrueConstant() {
|
return ReinterpretCast<BoolT>(Int32Constant(1));
|
}
|
TNode<BoolT> Int32FalseConstant() {
|
return ReinterpretCast<BoolT>(Int32Constant(0));
|
}
|
TNode<BoolT> BoolConstant(bool value) {
|
return value ? Int32TrueConstant() : Int32FalseConstant();
|
}
|
|
bool ToInt32Constant(Node* node, int32_t& out_value);
|
bool ToInt64Constant(Node* node, int64_t& out_value);
|
bool ToSmiConstant(Node* node, Smi*& out_value);
|
bool ToIntPtrConstant(Node* node, intptr_t& out_value);
|
|
bool IsUndefinedConstant(TNode<Object> node);
|
bool IsNullConstant(TNode<Object> node);
|
|
TNode<Int32T> Signed(TNode<Word32T> x) { return UncheckedCast<Int32T>(x); }
|
TNode<IntPtrT> Signed(TNode<WordT> x) { return UncheckedCast<IntPtrT>(x); }
|
TNode<Uint32T> Unsigned(TNode<Word32T> x) {
|
return UncheckedCast<Uint32T>(x);
|
}
|
TNode<UintPtrT> Unsigned(TNode<WordT> x) {
|
return UncheckedCast<UintPtrT>(x);
|
}
|
|
static constexpr int kTargetParameterIndex = -1;
|
|
Node* Parameter(int value);
|
|
TNode<Context> GetJSContextParameter();
|
void Return(SloppyTNode<Object> value);
|
void Return(SloppyTNode<Object> value1, SloppyTNode<Object> value2);
|
void Return(SloppyTNode<Object> value1, SloppyTNode<Object> value2,
|
SloppyTNode<Object> value3);
|
void PopAndReturn(Node* pop, Node* value);
|
|
void ReturnIf(Node* condition, Node* value);
|
|
void ReturnRaw(Node* value);
|
|
void DebugAbort(Node* message);
|
void DebugBreak();
|
void Unreachable();
|
void Comment(const char* format, ...);
|
|
void Bind(Label* label);
|
#if DEBUG
|
void Bind(Label* label, AssemblerDebugInfo debug_info);
|
#endif // DEBUG
|
void Goto(Label* label);
|
void GotoIf(SloppyTNode<IntegralT> condition, Label* true_label);
|
void GotoIfNot(SloppyTNode<IntegralT> condition, Label* false_label);
|
void Branch(SloppyTNode<IntegralT> condition, Label* true_label,
|
Label* false_label);
|
|
void Branch(TNode<BoolT> condition, std::function<void()> true_body,
|
std::function<void()> false_body);
|
void Branch(TNode<BoolT> condition, Label* true_label,
|
std::function<void()> false_body);
|
void Branch(TNode<BoolT> condition, std::function<void()> true_body,
|
Label* false_label);
|
|
void Switch(Node* index, Label* default_label, const int32_t* case_values,
|
Label** case_labels, size_t case_count);
|
|
// Access to the frame pointer
|
Node* LoadFramePointer();
|
Node* LoadParentFramePointer();
|
|
// Access to the stack pointer
|
Node* LoadStackPointer();
|
|
// Poison |value| on speculative paths.
|
TNode<Object> TaggedPoisonOnSpeculation(SloppyTNode<Object> value);
|
TNode<WordT> WordPoisonOnSpeculation(SloppyTNode<WordT> value);
|
|
// Load raw memory location.
|
Node* Load(MachineType rep, Node* base,
|
LoadSensitivity needs_poisoning = LoadSensitivity::kSafe);
|
template <class Type>
|
TNode<Type> Load(MachineType rep, TNode<RawPtr<Type>> base) {
|
DCHECK(
|
IsSubtype(rep.representation(), MachineRepresentationOf<Type>::value));
|
return UncheckedCast<Type>(Load(rep, static_cast<Node*>(base)));
|
}
|
Node* Load(MachineType rep, Node* base, Node* offset,
|
LoadSensitivity needs_poisoning = LoadSensitivity::kSafe);
|
Node* AtomicLoad(MachineType rep, Node* base, Node* offset);
|
|
// Load a value from the root array.
|
TNode<Object> LoadRoot(Heap::RootListIndex root_index);
|
|
// Store value to raw memory location.
|
Node* Store(Node* base, Node* value);
|
Node* Store(Node* base, Node* offset, Node* value);
|
Node* StoreWithMapWriteBarrier(Node* base, Node* offset, Node* value);
|
Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* value);
|
Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* offset,
|
Node* value);
|
Node* AtomicStore(MachineRepresentation rep, Node* base, Node* offset,
|
Node* value);
|
|
// Exchange value at raw memory location
|
Node* AtomicExchange(MachineType type, Node* base, Node* offset, Node* value);
|
|
// Compare and Exchange value at raw memory location
|
Node* AtomicCompareExchange(MachineType type, Node* base, Node* offset,
|
Node* old_value, Node* new_value);
|
|
Node* AtomicAdd(MachineType type, Node* base, Node* offset, Node* value);
|
|
Node* AtomicSub(MachineType type, Node* base, Node* offset, Node* value);
|
|
Node* AtomicAnd(MachineType type, Node* base, Node* offset, Node* value);
|
|
Node* AtomicOr(MachineType type, Node* base, Node* offset, Node* value);
|
|
Node* AtomicXor(MachineType type, Node* base, Node* offset, Node* value);
|
|
// Store a value to the root array.
|
Node* StoreRoot(Heap::RootListIndex root_index, Node* value);
|
|
// Basic arithmetic operations.
|
#define DECLARE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \
|
TNode<ResType> name(SloppyTNode<Arg1Type> a, SloppyTNode<Arg2Type> b);
|
CODE_ASSEMBLER_BINARY_OP_LIST(DECLARE_CODE_ASSEMBLER_BINARY_OP)
|
#undef DECLARE_CODE_ASSEMBLER_BINARY_OP
|
|
TNode<IntPtrT> WordShr(TNode<IntPtrT> left, TNode<IntegralT> right) {
|
return UncheckedCast<IntPtrT>(
|
WordShr(static_cast<Node*>(left), static_cast<Node*>(right)));
|
}
|
|
TNode<IntPtrT> WordAnd(TNode<IntPtrT> left, TNode<IntPtrT> right) {
|
return UncheckedCast<IntPtrT>(
|
WordAnd(static_cast<Node*>(left), static_cast<Node*>(right)));
|
}
|
|
template <class Left, class Right,
|
class = typename std::enable_if<
|
std::is_base_of<Object, Left>::value &&
|
std::is_base_of<Object, Right>::value>::type>
|
TNode<BoolT> WordEqual(TNode<Left> left, TNode<Right> right) {
|
return WordEqual(ReinterpretCast<WordT>(left),
|
ReinterpretCast<WordT>(right));
|
}
|
TNode<BoolT> WordEqual(TNode<Object> left, Node* right) {
|
return WordEqual(ReinterpretCast<WordT>(left),
|
ReinterpretCast<WordT>(right));
|
}
|
TNode<BoolT> WordEqual(Node* left, TNode<Object> right) {
|
return WordEqual(ReinterpretCast<WordT>(left),
|
ReinterpretCast<WordT>(right));
|
}
|
template <class Left, class Right,
|
class = typename std::enable_if<
|
std::is_base_of<Object, Left>::value &&
|
std::is_base_of<Object, Right>::value>::type>
|
TNode<BoolT> WordNotEqual(TNode<Left> left, TNode<Right> right) {
|
return WordNotEqual(ReinterpretCast<WordT>(left),
|
ReinterpretCast<WordT>(right));
|
}
|
TNode<BoolT> WordNotEqual(TNode<Object> left, Node* right) {
|
return WordNotEqual(ReinterpretCast<WordT>(left),
|
ReinterpretCast<WordT>(right));
|
}
|
TNode<BoolT> WordNotEqual(Node* left, TNode<Object> right) {
|
return WordNotEqual(ReinterpretCast<WordT>(left),
|
ReinterpretCast<WordT>(right));
|
}
|
|
TNode<BoolT> IntPtrEqual(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<BoolT> WordEqual(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<BoolT> WordNotEqual(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<BoolT> Word32Equal(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<BoolT> Word32NotEqual(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<BoolT> Word64Equal(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
TNode<BoolT> Word64NotEqual(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
|
TNode<Int32T> Int32Add(TNode<Int32T> left, TNode<Int32T> right) {
|
return Signed(
|
Int32Add(static_cast<Node*>(left), static_cast<Node*>(right)));
|
}
|
|
TNode<WordT> IntPtrAdd(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<WordT> IntPtrSub(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<WordT> IntPtrMul(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<IntPtrT> IntPtrAdd(TNode<IntPtrT> left, TNode<IntPtrT> right) {
|
return Signed(
|
IntPtrAdd(static_cast<Node*>(left), static_cast<Node*>(right)));
|
}
|
TNode<IntPtrT> IntPtrSub(TNode<IntPtrT> left, TNode<IntPtrT> right) {
|
return Signed(
|
IntPtrSub(static_cast<Node*>(left), static_cast<Node*>(right)));
|
}
|
TNode<IntPtrT> IntPtrMul(TNode<IntPtrT> left, TNode<IntPtrT> right) {
|
return Signed(
|
IntPtrMul(static_cast<Node*>(left), static_cast<Node*>(right)));
|
}
|
|
TNode<WordT> WordShl(SloppyTNode<WordT> value, int shift);
|
TNode<WordT> WordShr(SloppyTNode<WordT> value, int shift);
|
TNode<WordT> WordSar(SloppyTNode<WordT> value, int shift);
|
TNode<IntPtrT> WordShr(TNode<IntPtrT> value, int shift) {
|
return UncheckedCast<IntPtrT>(WordShr(static_cast<Node*>(value), shift));
|
}
|
TNode<Word32T> Word32Shr(SloppyTNode<Word32T> value, int shift);
|
|
TNode<WordT> WordOr(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<WordT> WordAnd(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<WordT> WordXor(SloppyTNode<WordT> left, SloppyTNode<WordT> right);
|
TNode<WordT> WordShl(SloppyTNode<WordT> left, SloppyTNode<IntegralT> right);
|
TNode<WordT> WordShr(SloppyTNode<WordT> left, SloppyTNode<IntegralT> right);
|
TNode<WordT> WordSar(SloppyTNode<WordT> left, SloppyTNode<IntegralT> right);
|
TNode<Word32T> Word32Or(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<Word32T> Word32And(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<Word32T> Word32Xor(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<Word32T> Word32Shl(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<Word32T> Word32Shr(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<Word32T> Word32Sar(SloppyTNode<Word32T> left,
|
SloppyTNode<Word32T> right);
|
TNode<Word64T> Word64Or(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
TNode<Word64T> Word64And(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
TNode<Word64T> Word64Xor(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
TNode<Word64T> Word64Shl(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
TNode<Word64T> Word64Shr(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
TNode<Word64T> Word64Sar(SloppyTNode<Word64T> left,
|
SloppyTNode<Word64T> right);
|
|
// Unary
|
#define DECLARE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \
|
TNode<ResType> name(SloppyTNode<ArgType> a);
|
CODE_ASSEMBLER_UNARY_OP_LIST(DECLARE_CODE_ASSEMBLER_UNARY_OP)
|
#undef DECLARE_CODE_ASSEMBLER_UNARY_OP
|
|
// Changes a double to an inptr_t for pointer arithmetic outside of Smi range.
|
// Assumes that the double can be exactly represented as an int.
|
TNode<UintPtrT> ChangeFloat64ToUintPtr(SloppyTNode<Float64T> value);
|
|
// Changes an intptr_t to a double, e.g. for storing an element index
|
// outside Smi range in a HeapNumber. Lossless on 32-bit,
|
// rounds on 64-bit (which doesn't affect valid element indices).
|
Node* RoundIntPtrToFloat64(Node* value);
|
// No-op on 32-bit, otherwise zero extend.
|
TNode<UintPtrT> ChangeUint32ToWord(SloppyTNode<Word32T> value);
|
// No-op on 32-bit, otherwise sign extend.
|
TNode<IntPtrT> ChangeInt32ToIntPtr(SloppyTNode<Word32T> value);
|
|
// No-op that guarantees that the value is kept alive till this point even
|
// if GC happens.
|
Node* Retain(Node* value);
|
|
// Projections
|
Node* Projection(int index, Node* value);
|
|
template <int index, class T1, class T2>
|
TNode<typename std::tuple_element<index, std::tuple<T1, T2>>::type>
|
Projection(TNode<PairT<T1, T2>> value) {
|
return UncheckedCast<
|
typename std::tuple_element<index, std::tuple<T1, T2>>::type>(
|
Projection(index, value));
|
}
|
|
// Calls
|
template <class... TArgs>
|
TNode<Object> CallRuntime(Runtime::FunctionId function,
|
SloppyTNode<Object> context, TArgs... args) {
|
return CallRuntimeImpl(function, context,
|
{implicit_cast<SloppyTNode<Object>>(args)...});
|
}
|
|
template <class... TArgs>
|
TNode<Object> CallRuntimeWithCEntry(Runtime::FunctionId function,
|
TNode<Code> centry,
|
SloppyTNode<Object> context,
|
TArgs... args) {
|
return CallRuntimeWithCEntryImpl(function, centry, context, {args...});
|
}
|
|
template <class... TArgs>
|
void TailCallRuntime(Runtime::FunctionId function,
|
SloppyTNode<Object> context, TArgs... args) {
|
int argc = static_cast<int>(sizeof...(args));
|
TNode<Int32T> arity = Int32Constant(argc);
|
return TailCallRuntimeImpl(function, arity, context,
|
{implicit_cast<SloppyTNode<Object>>(args)...});
|
}
|
|
template <class... TArgs>
|
void TailCallRuntime(Runtime::FunctionId function, TNode<Int32T> arity,
|
SloppyTNode<Object> context, TArgs... args) {
|
return TailCallRuntimeImpl(function, arity, context,
|
{implicit_cast<SloppyTNode<Object>>(args)...});
|
}
|
|
template <class... TArgs>
|
void TailCallRuntimeWithCEntry(Runtime::FunctionId function,
|
TNode<Code> centry, TNode<Object> context,
|
TArgs... args) {
|
int argc = sizeof...(args);
|
TNode<Int32T> arity = Int32Constant(argc);
|
return TailCallRuntimeWithCEntryImpl(
|
function, arity, centry, context,
|
{implicit_cast<SloppyTNode<Object>>(args)...});
|
}
|
|
//
|
// If context passed to CallStub is nullptr, it won't be passed to the stub.
|
//
|
|
template <class T = Object, class... TArgs>
|
TNode<T> CallStub(Callable const& callable, SloppyTNode<Object> context,
|
TArgs... args) {
|
TNode<Code> target = HeapConstant(callable.code());
|
return CallStub<T>(callable.descriptor(), target, context, args...);
|
}
|
|
template <class T = Object, class... TArgs>
|
TNode<T> CallStub(const CallInterfaceDescriptor& descriptor,
|
SloppyTNode<Code> target, SloppyTNode<Object> context,
|
TArgs... args) {
|
return UncheckedCast<T>(CallStubR(descriptor, 1, target, context, args...));
|
}
|
|
template <class... TArgs>
|
Node* CallStubR(const CallInterfaceDescriptor& descriptor, size_t result_size,
|
SloppyTNode<Code> target, SloppyTNode<Object> context,
|
TArgs... args) {
|
return CallStubRImpl(descriptor, result_size, target, context, {args...});
|
}
|
|
Node* CallStubN(const CallInterfaceDescriptor& descriptor, size_t result_size,
|
int input_count, Node* const* inputs);
|
|
template <class... TArgs>
|
void TailCallStub(Callable const& callable, SloppyTNode<Object> context,
|
TArgs... args) {
|
TNode<Code> target = HeapConstant(callable.code());
|
return TailCallStub(callable.descriptor(), target, context, args...);
|
}
|
|
template <class... TArgs>
|
void TailCallStub(const CallInterfaceDescriptor& descriptor,
|
SloppyTNode<Code> target, SloppyTNode<Object> context,
|
TArgs... args) {
|
return TailCallStubImpl(descriptor, target, context, {args...});
|
}
|
|
template <class... TArgs>
|
Node* TailCallBytecodeDispatch(const CallInterfaceDescriptor& descriptor,
|
Node* target, TArgs... args);
|
|
template <class... TArgs>
|
Node* TailCallStubThenBytecodeDispatch(
|
const CallInterfaceDescriptor& descriptor, Node* target, Node* context,
|
TArgs... args) {
|
return TailCallStubThenBytecodeDispatchImpl(descriptor, target, context,
|
{args...});
|
}
|
|
// Tailcalls to the given code object with JSCall linkage. The JS arguments
|
// (including receiver) are supposed to be already on the stack.
|
// This is a building block for implementing trampoline stubs that are
|
// installed instead of code objects with JSCall linkage.
|
// Note that no arguments adaption is going on here - all the JavaScript
|
// arguments are left on the stack unmodified. Therefore, this tail call can
|
// only be used after arguments adaptation has been performed already.
|
TNode<Object> TailCallJSCode(TNode<Code> code, TNode<Context> context,
|
TNode<JSFunction> function,
|
TNode<Object> new_target,
|
TNode<Int32T> arg_count);
|
|
template <class... TArgs>
|
Node* CallJS(Callable const& callable, Node* context, Node* function,
|
Node* receiver, TArgs... args) {
|
int argc = static_cast<int>(sizeof...(args));
|
Node* arity = Int32Constant(argc);
|
return CallStub(callable, context, function, arity, receiver, args...);
|
}
|
|
template <class... TArgs>
|
Node* ConstructJS(Callable const& callable, Node* context, Node* new_target,
|
TArgs... args) {
|
int argc = static_cast<int>(sizeof...(args));
|
Node* arity = Int32Constant(argc);
|
Node* receiver = LoadRoot(Heap::kUndefinedValueRootIndex);
|
|
// Construct(target, new_target, arity, receiver, arguments...)
|
return CallStub(callable, context, new_target, new_target, arity, receiver,
|
args...);
|
}
|
|
Node* CallCFunctionN(Signature<MachineType>* signature, int input_count,
|
Node* const* inputs);
|
|
// Call to a C function with one argument.
|
Node* CallCFunction1(MachineType return_type, MachineType arg0_type,
|
Node* function, Node* arg0);
|
|
// Call to a C function with one argument, while saving/restoring caller
|
// registers except the register used for return value.
|
Node* CallCFunction1WithCallerSavedRegisters(MachineType return_type,
|
MachineType arg0_type,
|
Node* function, Node* arg0,
|
SaveFPRegsMode mode);
|
|
// Call to a C function with two arguments.
|
Node* CallCFunction2(MachineType return_type, MachineType arg0_type,
|
MachineType arg1_type, Node* function, Node* arg0,
|
Node* arg1);
|
|
// Call to a C function with three arguments.
|
Node* CallCFunction3(MachineType return_type, MachineType arg0_type,
|
MachineType arg1_type, MachineType arg2_type,
|
Node* function, Node* arg0, Node* arg1, Node* arg2);
|
|
// Call to a C function with three arguments, while saving/restoring caller
|
// registers except the register used for return value.
|
Node* CallCFunction3WithCallerSavedRegisters(
|
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
|
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
|
SaveFPRegsMode mode);
|
|
// Call to a C function with four arguments.
|
Node* CallCFunction4(MachineType return_type, MachineType arg0_type,
|
MachineType arg1_type, MachineType arg2_type,
|
MachineType arg3_type, Node* function, Node* arg0,
|
Node* arg1, Node* arg2, Node* arg3);
|
|
// Call to a C function with five arguments.
|
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
|
MachineType arg1_type, MachineType arg2_type,
|
MachineType arg3_type, MachineType arg4_type,
|
Node* function, Node* arg0, Node* arg1, Node* arg2,
|
Node* arg3, Node* arg4);
|
|
// Call to a C function with six arguments.
|
Node* CallCFunction6(MachineType return_type, MachineType arg0_type,
|
MachineType arg1_type, MachineType arg2_type,
|
MachineType arg3_type, MachineType arg4_type,
|
MachineType arg5_type, Node* function, Node* arg0,
|
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
|
Node* arg5);
|
|
// Call to a C function with nine arguments.
|
Node* CallCFunction9(MachineType return_type, MachineType arg0_type,
|
MachineType arg1_type, MachineType arg2_type,
|
MachineType arg3_type, MachineType arg4_type,
|
MachineType arg5_type, MachineType arg6_type,
|
MachineType arg7_type, MachineType arg8_type,
|
Node* function, Node* arg0, Node* arg1, Node* arg2,
|
Node* arg3, Node* arg4, Node* arg5, Node* arg6,
|
Node* arg7, Node* arg8);
|
|
// Exception handling support.
|
void GotoIfException(Node* node, Label* if_exception,
|
Variable* exception_var = nullptr);
|
|
// Helpers which delegate to RawMachineAssembler.
|
Factory* factory() const;
|
Isolate* isolate() const;
|
Zone* zone() const;
|
|
CodeAssemblerState* state() { return state_; }
|
|
void BreakOnNode(int node_id);
|
|
bool UnalignedLoadSupported(MachineRepresentation rep) const;
|
bool UnalignedStoreSupported(MachineRepresentation rep) const;
|
|
protected:
|
void RegisterCallGenerationCallbacks(
|
const CodeAssemblerCallback& call_prologue,
|
const CodeAssemblerCallback& call_epilogue);
|
void UnregisterCallGenerationCallbacks();
|
|
bool Word32ShiftIsSafe() const;
|
PoisoningMitigationLevel poisoning_level() const;
|
|
bool IsJSFunctionCall() const;
|
|
private:
|
TNode<Object> CallRuntimeImpl(Runtime::FunctionId function,
|
TNode<Object> context,
|
std::initializer_list<TNode<Object>> args);
|
|
TNode<Object> CallRuntimeWithCEntryImpl(
|
Runtime::FunctionId function, TNode<Code> centry, TNode<Object> context,
|
std::initializer_list<TNode<Object>> args);
|
|
void TailCallRuntimeImpl(Runtime::FunctionId function, TNode<Int32T> arity,
|
TNode<Object> context,
|
std::initializer_list<TNode<Object>> args);
|
|
void TailCallRuntimeWithCEntryImpl(Runtime::FunctionId function,
|
TNode<Int32T> arity, TNode<Code> centry,
|
TNode<Object> context,
|
std::initializer_list<TNode<Object>> args);
|
|
void TailCallStubImpl(const CallInterfaceDescriptor& descriptor,
|
TNode<Code> target, TNode<Object> context,
|
std::initializer_list<Node*> args);
|
|
Node* TailCallStubThenBytecodeDispatchImpl(
|
const CallInterfaceDescriptor& descriptor, Node* target, Node* context,
|
std::initializer_list<Node*> args);
|
|
Node* CallStubRImpl(const CallInterfaceDescriptor& descriptor,
|
size_t result_size, SloppyTNode<Code> target,
|
SloppyTNode<Object> context,
|
std::initializer_list<Node*> args);
|
|
// These two don't have definitions and are here only for catching use cases
|
// where the cast is not necessary.
|
TNode<Int32T> Signed(TNode<Int32T> x);
|
TNode<Uint32T> Unsigned(TNode<Uint32T> x);
|
|
RawMachineAssembler* raw_assembler() const;
|
|
// Calls respective callback registered in the state.
|
void CallPrologue();
|
void CallEpilogue();
|
|
CodeAssemblerState* state_;
|
|
DISALLOW_COPY_AND_ASSIGN(CodeAssembler);
|
};
|
|
class CodeAssemblerVariable {
|
public:
|
explicit CodeAssemblerVariable(CodeAssembler* assembler,
|
MachineRepresentation rep);
|
CodeAssemblerVariable(CodeAssembler* assembler, MachineRepresentation rep,
|
Node* initial_value);
|
#if DEBUG
|
CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info,
|
MachineRepresentation rep);
|
CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info,
|
MachineRepresentation rep, Node* initial_value);
|
#endif // DEBUG
|
|
~CodeAssemblerVariable();
|
void Bind(Node* value);
|
Node* value() const;
|
MachineRepresentation rep() const;
|
bool IsBound() const;
|
|
private:
|
class Impl;
|
friend class CodeAssemblerLabel;
|
friend class CodeAssemblerState;
|
friend std::ostream& operator<<(std::ostream&, const Impl&);
|
friend std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable&);
|
Impl* impl_;
|
CodeAssemblerState* state_;
|
DISALLOW_COPY_AND_ASSIGN(CodeAssemblerVariable);
|
};
|
|
std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable&);
|
std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable::Impl&);
|
|
template <class T>
|
class TypedCodeAssemblerVariable : public CodeAssemblerVariable {
|
public:
|
TypedCodeAssemblerVariable(TNode<T> initial_value, CodeAssembler* assembler)
|
: CodeAssemblerVariable(assembler, MachineRepresentationOf<T>::value,
|
initial_value) {}
|
explicit TypedCodeAssemblerVariable(CodeAssembler* assembler)
|
: CodeAssemblerVariable(assembler, MachineRepresentationOf<T>::value) {}
|
#if DEBUG
|
TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info,
|
CodeAssembler* assembler)
|
: CodeAssemblerVariable(assembler, debug_info,
|
MachineRepresentationOf<T>::value) {}
|
TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info,
|
TNode<T> initial_value, CodeAssembler* assembler)
|
: CodeAssemblerVariable(assembler, debug_info,
|
MachineRepresentationOf<T>::value,
|
initial_value) {}
|
#endif // DEBUG
|
|
TNode<T> value() const {
|
return TNode<T>::UncheckedCast(CodeAssemblerVariable::value());
|
}
|
|
void operator=(TNode<T> value) { Bind(value); }
|
void operator=(const TypedCodeAssemblerVariable<T>& variable) {
|
Bind(variable.value());
|
}
|
|
private:
|
using CodeAssemblerVariable::Bind;
|
};
|
|
class CodeAssemblerLabel {
|
public:
|
enum Type { kDeferred, kNonDeferred };
|
|
explicit CodeAssemblerLabel(
|
CodeAssembler* assembler,
|
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred)
|
: CodeAssemblerLabel(assembler, 0, nullptr, type) {}
|
CodeAssemblerLabel(
|
CodeAssembler* assembler,
|
const CodeAssemblerVariableList& merged_variables,
|
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred)
|
: CodeAssemblerLabel(assembler, merged_variables.size(),
|
&(merged_variables[0]), type) {}
|
CodeAssemblerLabel(
|
CodeAssembler* assembler, size_t count,
|
CodeAssemblerVariable* const* vars,
|
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred);
|
CodeAssemblerLabel(
|
CodeAssembler* assembler,
|
std::initializer_list<CodeAssemblerVariable*> vars,
|
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred)
|
: CodeAssemblerLabel(assembler, vars.size(), vars.begin(), type) {}
|
CodeAssemblerLabel(
|
CodeAssembler* assembler, CodeAssemblerVariable* merged_variable,
|
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred)
|
: CodeAssemblerLabel(assembler, 1, &merged_variable, type) {}
|
~CodeAssemblerLabel();
|
|
inline bool is_bound() const { return bound_; }
|
inline bool is_used() const { return merge_count_ != 0; }
|
|
private:
|
friend class CodeAssembler;
|
|
void Bind();
|
#if DEBUG
|
void Bind(AssemblerDebugInfo debug_info);
|
#endif // DEBUG
|
void UpdateVariablesAfterBind();
|
void MergeVariables();
|
|
bool bound_;
|
size_t merge_count_;
|
CodeAssemblerState* state_;
|
RawMachineLabel* label_;
|
// Map of variables that need to be merged to their phi nodes (or placeholders
|
// for those phis).
|
std::map<CodeAssemblerVariable::Impl*, Node*> variable_phis_;
|
// Map of variables to the list of value nodes that have been added from each
|
// merge path in their order of merging.
|
std::map<CodeAssemblerVariable::Impl*, std::vector<Node*>> variable_merges_;
|
};
|
|
class V8_EXPORT_PRIVATE CodeAssemblerState {
|
public:
|
// Create with CallStub linkage.
|
// |result_size| specifies the number of results returned by the stub.
|
// TODO(rmcilroy): move result_size to the CallInterfaceDescriptor.
|
CodeAssemblerState(Isolate* isolate, Zone* zone,
|
const CallInterfaceDescriptor& descriptor, Code::Kind kind,
|
const char* name, PoisoningMitigationLevel poisoning_level,
|
uint32_t stub_key = 0,
|
int32_t builtin_index = Builtins::kNoBuiltinId);
|
|
// Create with JSCall linkage.
|
CodeAssemblerState(Isolate* isolate, Zone* zone, int parameter_count,
|
Code::Kind kind, const char* name,
|
PoisoningMitigationLevel poisoning_level,
|
int32_t builtin_index = Builtins::kNoBuiltinId);
|
|
~CodeAssemblerState();
|
|
const char* name() const { return name_; }
|
int parameter_count() const;
|
|
#if DEBUG
|
void PrintCurrentBlock(std::ostream& os);
|
bool InsideBlock();
|
#endif // DEBUG
|
void SetInitialDebugInformation(const char* msg, const char* file, int line);
|
|
private:
|
friend class CodeAssembler;
|
friend class CodeAssemblerLabel;
|
friend class CodeAssemblerVariable;
|
friend class CodeAssemblerTester;
|
|
CodeAssemblerState(Isolate* isolate, Zone* zone,
|
CallDescriptor* call_descriptor, Code::Kind kind,
|
const char* name, PoisoningMitigationLevel poisoning_level,
|
uint32_t stub_key, int32_t builtin_index);
|
|
std::unique_ptr<RawMachineAssembler> raw_assembler_;
|
Code::Kind kind_;
|
const char* name_;
|
uint32_t stub_key_;
|
int32_t builtin_index_;
|
bool code_generated_;
|
ZoneSet<CodeAssemblerVariable::Impl*> variables_;
|
CodeAssemblerCallback call_prologue_;
|
CodeAssemblerCallback call_epilogue_;
|
|
DISALLOW_COPY_AND_ASSIGN(CodeAssemblerState);
|
};
|
|
} // namespace compiler
|
} // namespace internal
|
} // namespace v8
|
|
#endif // V8_COMPILER_CODE_ASSEMBLER_H_
|