diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jit/CacheIR.h | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/CacheIR.h')
-rw-r--r-- | js/src/jit/CacheIR.h | 1914 |
1 files changed, 1914 insertions, 0 deletions
diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h new file mode 100644 index 0000000000..fa3441b26c --- /dev/null +++ b/js/src/jit/CacheIR.h @@ -0,0 +1,1914 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_CacheIR_h +#define jit_CacheIR_h + +#include "mozilla/Maybe.h" + +#include "NamespaceImports.h" + +#include "gc/Rooting.h" +#include "jit/CacheIROpsGenerated.h" +#include "jit/CompactBuffer.h" +#include "jit/ICState.h" +#include "jit/Simulator.h" +#include "js/experimental/JitInfo.h" +#include "js/friend/XrayJitInfo.h" // JS::XrayJitInfo +#include "js/ScalarType.h" // js::Scalar::Type +#include "vm/JSFunction.h" +#include "vm/Shape.h" +#include "wasm/TypedObject.h" + +enum class JSOp : uint8_t; + +namespace js { + +enum class ReferenceType; +enum class UnaryMathFunction : uint8_t; + +namespace jit { + +enum class BaselineCacheIRStubKind; +enum class InlinableNative : uint16_t; + +class ICCacheIRStub; +class ICScript; +class Label; +class MacroAssembler; +struct Register; + +// [SMDOC] CacheIR +// +// CacheIR is an (extremely simple) linear IR language for inline caches. +// From this IR, we can generate machine code for Baseline or Ion IC stubs. +// +// IRWriter +// -------- +// CacheIR bytecode is written using IRWriter. This class also records some +// metadata that's used by the Baseline and Ion code generators to generate +// (efficient) machine code. +// +// Sharing Baseline stub code +// -------------------------- +// Baseline stores data (like Shape* and fixed slot offsets) inside the ICStub +// structure, instead of embedding them directly in the JitCode. This makes +// Baseline IC code slightly slower, but allows us to share IC code between +// caches. CacheIR makes it easy to share code between stubs: stubs that have +// the same CacheIR (and CacheKind), will have the same Baseline stub code. +// +// Baseline stubs that share JitCode also share a CacheIRStubInfo structure. +// This class stores the CacheIR and the location of GC things stored in the +// stub, for the GC. +// +// JitZone has a CacheIRStubInfo* -> JitCode* weak map that's used to share both +// the IR and JitCode between Baseline CacheIR stubs. This HashMap owns the +// stubInfo (it uses UniquePtr), so once there are no references left to the +// shared stub code, we can also free the CacheIRStubInfo. +// +// Ion stubs +// --------- +// Unlike Baseline stubs, Ion stubs do not share stub code, and data stored in +// the IonICStub is baked into JIT code. This is one of the reasons Ion stubs +// are faster than Baseline stubs. Also note that Ion ICs contain more state +// (see IonGetPropertyIC for example) and use dynamic input/output registers, +// so sharing stub code for Ion would be much more difficult. + +// An OperandId represents either a cache input or a value returned by a +// CacheIR instruction. Most code should use the ValOperandId and ObjOperandId +// classes below. The ObjOperandId class represents an operand that's known to +// be an object, just as StringOperandId represents a known string, etc. +class OperandId { + protected: + static const uint16_t InvalidId = UINT16_MAX; + uint16_t id_; + + explicit OperandId(uint16_t id) : id_(id) {} + + public: + OperandId() : id_(InvalidId) {} + uint16_t id() const { return id_; } + bool valid() const { return id_ != InvalidId; } +}; + +class ValOperandId : public OperandId { + public: + ValOperandId() = default; + explicit ValOperandId(uint16_t id) : OperandId(id) {} +}; + +class ValueTagOperandId : public OperandId { + public: + ValueTagOperandId() = default; + explicit ValueTagOperandId(uint16_t id) : OperandId(id) {} +}; + +class ObjOperandId : public OperandId { + public: + ObjOperandId() = default; + explicit ObjOperandId(uint16_t id) : OperandId(id) {} + + bool operator==(const ObjOperandId& other) const { return id_ == other.id_; } + bool operator!=(const ObjOperandId& other) const { return id_ != other.id_; } +}; + +class NumberOperandId : public ValOperandId { + public: + NumberOperandId() = default; + explicit NumberOperandId(uint16_t id) : ValOperandId(id) {} +}; + +class StringOperandId : public OperandId { + public: + StringOperandId() = default; + explicit StringOperandId(uint16_t id) : OperandId(id) {} +}; + +class SymbolOperandId : public OperandId { + public: + SymbolOperandId() = default; + explicit SymbolOperandId(uint16_t id) : OperandId(id) {} +}; + +class BigIntOperandId : public OperandId { + public: + BigIntOperandId() = default; + explicit BigIntOperandId(uint16_t id) : OperandId(id) {} +}; + +class BooleanOperandId : public OperandId { + public: + BooleanOperandId() = default; + explicit BooleanOperandId(uint16_t id) : OperandId(id) {} +}; + +class Int32OperandId : public OperandId { + public: + Int32OperandId() = default; + explicit Int32OperandId(uint16_t id) : OperandId(id) {} +}; + +class TypedOperandId : public OperandId { + JSValueType type_; + + public: + MOZ_IMPLICIT TypedOperandId(ObjOperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_OBJECT) {} + MOZ_IMPLICIT TypedOperandId(StringOperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_STRING) {} + MOZ_IMPLICIT TypedOperandId(SymbolOperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_SYMBOL) {} + MOZ_IMPLICIT TypedOperandId(BigIntOperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_BIGINT) {} + MOZ_IMPLICIT TypedOperandId(BooleanOperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_BOOLEAN) {} + MOZ_IMPLICIT TypedOperandId(Int32OperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_INT32) {} + MOZ_IMPLICIT TypedOperandId(ValueTagOperandId val) + : OperandId(val.id()), type_(JSVAL_TYPE_UNKNOWN) {} + TypedOperandId(ValOperandId val, JSValueType type) + : OperandId(val.id()), type_(type) {} + + JSValueType type() const { return type_; } +}; + +#define CACHE_IR_KINDS(_) \ + _(GetProp) \ + _(GetElem) \ + _(GetName) \ + _(GetPropSuper) \ + _(GetElemSuper) \ + _(GetIntrinsic) \ + _(SetProp) \ + _(SetElem) \ + _(BindName) \ + _(In) \ + _(HasOwn) \ + _(CheckPrivateField) \ + _(TypeOf) \ + _(ToPropertyKey) \ + _(InstanceOf) \ + _(GetIterator) \ + _(OptimizeSpreadCall) \ + _(Compare) \ + _(ToBool) \ + _(Call) \ + _(UnaryArith) \ + _(BinaryArith) \ + _(NewObject) + +enum class CacheKind : uint8_t { +#define DEFINE_KIND(kind) kind, + CACHE_IR_KINDS(DEFINE_KIND) +#undef DEFINE_KIND +}; + +extern const char* const CacheKindNames[]; + +#ifdef DEBUG +extern size_t NumInputsForCacheKind(CacheKind kind); +#endif + +enum class CacheOp { +#define DEFINE_OP(op, ...) op, + CACHE_IR_OPS(DEFINE_OP) +#undef DEFINE_OP +}; + +// CacheIR opcode info that's read in performance-sensitive code. Stored as a +// single byte per op for better cache locality. +struct CacheIROpInfo { + uint8_t argLength : 7; + bool transpile : 1; +}; +static_assert(sizeof(CacheIROpInfo) == 1); +extern const CacheIROpInfo CacheIROpInfos[]; + +extern const char* const CacheIROpNames[]; +extern const uint32_t CacheIROpHealth[]; + +class StubField { + public: + enum class Type : uint8_t { + // These fields take up a single word. + RawInt32, + RawPointer, + Shape, + ObjectGroup, + JSObject, + Symbol, + String, + BaseScript, + Id, + + // These fields take up 64 bits on all platforms. + RawInt64, + First64BitType = RawInt64, + Value, + + Limit + }; + + static bool sizeIsWord(Type type) { + MOZ_ASSERT(type != Type::Limit); + return type < Type::First64BitType; + } + + static bool sizeIsInt64(Type type) { + MOZ_ASSERT(type != Type::Limit); + return type >= Type::First64BitType; + } + + static size_t sizeInBytes(Type type) { + if (sizeIsWord(type)) { + return sizeof(uintptr_t); + } + MOZ_ASSERT(sizeIsInt64(type)); + return sizeof(int64_t); + } + + private: + uint64_t data_; + Type type_; + + public: + StubField(uint64_t data, Type type) : data_(data), type_(type) { + MOZ_ASSERT_IF(sizeIsWord(), data <= UINTPTR_MAX); + } + + Type type() const { return type_; } + + bool sizeIsWord() const { return sizeIsWord(type_); } + bool sizeIsInt64() const { return sizeIsInt64(type_); } + + uintptr_t asWord() const { + MOZ_ASSERT(sizeIsWord()); + return uintptr_t(data_); + } + uint64_t asInt64() const { + MOZ_ASSERT(sizeIsInt64()); + return data_; + } +} JS_HAZ_GC_POINTER; + +// This class is used to wrap up information about a call to make it +// easier to convey from one function to another. (In particular, +// CacheIRWriter encodes the CallFlags in CacheIR, and CacheIRReader +// decodes them and uses them for compilation.) +class CallFlags { + public: + enum ArgFormat : uint8_t { + Unknown, + Standard, + Spread, + FunCall, + FunApplyArgs, + FunApplyArray, + LastArgFormat = FunApplyArray + }; + + CallFlags() = default; + explicit CallFlags(ArgFormat format) : argFormat_(format) {} + CallFlags(bool isConstructing, bool isSpread, bool isSameRealm = false, + bool needsUninitializedThis = false) + : argFormat_(isSpread ? Spread : Standard), + isConstructing_(isConstructing), + isSameRealm_(isSameRealm), + needsUninitializedThis_(needsUninitializedThis) {} + + ArgFormat getArgFormat() const { return argFormat_; } + bool isConstructing() const { + MOZ_ASSERT_IF(isConstructing_, + argFormat_ == Standard || argFormat_ == Spread); + return isConstructing_; + } + bool isSameRealm() const { return isSameRealm_; } + + bool needsUninitializedThis() const { return needsUninitializedThis_; } + void setNeedsUninitializedThis() { needsUninitializedThis_ = true; } + + uint8_t toByte() const { + // See CacheIRReader::callFlags() + MOZ_ASSERT(argFormat_ != ArgFormat::Unknown); + uint8_t value = getArgFormat(); + if (isConstructing()) { + value |= CallFlags::IsConstructing; + } + if (isSameRealm()) { + value |= CallFlags::IsSameRealm; + } + if (needsUninitializedThis()) { + value |= CallFlags::NeedsUninitializedThis; + } + return value; + } + + private: + ArgFormat argFormat_ = ArgFormat::Unknown; + bool isConstructing_ = false; + bool isSameRealm_ = false; + bool needsUninitializedThis_ = false; + + // Used for encoding/decoding + static const uint8_t ArgFormatBits = 4; + static const uint8_t ArgFormatMask = (1 << ArgFormatBits) - 1; + static_assert(LastArgFormat <= ArgFormatMask, "Not enough arg format bits"); + static const uint8_t IsConstructing = 1 << 5; + static const uint8_t IsSameRealm = 1 << 6; + static const uint8_t NeedsUninitializedThis = 1 << 7; + + friend class CacheIRReader; + friend class CacheIRWriter; +}; + +enum class AttachDecision { + // We cannot attach a stub. + NoAction, + + // We can attach a stub. + Attach, + + // We cannot currently attach a stub, but we expect to be able to do so in the + // future. In this case, we do not call trackNotAttached(). + TemporarilyUnoptimizable, + + // We want to attach a stub, but the result of the operation is + // needed to generate that stub. For example, AddSlot needs to know + // the resulting shape. Note: the attached stub will inspect the + // inputs to the operation, so most input checks should be done + // before the actual operation, with only minimal checks remaining + // for the deferred portion. This prevents arbitrary scripted code + // run by the operation from interfering with the conditions being + // checked. + Deferred +}; + +// If the input expression evaluates to an AttachDecision other than NoAction, +// return that AttachDecision. If it is NoAction, do nothing. +#define TRY_ATTACH(expr) \ + do { \ + AttachDecision tryAttachTempResult_ = expr; \ + if (tryAttachTempResult_ != AttachDecision::NoAction) { \ + return tryAttachTempResult_; \ + } \ + } while (0) + +// Set of arguments supported by GetIndexOfArgument. +// Support for higher argument indices can be added easily, but is currently +// unneeded. +enum class ArgumentKind : uint8_t { + Callee, + This, + NewTarget, + Arg0, + Arg1, + Arg2, + Arg3, + Arg4, + Arg5, + Arg6, + Arg7, + NumKinds +}; + +const uint8_t ArgumentKindArgIndexLimit = + uint8_t(ArgumentKind::NumKinds) - uint8_t(ArgumentKind::Arg0); + +inline ArgumentKind ArgumentKindForArgIndex(uint32_t idx) { + MOZ_ASSERT(idx < ArgumentKindArgIndexLimit); + return ArgumentKind(uint32_t(ArgumentKind::Arg0) + idx); +} + +// This function calculates the index of an argument based on the call flags. +// addArgc is an out-parameter, indicating whether the value of argc should +// be added to the return value to find the actual index. +inline int32_t GetIndexOfArgument(ArgumentKind kind, CallFlags flags, + bool* addArgc) { + // *** STACK LAYOUT (bottom to top) *** ******** INDEX ******** + // Callee <-- argc+1 + isConstructing + // ThisValue <-- argc + isConstructing + // Args: | Arg0 | | ArgArray | <-- argc-1 + isConstructing + // | Arg1 | --or-- | | <-- argc-2 + isConstructing + // | ... | | (if spread | <-- ... + // | ArgN | | call) | <-- 0 + isConstructing + // NewTarget (only if constructing) <-- 0 (if it exists) + // + // If this is a spread call, then argc is always 1, and we can calculate the + // index directly. If this is not a spread call, then the index of any + // argument other than NewTarget depends on argc. + + // First we determine whether the caller needs to add argc. + switch (flags.getArgFormat()) { + case CallFlags::Standard: + *addArgc = true; + break; + case CallFlags::Spread: + // Spread calls do not have Arg1 or higher. + MOZ_ASSERT(kind <= ArgumentKind::Arg0); + *addArgc = false; + break; + case CallFlags::Unknown: + case CallFlags::FunCall: + case CallFlags::FunApplyArgs: + case CallFlags::FunApplyArray: + MOZ_CRASH("Currently unreachable"); + break; + } + + // Second, we determine the offset relative to argc. + bool hasArgumentArray = !*addArgc; + switch (kind) { + case ArgumentKind::Callee: + return flags.isConstructing() + hasArgumentArray + 1; + case ArgumentKind::This: + return flags.isConstructing() + hasArgumentArray; + case ArgumentKind::Arg0: + return flags.isConstructing() + hasArgumentArray - 1; + case ArgumentKind::Arg1: + return flags.isConstructing() + hasArgumentArray - 2; + case ArgumentKind::Arg2: + return flags.isConstructing() + hasArgumentArray - 3; + case ArgumentKind::Arg3: + return flags.isConstructing() + hasArgumentArray - 4; + case ArgumentKind::Arg4: + return flags.isConstructing() + hasArgumentArray - 5; + case ArgumentKind::Arg5: + return flags.isConstructing() + hasArgumentArray - 6; + case ArgumentKind::Arg6: + return flags.isConstructing() + hasArgumentArray - 7; + case ArgumentKind::Arg7: + return flags.isConstructing() + hasArgumentArray - 8; + case ArgumentKind::NewTarget: + MOZ_ASSERT(flags.isConstructing()); + *addArgc = false; + return 0; + default: + MOZ_CRASH("Invalid argument kind"); + } +} + +// We use this enum as GuardClass operand, instead of storing Class* pointers +// in the IR, to keep the IR compact and the same size on all platforms. +enum class GuardClassKind : uint8_t { + Array, + ArrayBuffer, + SharedArrayBuffer, + DataView, + MappedArguments, + UnmappedArguments, + WindowProxy, + JSFunction, +}; + +// Some ops refer to shapes that might be in other zones. Instead of putting +// cross-zone pointers in the caches themselves (which would complicate tracing +// enormously), these ops instead contain wrappers for objects in the target +// zone, which refer to the actual shape via a reserved slot. +JSObject* NewWrapperWithObjectShape(JSContext* cx, HandleNativeObject obj); + +void LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst, + Label* failure); + +#ifdef JS_SIMULATOR +bool CallAnyNative(JSContext* cx, unsigned argc, Value* vp); +#endif + +// Class to record CacheIR + some additional metadata for code generation. +class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { + JSContext* cx_; + CompactBufferWriter buffer_; + + uint32_t nextOperandId_; + uint32_t nextInstructionId_; + uint32_t numInputOperands_; + + // The data (shapes, slot offsets, etc.) that will be stored in the ICStub. + Vector<StubField, 8, SystemAllocPolicy> stubFields_; + size_t stubDataSize_; + + // For each operand id, record which instruction accessed it last. This + // information greatly improves register allocation. + Vector<uint32_t, 8, SystemAllocPolicy> operandLastUsed_; + + // OperandId and stub offsets are stored in a single byte, so make sure + // this doesn't overflow. We use a very conservative limit for now. + static const size_t MaxOperandIds = 20; + static const size_t MaxStubDataSizeInBytes = 20 * sizeof(uintptr_t); + bool tooLarge_; + + // Assume this stub can't be trial inlined until we see a scripted call/inline + // instruction. + TrialInliningState trialInliningState_ = TrialInliningState::Failure; + + // Basic caching to avoid quadatic lookup behaviour in readStubFieldForIon. + mutable uint32_t lastOffset_; + mutable uint32_t lastIndex_; + +#ifdef DEBUG + // Information for assertLengthMatches. + mozilla::Maybe<CacheOp> currentOp_; + size_t currentOpArgsStart_ = 0; +#endif + + void assertSameCompartment(JSObject*); + + void writeOp(CacheOp op) { + buffer_.writeUnsigned15Bit(uint32_t(op)); + nextInstructionId_++; +#ifdef DEBUG + MOZ_ASSERT(currentOp_.isNothing(), "Missing call to assertLengthMatches?"); + currentOp_.emplace(op); + currentOpArgsStart_ = buffer_.length(); +#endif + } + + void assertLengthMatches() { +#ifdef DEBUG + // After writing arguments, assert the length matches CacheIROpArgLengths. + size_t expectedLen = CacheIROpInfos[size_t(*currentOp_)].argLength; + MOZ_ASSERT_IF(!failed(), + buffer_.length() - currentOpArgsStart_ == expectedLen); + currentOp_.reset(); +#endif + } + + void writeOperandId(OperandId opId) { + if (opId.id() < MaxOperandIds) { + static_assert(MaxOperandIds <= UINT8_MAX, + "operand id must fit in a single byte"); + buffer_.writeByte(opId.id()); + } else { + tooLarge_ = true; + return; + } + if (opId.id() >= operandLastUsed_.length()) { + buffer_.propagateOOM(operandLastUsed_.resize(opId.id() + 1)); + if (buffer_.oom()) { + return; + } + } + MOZ_ASSERT(nextInstructionId_ > 0); + operandLastUsed_[opId.id()] = nextInstructionId_ - 1; + } + + void writeCallFlagsImm(CallFlags flags) { buffer_.writeByte(flags.toByte()); } + + uint8_t addStubField(uint64_t value, StubField::Type fieldType) { + uint8_t offset = 0; + size_t newStubDataSize = stubDataSize_ + StubField::sizeInBytes(fieldType); + if (newStubDataSize < MaxStubDataSizeInBytes) { + buffer_.propagateOOM(stubFields_.append(StubField(value, fieldType))); + MOZ_ASSERT((stubDataSize_ % sizeof(uintptr_t)) == 0); + offset = stubDataSize_ / sizeof(uintptr_t); + buffer_.writeByte(offset); + stubDataSize_ = newStubDataSize; + } else { + tooLarge_ = true; + } + return offset; + } + + void writeShapeField(Shape* shape) { + MOZ_ASSERT(shape); + addStubField(uintptr_t(shape), StubField::Type::Shape); + } + void writeGroupField(ObjectGroup* group) { + MOZ_ASSERT(group); + addStubField(uintptr_t(group), StubField::Type::ObjectGroup); + } + void writeObjectField(JSObject* obj) { + MOZ_ASSERT(obj); + assertSameCompartment(obj); + addStubField(uintptr_t(obj), StubField::Type::JSObject); + } + void writeStringField(JSString* str) { + MOZ_ASSERT(str); + addStubField(uintptr_t(str), StubField::Type::String); + } + void writeSymbolField(JS::Symbol* sym) { + MOZ_ASSERT(sym); + addStubField(uintptr_t(sym), StubField::Type::Symbol); + } + void writeBaseScriptField(BaseScript* script) { + MOZ_ASSERT(script); + addStubField(uintptr_t(script), StubField::Type::BaseScript); + } + void writeRawInt32Field(uint32_t val) { + addStubField(val, StubField::Type::RawInt32); + } + void writeRawPointerField(const void* ptr) { + addStubField(uintptr_t(ptr), StubField::Type::RawPointer); + } + void writeIdField(jsid id) { + addStubField(uintptr_t(JSID_BITS(id)), StubField::Type::Id); + } + void writeValueField(const Value& val) { + addStubField(val.asRawBits(), StubField::Type::Value); + } + void writeRawInt64Field(uint64_t val) { + addStubField(val, StubField::Type::RawInt64); + } + + void writeJSOpImm(JSOp op) { + static_assert(sizeof(JSOp) == sizeof(uint8_t), "JSOp must fit in a byte"); + buffer_.writeByte(uint8_t(op)); + } + void writeGuardClassKindImm(GuardClassKind kind) { + static_assert(sizeof(GuardClassKind) == sizeof(uint8_t), + "GuardClassKind must fit in a byte"); + buffer_.writeByte(uint8_t(kind)); + } + void writeValueTypeImm(ValueType type) { + static_assert(sizeof(ValueType) == sizeof(uint8_t), + "ValueType must fit in uint8_t"); + buffer_.writeByte(uint8_t(type)); + } + void writeJSWhyMagicImm(JSWhyMagic whyMagic) { + static_assert(JS_WHY_MAGIC_COUNT <= UINT8_MAX, + "JSWhyMagic must fit in uint8_t"); + buffer_.writeByte(uint8_t(whyMagic)); + } + void writeScalarTypeImm(Scalar::Type type) { + MOZ_ASSERT(size_t(type) <= UINT8_MAX); + buffer_.writeByte(uint8_t(type)); + } + void writeUnaryMathFunctionImm(UnaryMathFunction fun) { + static_assert(sizeof(UnaryMathFunction) == sizeof(uint8_t), + "UnaryMathFunction must fit in a byte"); + buffer_.writeByte(uint8_t(fun)); + } + void writeBoolImm(bool b) { buffer_.writeByte(uint32_t(b)); } + + void writeByteImm(uint32_t b) { + MOZ_ASSERT(b <= UINT8_MAX); + buffer_.writeByte(b); + } + + void writeInt32Imm(int32_t i32) { buffer_.writeFixedUint32_t(i32); } + void writeUInt32Imm(uint32_t u32) { buffer_.writeFixedUint32_t(u32); } + void writePointer(const void* ptr) { buffer_.writeRawPointer(ptr); } + + void writeJSNativeImm(JSNative native) { + writePointer(JS_FUNC_TO_DATA_PTR(void*, native)); + } + void writeStaticStringImm(const char* str) { writePointer(str); } + + void writeWasmValTypeImm(wasm::ValType::Kind kind) { + static_assert(unsigned(wasm::TypeCode::Limit) <= UINT8_MAX); + buffer_.writeByte(uint8_t(kind)); + } + + uint32_t newOperandId() { return nextOperandId_++; } + + CacheIRWriter(const CacheIRWriter&) = delete; + CacheIRWriter& operator=(const CacheIRWriter&) = delete; + + public: + explicit CacheIRWriter(JSContext* cx) + : CustomAutoRooter(cx), + cx_(cx), + nextOperandId_(0), + nextInstructionId_(0), + numInputOperands_(0), + stubDataSize_(0), + tooLarge_(false), + lastOffset_(0), + lastIndex_(0) {} + + bool failed() const { return buffer_.oom() || tooLarge_; } + + TrialInliningState trialInliningState() const { return trialInliningState_; } + + uint32_t numInputOperands() const { return numInputOperands_; } + uint32_t numOperandIds() const { return nextOperandId_; } + uint32_t numInstructions() const { return nextInstructionId_; } + + size_t numStubFields() const { return stubFields_.length(); } + StubField::Type stubFieldType(uint32_t i) const { + return stubFields_[i].type(); + } + + uint32_t setInputOperandId(uint32_t op) { + MOZ_ASSERT(op == nextOperandId_); + nextOperandId_++; + numInputOperands_++; + return op; + } + + void trace(JSTracer* trc) override { + // For now, assert we only GC before we append stub fields. + MOZ_RELEASE_ASSERT(stubFields_.empty()); + } + + size_t stubDataSize() const { return stubDataSize_; } + void copyStubData(uint8_t* dest) const; + bool stubDataEquals(const uint8_t* stubData) const; + + bool operandIsDead(uint32_t operandId, uint32_t currentInstruction) const { + if (operandId >= operandLastUsed_.length()) { + return false; + } + return currentInstruction > operandLastUsed_[operandId]; + } + + const uint8_t* codeStart() const { + MOZ_ASSERT(!failed()); + return buffer_.buffer(); + } + + const uint8_t* codeEnd() const { + MOZ_ASSERT(!failed()); + return buffer_.buffer() + buffer_.length(); + } + + uint32_t codeLength() const { + MOZ_ASSERT(!failed()); + return buffer_.length(); + } + + // This should not be used when compiling Baseline code, as Baseline code + // shouldn't bake in stub values. + StubField readStubFieldForIon(uint32_t offset, StubField::Type type) const; + + ObjOperandId guardToObject(ValOperandId input) { + guardToObject_(input); + return ObjOperandId(input.id()); + } + + StringOperandId guardToString(ValOperandId input) { + guardToString_(input); + return StringOperandId(input.id()); + } + + SymbolOperandId guardToSymbol(ValOperandId input) { + guardToSymbol_(input); + return SymbolOperandId(input.id()); + } + + BigIntOperandId guardToBigInt(ValOperandId input) { + guardToBigInt_(input); + return BigIntOperandId(input.id()); + } + + BooleanOperandId guardToBoolean(ValOperandId input) { + guardToBoolean_(input); + return BooleanOperandId(input.id()); + } + + Int32OperandId guardToInt32(ValOperandId input) { + guardToInt32_(input); + return Int32OperandId(input.id()); + } + + NumberOperandId guardIsNumber(ValOperandId input) { + guardIsNumber_(input); + return NumberOperandId(input.id()); + } + + ValOperandId boxObject(ObjOperandId input) { + return ValOperandId(input.id()); + } + + void guardShapeForClass(ObjOperandId obj, Shape* shape) { + // Guard shape to ensure that object class is unchanged. This is true + // for all shapes. + guardShape(obj, shape); + } + + void guardShapeForOwnProperties(ObjOperandId obj, Shape* shape) { + // Guard shape to detect changes to (non-dense) own properties. This + // also implies |guardShapeForClass|. + MOZ_ASSERT(shape->getObjectClass()->isNative()); + guardShape(obj, shape); + } + + public: + // Instead of calling guardGroup manually, use (or create) a specialization + // below to clarify what constraint the group guard is implying. + void guardGroupForProto(ObjOperandId obj, ObjectGroup* group) { + guardGroup(obj, group); + } + + void guardGroupForLayout(ObjOperandId obj, ObjectGroup* group) { + MOZ_ASSERT(IsTypedObjectClass(group->clasp())); + guardGroup(obj, group); + } + + static uint32_t encodeNargsAndFlags(JSFunction* fun) { + static_assert(JSFunction::NArgsBits == 16); + static_assert(sizeof(decltype(fun->flags().toRaw())) == sizeof(uint16_t)); + return (uint32_t(fun->nargs()) << 16) | fun->flags().toRaw(); + } + + void guardSpecificFunction(ObjOperandId obj, JSFunction* expected) { + // Guard object is a specific function. This implies immutable fields on + // the JSFunction struct itself are unchanged. + // Bake in the nargs and FunctionFlags so Warp can use them off-main thread, + // instead of directly using the JSFunction fields. + uint32_t nargsAndFlags = encodeNargsAndFlags(expected); + guardSpecificFunction_(obj, expected, nargsAndFlags); + } + + void guardFunctionScript(ObjOperandId fun, BaseScript* expected) { + // Guard function has a specific BaseScript. This implies immutable fields + // on the JSFunction struct itself are unchanged and are equivalent for + // lambda clones. + // Bake in the nargs and FunctionFlags so Warp can use them off-main thread, + // instead of directly using the JSFunction fields. + uint32_t nargsAndFlags = encodeNargsAndFlags(expected->function()); + guardFunctionScript_(fun, expected, nargsAndFlags); + } + + ValOperandId loadArgumentFixedSlot( + ArgumentKind kind, uint32_t argc, + CallFlags flags = CallFlags(CallFlags::Standard)) { + bool addArgc; + int32_t slotIndex = GetIndexOfArgument(kind, flags, &addArgc); + if (addArgc) { + slotIndex += argc; + } + MOZ_ASSERT(slotIndex >= 0); + MOZ_ASSERT(slotIndex <= UINT8_MAX); + return loadArgumentFixedSlot_(slotIndex); + } + + ValOperandId loadStandardCallArgument(uint32_t index, uint32_t argc) { + int32_t slotIndex = -int32_t(index + 1); + slotIndex += argc; + MOZ_ASSERT(slotIndex >= 0); + MOZ_ASSERT(slotIndex <= UINT8_MAX); + return loadArgumentFixedSlot_(slotIndex); + } + + ValOperandId loadArgumentDynamicSlot( + ArgumentKind kind, Int32OperandId argcId, + CallFlags flags = CallFlags(CallFlags::Standard)) { + bool addArgc; + int32_t slotIndex = GetIndexOfArgument(kind, flags, &addArgc); + if (addArgc) { + return loadArgumentDynamicSlot_(argcId, slotIndex); + } + return loadArgumentFixedSlot_(slotIndex); + } + + void callScriptedFunction(ObjOperandId callee, Int32OperandId argc, + CallFlags flags) { + callScriptedFunction_(callee, argc, flags); + trialInliningState_ = TrialInliningState::Candidate; + } + + void callInlinedFunction(ObjOperandId callee, Int32OperandId argc, + ICScript* icScript, CallFlags flags) { + callInlinedFunction_(callee, argc, icScript, flags); + trialInliningState_ = TrialInliningState::Inlined; + } + + void callNativeFunction(ObjOperandId calleeId, Int32OperandId argc, JSOp op, + HandleFunction calleeFunc, CallFlags flags) { + // Some native functions can be implemented faster if we know that + // the return value is ignored. + bool ignoresReturnValue = + op == JSOp::CallIgnoresRv && calleeFunc->hasJitInfo() && + calleeFunc->jitInfo()->type() == JSJitInfo::IgnoresReturnValueNative; + +#ifdef JS_SIMULATOR + // The simulator requires VM calls to be redirected to a special + // swi instruction to handle them, so we store the redirected + // pointer in the stub and use that instead of the original one. + // If we are calling the ignoresReturnValue version of a native + // function, we bake it into the redirected pointer. + // (See BaselineCacheIRCompiler::emitCallNativeFunction.) + JSNative target = ignoresReturnValue + ? calleeFunc->jitInfo()->ignoresReturnValueMethod + : calleeFunc->native(); + void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, target); + void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3); + callNativeFunction_(calleeId, argc, flags, redirected); +#else + // If we are not running in the simulator, we generate different jitcode + // to find the ignoresReturnValue version of a native function. + callNativeFunction_(calleeId, argc, flags, ignoresReturnValue); +#endif + } + + void callDOMFunction(ObjOperandId calleeId, Int32OperandId argc, + ObjOperandId thisObjId, HandleFunction calleeFunc, + CallFlags flags) { +#ifdef JS_SIMULATOR + void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, calleeFunc->native()); + void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3); + callDOMFunction_(calleeId, argc, thisObjId, flags, redirected); +#else + callDOMFunction_(calleeId, argc, thisObjId, flags); +#endif + } + + void callAnyNativeFunction(ObjOperandId calleeId, Int32OperandId argc, + CallFlags flags) { + MOZ_ASSERT(!flags.isSameRealm()); +#ifdef JS_SIMULATOR + // The simulator requires native calls to be redirected to a + // special swi instruction. If we are calling an arbitrary native + // function, we can't wrap the real target ahead of time, so we + // call a wrapper function (CallAnyNative) that calls the target + // itself, and redirect that wrapper. + JSNative target = CallAnyNative; + void* rawPtr = JS_FUNC_TO_DATA_PTR(void*, target); + void* redirected = Simulator::RedirectNativeFunction(rawPtr, Args_General3); + callNativeFunction_(calleeId, argc, flags, redirected); +#else + callNativeFunction_(calleeId, argc, flags, + /* ignoresReturnValue = */ false); +#endif + } + + void callClassHook(ObjOperandId calleeId, Int32OperandId argc, JSNative hook, + CallFlags flags) { + MOZ_ASSERT(!flags.isSameRealm()); + void* target = JS_FUNC_TO_DATA_PTR(void*, hook); +#ifdef JS_SIMULATOR + // The simulator requires VM calls to be redirected to a special + // swi instruction to handle them, so we store the redirected + // pointer in the stub and use that instead of the original one. + target = Simulator::RedirectNativeFunction(target, Args_General3); +#endif + callClassHook_(calleeId, argc, flags, target); + } + + void callScriptedGetterResult(ValOperandId receiver, JSFunction* getter, + bool sameRealm) { + MOZ_ASSERT(getter->hasJitEntry()); + uint32_t nargsAndFlags = encodeNargsAndFlags(getter); + callScriptedGetterResult_(receiver, getter, sameRealm, nargsAndFlags); + trialInliningState_ = TrialInliningState::Candidate; + } + + void callInlinedGetterResult(ValOperandId receiver, JSFunction* getter, + ICScript* icScript, bool sameRealm) { + MOZ_ASSERT(getter->hasJitEntry()); + uint32_t nargsAndFlags = encodeNargsAndFlags(getter); + callInlinedGetterResult_(receiver, getter, icScript, sameRealm, + nargsAndFlags); + trialInliningState_ = TrialInliningState::Inlined; + } + + void callNativeGetterResult(ValOperandId receiver, JSFunction* getter, + bool sameRealm) { + MOZ_ASSERT(getter->isNativeWithoutJitEntry()); + uint32_t nargsAndFlags = encodeNargsAndFlags(getter); + callNativeGetterResult_(receiver, getter, sameRealm, nargsAndFlags); + } + + void callScriptedSetter(ObjOperandId receiver, JSFunction* setter, + ValOperandId rhs, bool sameRealm) { + MOZ_ASSERT(setter->hasJitEntry()); + uint32_t nargsAndFlags = encodeNargsAndFlags(setter); + callScriptedSetter_(receiver, setter, rhs, sameRealm, nargsAndFlags); + trialInliningState_ = TrialInliningState::Candidate; + } + + void callInlinedSetter(ObjOperandId receiver, JSFunction* setter, + ValOperandId rhs, ICScript* icScript, bool sameRealm) { + MOZ_ASSERT(setter->hasJitEntry()); + uint32_t nargsAndFlags = encodeNargsAndFlags(setter); + callInlinedSetter_(receiver, setter, rhs, icScript, sameRealm, + nargsAndFlags); + trialInliningState_ = TrialInliningState::Inlined; + } + + void callNativeSetter(ObjOperandId receiver, JSFunction* setter, + ValOperandId rhs, bool sameRealm) { + MOZ_ASSERT(setter->isNativeWithoutJitEntry()); + uint32_t nargsAndFlags = encodeNargsAndFlags(setter); + callNativeSetter_(receiver, setter, rhs, sameRealm, nargsAndFlags); + } + + void metaScriptedTemplateObject(JSFunction* callee, + JSObject* templateObject) { + metaTwoByte_(callee, templateObject); + } + friend class CacheIRCloner; + + CACHE_IR_WRITER_GENERATED +}; + +class CacheIRStubInfo; + +// Helper class for reading CacheIR bytecode. +class MOZ_RAII CacheIRReader { + CompactBufferReader buffer_; + + CacheIRReader(const CacheIRReader&) = delete; + CacheIRReader& operator=(const CacheIRReader&) = delete; + + public: + CacheIRReader(const uint8_t* start, const uint8_t* end) + : buffer_(start, end) {} + explicit CacheIRReader(const CacheIRWriter& writer) + : CacheIRReader(writer.codeStart(), writer.codeEnd()) {} + explicit CacheIRReader(const CacheIRStubInfo* stubInfo); + + bool more() const { return buffer_.more(); } + + CacheOp readOp() { return CacheOp(buffer_.readUnsigned15Bit()); } + + // Skip data not currently used. + void skip() { buffer_.readByte(); } + void skip(uint32_t skipLength) { + if (skipLength > 0) { + buffer_.seek(buffer_.currentPosition(), skipLength); + } + } + + ValOperandId valOperandId() { return ValOperandId(buffer_.readByte()); } + ValueTagOperandId valueTagOperandId() { + return ValueTagOperandId(buffer_.readByte()); + } + + ObjOperandId objOperandId() { return ObjOperandId(buffer_.readByte()); } + NumberOperandId numberOperandId() { + return NumberOperandId(buffer_.readByte()); + } + StringOperandId stringOperandId() { + return StringOperandId(buffer_.readByte()); + } + + SymbolOperandId symbolOperandId() { + return SymbolOperandId(buffer_.readByte()); + } + + BigIntOperandId bigIntOperandId() { + return BigIntOperandId(buffer_.readByte()); + } + + BooleanOperandId booleanOperandId() { + return BooleanOperandId(buffer_.readByte()); + } + + Int32OperandId int32OperandId() { return Int32OperandId(buffer_.readByte()); } + + uint32_t rawOperandId() { return buffer_.readByte(); } + + uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); } + GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); } + JSValueType jsValueType() { return JSValueType(buffer_.readByte()); } + ValueType valueType() { return ValueType(buffer_.readByte()); } + wasm::ValType::Kind wasmValType() { + return wasm::ValType::Kind(buffer_.readByte()); + } + + Scalar::Type scalarType() { return Scalar::Type(buffer_.readByte()); } + uint32_t typeDescrKey() { return buffer_.readByte(); } + JSWhyMagic whyMagic() { return JSWhyMagic(buffer_.readByte()); } + JSOp jsop() { return JSOp(buffer_.readByte()); } + int32_t int32Immediate() { return int32_t(buffer_.readFixedUint32_t()); } + uint32_t uint32Immediate() { return buffer_.readFixedUint32_t(); } + void* pointer() { return buffer_.readRawPointer(); } + + template <typename MetaKind> + MetaKind metaKind() { + return MetaKind(buffer_.readByte()); + } + + UnaryMathFunction unaryMathFunction() { + return UnaryMathFunction(buffer_.readByte()); + } + + CallFlags callFlags() { + // See CacheIRWriter::writeCallFlagsImm() + uint8_t encoded = buffer_.readByte(); + CallFlags::ArgFormat format = + CallFlags::ArgFormat(encoded & CallFlags::ArgFormatMask); + bool isConstructing = encoded & CallFlags::IsConstructing; + bool isSameRealm = encoded & CallFlags::IsSameRealm; + bool needsUninitializedThis = encoded & CallFlags::NeedsUninitializedThis; + MOZ_ASSERT_IF(needsUninitializedThis, isConstructing); + switch (format) { + case CallFlags::Unknown: + MOZ_CRASH("Unexpected call flags"); + case CallFlags::Standard: + return CallFlags(isConstructing, /*isSpread =*/false, isSameRealm, + needsUninitializedThis); + case CallFlags::Spread: + return CallFlags(isConstructing, /*isSpread =*/true, isSameRealm, + needsUninitializedThis); + default: + // The existing non-standard argument formats (FunCall and FunApply) + // can't be constructors and have no support for isSameRealm. + MOZ_ASSERT(!isConstructing && !isSameRealm); + return CallFlags(format); + } + } + + uint8_t readByte() { return buffer_.readByte(); } + bool readBool() { + uint8_t b = buffer_.readByte(); + MOZ_ASSERT(b <= 1); + return bool(b); + } + + bool matchOp(CacheOp op) { + const uint8_t* pos = buffer_.currentPosition(); + if (readOp() == op) { + return true; + } + buffer_.seek(pos, 0); + return false; + } + + bool matchOp(CacheOp op, OperandId id) { + const uint8_t* pos = buffer_.currentPosition(); + if (readOp() == op && buffer_.readByte() == id.id()) { + return true; + } + buffer_.seek(pos, 0); + return false; + } + + bool matchOpEither(CacheOp op1, CacheOp op2) { + const uint8_t* pos = buffer_.currentPosition(); + CacheOp op = readOp(); + if (op == op1 || op == op2) { + return true; + } + buffer_.seek(pos, 0); + return false; + } + const uint8_t* currentPosition() const { return buffer_.currentPosition(); } +}; + +class MOZ_RAII CacheIRCloner { + public: + explicit CacheIRCloner(ICCacheIRStub* stubInfo); + + void cloneOp(CacheOp op, CacheIRReader& reader, CacheIRWriter& writer); + + CACHE_IR_CLONE_GENERATED + + private: + const CacheIRStubInfo* stubInfo_; + const uint8_t* stubData_; + + uintptr_t readStubWord(uint32_t offset); + int64_t readStubInt64(uint32_t offset); + + Shape* getShapeField(uint32_t stubOffset); + ObjectGroup* getGroupField(uint32_t stubOffset); + JSObject* getObjectField(uint32_t stubOffset); + JSString* getStringField(uint32_t stubOffset); + JSAtom* getAtomField(uint32_t stubOffset); + PropertyName* getPropertyNameField(uint32_t stubOffset); + JS::Symbol* getSymbolField(uint32_t stubOffset); + BaseScript* getBaseScriptField(uint32_t stubOffset); + uint32_t getRawInt32Field(uint32_t stubOffset); + const void* getRawPointerField(uint32_t stubOffset); + jsid getIdField(uint32_t stubOffset); + const Value getValueField(uint32_t stubOffset); + uint64_t getRawInt64Field(uint32_t stubOffset); +}; + +class MOZ_RAII IRGenerator { + protected: + CacheIRWriter writer; + JSContext* cx_; + HandleScript script_; + jsbytecode* pc_; + CacheKind cacheKind_; + ICState::Mode mode_; + + IRGenerator(const IRGenerator&) = delete; + IRGenerator& operator=(const IRGenerator&) = delete; + + bool maybeGuardInt32Index(const Value& index, ValOperandId indexId, + uint32_t* int32Index, Int32OperandId* int32IndexId); + + ObjOperandId guardDOMProxyExpandoObjectAndShape(JSObject* obj, + ObjOperandId objId, + const Value& expandoVal, + JSObject* expandoObj); + + void emitIdGuard(ValOperandId valId, jsid id); + + OperandId emitNumericGuard(ValOperandId valId, Scalar::Type type); + + friend class CacheIRSpewer; + + public: + explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + CacheKind cacheKind, ICState::Mode mode); + + const CacheIRWriter& writerRef() const { return writer; } + CacheKind cacheKind() const { return cacheKind_; } + + static constexpr char* NotAttached = nullptr; +}; + +// GetPropIRGenerator generates CacheIR for a GetProp IC. +class MOZ_RAII GetPropIRGenerator : public IRGenerator { + HandleValue val_; + HandleValue idVal_; + + AttachDecision tryAttachNative(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId receiverId); + AttachDecision tryAttachObjectLength(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachTypedArrayLength(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachCrossCompartmentWrapper(HandleObject obj, + ObjOperandId objId, + HandleId id); + AttachDecision tryAttachXrayCrossCompartmentWrapper(HandleObject obj, + ObjOperandId objId, + HandleId id, + ValOperandId receiverId); + AttachDecision tryAttachFunction(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachArgumentsObjectIterator(HandleObject obj, + ObjOperandId objId, + HandleId id); + + AttachDecision tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, + HandleId id, bool handleDOMProxies); + AttachDecision tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId receiverId); + AttachDecision tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, + HandleId id); + AttachDecision tryAttachDOMProxyUnshadowed(HandleObject obj, + ObjOperandId objId, HandleId id, + ValOperandId receiverId); + AttachDecision tryAttachProxy(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId receiverId); + + AttachDecision tryAttachPrimitive(ValOperandId valId, HandleId id); + AttachDecision tryAttachStringChar(ValOperandId valId, ValOperandId indexId); + AttachDecision tryAttachStringLength(ValOperandId valId, HandleId id); + AttachDecision tryAttachMagicArgumentsName(ValOperandId valId, HandleId id); + + AttachDecision tryAttachMagicArgument(ValOperandId valId, + ValOperandId indexId); + AttachDecision tryAttachArgumentsObjectArg(HandleObject obj, + ObjOperandId objId, uint32_t index, + Int32OperandId indexId); + + AttachDecision tryAttachDenseElement(HandleObject obj, ObjOperandId objId, + uint32_t index, Int32OperandId indexId); + AttachDecision tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId, + uint32_t index, + Int32OperandId indexId); + AttachDecision tryAttachSparseElement(HandleObject obj, ObjOperandId objId, + uint32_t index, Int32OperandId indexId); + AttachDecision tryAttachTypedArrayElement(HandleObject obj, + ObjOperandId objId, uint32_t index, + Int32OperandId indexId); + AttachDecision tryAttachTypedArrayNonInt32Index(HandleObject obj, + ObjOperandId objId); + + AttachDecision tryAttachGenericElement(HandleObject obj, ObjOperandId objId, + uint32_t index, + Int32OperandId indexId); + + AttachDecision tryAttachProxyElement(HandleObject obj, ObjOperandId objId); + + void attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, + bool handleMissing); + + ValOperandId getElemKeyValueId() const { + MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || + cacheKind_ == CacheKind::GetElemSuper); + return ValOperandId(1); + } + + ValOperandId getSuperReceiverValueId() const { + if (cacheKind_ == CacheKind::GetPropSuper) { + return ValOperandId(1); + } + + MOZ_ASSERT(cacheKind_ == CacheKind::GetElemSuper); + return ValOperandId(2); + } + + bool isSuper() const { + return (cacheKind_ == CacheKind::GetPropSuper || + cacheKind_ == CacheKind::GetElemSuper); + } + + // If this is a GetElem cache, emit instructions to guard the incoming Value + // matches |id|. + void maybeEmitIdGuard(jsid id); + + void trackAttached(const char* name); + + public: + GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + ICState::Mode mode, CacheKind cacheKind, HandleValue val, + HandleValue idVal); + + AttachDecision tryAttachStub(); +}; + +// GetNameIRGenerator generates CacheIR for a GetName IC. +class MOZ_RAII GetNameIRGenerator : public IRGenerator { + HandleObject env_; + HandlePropertyName name_; + + AttachDecision tryAttachGlobalNameValue(ObjOperandId objId, HandleId id); + AttachDecision tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id); + AttachDecision tryAttachEnvironmentName(ObjOperandId objId, HandleId id); + + void trackAttached(const char* name); + + public: + GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + ICState::Mode mode, HandleObject env, + HandlePropertyName name); + + AttachDecision tryAttachStub(); +}; + +// BindNameIRGenerator generates CacheIR for a BindName IC. +class MOZ_RAII BindNameIRGenerator : public IRGenerator { + HandleObject env_; + HandlePropertyName name_; + + AttachDecision tryAttachGlobalName(ObjOperandId objId, HandleId id); + AttachDecision tryAttachEnvironmentName(ObjOperandId objId, HandleId id); + + void trackAttached(const char* name); + + public: + BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + ICState::Mode mode, HandleObject env, + HandlePropertyName name); + + AttachDecision tryAttachStub(); +}; + +// SetPropIRGenerator generates CacheIR for a SetProp IC. +class MOZ_RAII SetPropIRGenerator : public IRGenerator { + HandleValue lhsVal_; + HandleValue idVal_; + HandleValue rhsVal_; + + public: + enum class DeferType { None, AddSlot }; + + private: + DeferType deferType_ = DeferType::None; + + ValOperandId setElemKeyValueId() const { + MOZ_ASSERT(cacheKind_ == CacheKind::SetElem); + return ValOperandId(1); + } + + ValOperandId rhsValueId() const { + if (cacheKind_ == CacheKind::SetProp) { + return ValOperandId(1); + } + MOZ_ASSERT(cacheKind_ == CacheKind::SetElem); + return ValOperandId(2); + } + + // If this is a SetElem cache, emit instructions to guard the incoming Value + // matches |id|. + void maybeEmitIdGuard(jsid id); + + AttachDecision tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + AttachDecision tryAttachSetter(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + AttachDecision tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + AttachDecision tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + + AttachDecision tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, + uint32_t index, + Int32OperandId indexId, + ValOperandId rhsId); + AttachDecision tryAttachSetTypedArrayElement(HandleObject obj, + ObjOperandId objId, + uint32_t index, + Int32OperandId indexId, + ValOperandId rhsId); + AttachDecision tryAttachSetTypedArrayElementNonInt32Index(HandleObject obj, + ObjOperandId objId, + ValOperandId rhsId); + + AttachDecision tryAttachSetDenseElementHole(HandleObject obj, + ObjOperandId objId, + uint32_t index, + Int32OperandId indexId, + ValOperandId rhsId); + + AttachDecision tryAttachAddOrUpdateSparseElement(HandleObject obj, + ObjOperandId objId, + uint32_t index, + Int32OperandId indexId, + ValOperandId rhsId); + + AttachDecision tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId, + bool handleDOMProxies); + AttachDecision tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + AttachDecision tryAttachDOMProxyUnshadowed(HandleObject obj, + ObjOperandId objId, HandleId id, + ValOperandId rhsId); + AttachDecision tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + AttachDecision tryAttachProxy(HandleObject obj, ObjOperandId objId, + HandleId id, ValOperandId rhsId); + AttachDecision tryAttachProxyElement(HandleObject obj, ObjOperandId objId, + ValOperandId rhsId); + AttachDecision tryAttachMegamorphicSetElement(HandleObject obj, + ObjOperandId objId, + ValOperandId rhsId); + + bool canAttachAddSlotStub(HandleObject obj, HandleId id); + + public: + SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + CacheKind cacheKind, ICState::Mode mode, + HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal); + + AttachDecision tryAttachStub(); + AttachDecision tryAttachAddSlotStub(HandleShape oldShape); + void trackAttached(const char* name); + + DeferType deferType() const { return deferType_; } +}; + +// HasPropIRGenerator generates CacheIR for a HasProp IC. Used for +// CacheKind::In / CacheKind::HasOwn. +class MOZ_RAII HasPropIRGenerator : public IRGenerator { + HandleValue val_; + HandleValue idVal_; + + AttachDecision tryAttachDense(HandleObject obj, ObjOperandId objId, + uint32_t index, Int32OperandId indexId); + AttachDecision tryAttachDenseHole(HandleObject obj, ObjOperandId objId, + uint32_t index, Int32OperandId indexId); + AttachDecision tryAttachTypedArray(HandleObject obj, ObjOperandId objId, + Int32OperandId indexId); + AttachDecision tryAttachTypedArrayNonInt32Index(HandleObject obj, + ObjOperandId objId, + ValOperandId keyId); + AttachDecision tryAttachSparse(HandleObject obj, ObjOperandId objId, + Int32OperandId indexId); + AttachDecision tryAttachNamedProp(HandleObject obj, ObjOperandId objId, + HandleId key, ValOperandId keyId); + AttachDecision tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId); + AttachDecision tryAttachNative(JSObject* obj, ObjOperandId objId, jsid key, + ValOperandId keyId, PropertyResult prop, + JSObject* holder); + AttachDecision tryAttachSlotDoesNotExist(JSObject* obj, ObjOperandId objId, + jsid key, ValOperandId keyId); + AttachDecision tryAttachDoesNotExist(HandleObject obj, ObjOperandId objId, + HandleId key, ValOperandId keyId); + AttachDecision tryAttachProxyElement(HandleObject obj, ObjOperandId objId, + ValOperandId keyId); + + void trackAttached(const char* name); + + public: + // NOTE: Argument order is PROPERTY, OBJECT + HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + ICState::Mode mode, CacheKind cacheKind, HandleValue idVal, + HandleValue val); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII CheckPrivateFieldIRGenerator : public IRGenerator { + HandleValue val_; + HandleValue idVal_; + + AttachDecision tryAttachNative(JSObject* obj, ObjOperandId objId, jsid key, + ValOperandId keyId, bool hasOwn); + + void trackAttached(const char* name); + + public: + CheckPrivateFieldIRGenerator(JSContext* cx, HandleScript script, + jsbytecode* pc, ICState::Mode mode, + CacheKind cacheKind, HandleValue idVal, + HandleValue val); + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII InstanceOfIRGenerator : public IRGenerator { + HandleValue lhsVal_; + HandleObject rhsObj_; + + void trackAttached(const char* name); + + public: + InstanceOfIRGenerator(JSContext*, HandleScript, jsbytecode*, ICState::Mode, + HandleValue, HandleObject); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII TypeOfIRGenerator : public IRGenerator { + HandleValue val_; + + AttachDecision tryAttachPrimitive(ValOperandId valId); + AttachDecision tryAttachObject(ValOperandId valId); + void trackAttached(const char* name); + + public: + TypeOfIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode mode, HandleValue value); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII GetIteratorIRGenerator : public IRGenerator { + HandleValue val_; + + AttachDecision tryAttachNativeIterator(ObjOperandId objId, HandleObject obj); + + public: + GetIteratorIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode mode, HandleValue value); + + AttachDecision tryAttachStub(); + + void trackAttached(const char* name); +}; + +class MOZ_RAII OptimizeSpreadCallIRGenerator : public IRGenerator { + HandleValue val_; + + AttachDecision tryAttachArray(); + AttachDecision tryAttachNotOptimizable(); + + public: + OptimizeSpreadCallIRGenerator(JSContext* cx, HandleScript script, + jsbytecode* pc, ICState::Mode mode, + HandleValue value); + + AttachDecision tryAttachStub(); + + void trackAttached(const char* name); +}; + +enum class StringChar { CodeAt, At }; +enum class ScriptedThisResult { NoAction, UninitializedThis, TemplateObject }; + +class MOZ_RAII CallIRGenerator : public IRGenerator { + private: + JSOp op_; + bool isFirstStub_; + uint32_t argc_; + HandleValue callee_; + HandleValue thisval_; + HandleValue newTarget_; + HandleValueArray args_; + + ScriptedThisResult getThisForScripted(HandleFunction calleeFunc, + MutableHandleObject result); + + void emitNativeCalleeGuard(HandleFunction callee); + void emitCalleeGuard(ObjOperandId calleeId, HandleFunction callee); + + bool canAttachAtomicsReadWriteModify(); + + struct AtomicsReadWriteModifyOperands { + ObjOperandId objId; + Int32OperandId int32IndexId; + Int32OperandId int32ValueId; + }; + + AtomicsReadWriteModifyOperands emitAtomicsReadWriteModifyOperands( + HandleFunction callee); + + AttachDecision tryAttachArrayPush(HandleFunction callee); + AttachDecision tryAttachArrayPopShift(HandleFunction callee, + InlinableNative native); + AttachDecision tryAttachArrayJoin(HandleFunction callee); + AttachDecision tryAttachArraySlice(HandleFunction callee); + AttachDecision tryAttachArrayIsArray(HandleFunction callee); + AttachDecision tryAttachDataViewGet(HandleFunction callee, Scalar::Type type); + AttachDecision tryAttachDataViewSet(HandleFunction callee, Scalar::Type type); + AttachDecision tryAttachUnsafeGetReservedSlot(HandleFunction callee, + InlinableNative native); + AttachDecision tryAttachUnsafeSetReservedSlot(HandleFunction callee); + AttachDecision tryAttachIsSuspendedGenerator(HandleFunction callee); + AttachDecision tryAttachToObject(HandleFunction callee, + InlinableNative native); + AttachDecision tryAttachToInteger(HandleFunction callee); + AttachDecision tryAttachToLength(HandleFunction callee); + AttachDecision tryAttachIsObject(HandleFunction callee); + AttachDecision tryAttachIsPackedArray(HandleFunction callee); + AttachDecision tryAttachIsCallable(HandleFunction callee); + AttachDecision tryAttachIsConstructor(HandleFunction callee); + AttachDecision tryAttachIsCrossRealmArrayConstructor(HandleFunction callee); + AttachDecision tryAttachGuardToClass(HandleFunction callee, + InlinableNative native); + AttachDecision tryAttachHasClass(HandleFunction callee, const JSClass* clasp, + bool isPossiblyWrapped); + AttachDecision tryAttachRegExpMatcherSearcherTester(HandleFunction callee, + InlinableNative native); + AttachDecision tryAttachRegExpPrototypeOptimizable(HandleFunction callee); + AttachDecision tryAttachRegExpInstanceOptimizable(HandleFunction callee); + AttachDecision tryAttachGetFirstDollarIndex(HandleFunction callee); + AttachDecision tryAttachSubstringKernel(HandleFunction callee); + AttachDecision tryAttachObjectHasPrototype(HandleFunction callee); + AttachDecision tryAttachString(HandleFunction callee); + AttachDecision tryAttachStringConstructor(HandleFunction callee); + AttachDecision tryAttachStringToStringValueOf(HandleFunction callee); + AttachDecision tryAttachStringChar(HandleFunction callee, StringChar kind); + AttachDecision tryAttachStringCharCodeAt(HandleFunction callee); + AttachDecision tryAttachStringCharAt(HandleFunction callee); + AttachDecision tryAttachStringFromCharCode(HandleFunction callee); + AttachDecision tryAttachStringFromCodePoint(HandleFunction callee); + AttachDecision tryAttachStringToLowerCase(HandleFunction callee); + AttachDecision tryAttachStringToUpperCase(HandleFunction callee); + AttachDecision tryAttachStringReplaceString(HandleFunction callee); + AttachDecision tryAttachStringSplitString(HandleFunction callee); + AttachDecision tryAttachMathRandom(HandleFunction callee); + AttachDecision tryAttachMathAbs(HandleFunction callee); + AttachDecision tryAttachMathClz32(HandleFunction callee); + AttachDecision tryAttachMathSign(HandleFunction callee); + AttachDecision tryAttachMathImul(HandleFunction callee); + AttachDecision tryAttachMathFloor(HandleFunction callee); + AttachDecision tryAttachMathCeil(HandleFunction callee); + AttachDecision tryAttachMathTrunc(HandleFunction callee); + AttachDecision tryAttachMathRound(HandleFunction callee); + AttachDecision tryAttachMathSqrt(HandleFunction callee); + AttachDecision tryAttachMathFRound(HandleFunction callee); + AttachDecision tryAttachMathHypot(HandleFunction callee); + AttachDecision tryAttachMathATan2(HandleFunction callee); + AttachDecision tryAttachMathFunction(HandleFunction callee, + UnaryMathFunction fun); + AttachDecision tryAttachMathPow(HandleFunction callee); + AttachDecision tryAttachMathMinMax(HandleFunction callee, bool isMax); + AttachDecision tryAttachIsTypedArray(HandleFunction callee, + bool isPossiblyWrapped); + AttachDecision tryAttachIsTypedArrayConstructor(HandleFunction callee); + AttachDecision tryAttachTypedArrayByteOffset(HandleFunction callee); + AttachDecision tryAttachTypedArrayElementShift(HandleFunction callee); + AttachDecision tryAttachTypedArrayLength(HandleFunction callee, + bool isPossiblyWrapped); + AttachDecision tryAttachArrayBufferByteLength(HandleFunction callee, + bool isPossiblyWrapped); + AttachDecision tryAttachIsConstructing(HandleFunction callee); + AttachDecision tryAttachGetNextMapSetEntryForIterator(HandleFunction callee, + bool isMap); + AttachDecision tryAttachFinishBoundFunctionInit(HandleFunction callee); + AttachDecision tryAttachNewArrayIterator(HandleFunction callee); + AttachDecision tryAttachNewStringIterator(HandleFunction callee); + AttachDecision tryAttachNewRegExpStringIterator(HandleFunction callee); + AttachDecision tryAttachArrayIteratorPrototypeOptimizable( + HandleFunction callee); + AttachDecision tryAttachObjectCreate(HandleFunction callee); + AttachDecision tryAttachArrayConstructor(HandleFunction callee); + AttachDecision tryAttachTypedArrayConstructor(HandleFunction callee); + AttachDecision tryAttachNumberToString(HandleFunction callee); + AttachDecision tryAttachReflectGetPrototypeOf(HandleFunction callee); + AttachDecision tryAttachAtomicsCompareExchange(HandleFunction callee); + AttachDecision tryAttachAtomicsExchange(HandleFunction callee); + AttachDecision tryAttachAtomicsAdd(HandleFunction callee); + AttachDecision tryAttachAtomicsSub(HandleFunction callee); + AttachDecision tryAttachAtomicsAnd(HandleFunction callee); + AttachDecision tryAttachAtomicsOr(HandleFunction callee); + AttachDecision tryAttachAtomicsXor(HandleFunction callee); + AttachDecision tryAttachAtomicsLoad(HandleFunction callee); + AttachDecision tryAttachAtomicsStore(HandleFunction callee); + AttachDecision tryAttachAtomicsIsLockFree(HandleFunction callee); + AttachDecision tryAttachBoolean(HandleFunction callee); + AttachDecision tryAttachBailout(HandleFunction callee); + AttachDecision tryAttachAssertFloat32(HandleFunction callee); + AttachDecision tryAttachAssertRecoveredOnBailout(HandleFunction callee); + AttachDecision tryAttachObjectIs(HandleFunction callee); + AttachDecision tryAttachObjectIsPrototypeOf(HandleFunction callee); + AttachDecision tryAttachObjectToString(HandleFunction callee); + AttachDecision tryAttachBigIntAsIntN(HandleFunction callee); + AttachDecision tryAttachBigIntAsUintN(HandleFunction callee); + + AttachDecision tryAttachFunCall(HandleFunction calleeFunc); + AttachDecision tryAttachFunApply(HandleFunction calleeFunc); + AttachDecision tryAttachCallScripted(HandleFunction calleeFunc); + AttachDecision tryAttachInlinableNative(HandleFunction calleeFunc); + AttachDecision tryAttachWasmCall(HandleFunction calleeFunc); + AttachDecision tryAttachCallNative(HandleFunction calleeFunc); + AttachDecision tryAttachCallHook(HandleObject calleeObj); + + void trackAttached(const char* name); + + public: + CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op, + ICState::Mode mode, bool isFirstStub, uint32_t argc, + HandleValue callee, HandleValue thisval, + HandleValue newTarget, HandleValueArray args); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII CompareIRGenerator : public IRGenerator { + JSOp op_; + HandleValue lhsVal_; + HandleValue rhsVal_; + + AttachDecision tryAttachString(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachObject(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachStrictDifferentTypes(ValOperandId lhsId, + ValOperandId rhsId); + AttachDecision tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachBigInt(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachNumberUndefined(ValOperandId lhsId, + ValOperandId rhsId); + AttachDecision tryAttachAnyNullUndefined(ValOperandId lhsId, + ValOperandId rhsId); + AttachDecision tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachStringNumber(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachPrimitiveSymbol(ValOperandId lhsId, + ValOperandId rhsId); + AttachDecision tryAttachBoolStringOrNumber(ValOperandId lhsId, + ValOperandId rhsId); + AttachDecision tryAttachBigIntInt32(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachBigIntNumber(ValOperandId lhsId, ValOperandId rhsId); + AttachDecision tryAttachBigIntString(ValOperandId lhsId, ValOperandId rhsId); + + void trackAttached(const char* name); + + public: + CompareIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode mode, JSOp op, HandleValue lhsVal, + HandleValue rhsVal); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII ToBoolIRGenerator : public IRGenerator { + HandleValue val_; + + AttachDecision tryAttachInt32(); + AttachDecision tryAttachNumber(); + AttachDecision tryAttachString(); + AttachDecision tryAttachSymbol(); + AttachDecision tryAttachNullOrUndefined(); + AttachDecision tryAttachObject(); + AttachDecision tryAttachBigInt(); + + void trackAttached(const char* name); + + public: + ToBoolIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode mode, HandleValue val); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII GetIntrinsicIRGenerator : public IRGenerator { + HandleValue val_; + + void trackAttached(const char* name); + + public: + GetIntrinsicIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode, HandleValue val); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII UnaryArithIRGenerator : public IRGenerator { + JSOp op_; + HandleValue val_; + HandleValue res_; + + AttachDecision tryAttachInt32(); + AttachDecision tryAttachNumber(); + AttachDecision tryAttachBitwise(); + AttachDecision tryAttachBigInt(); + AttachDecision tryAttachStringInt32(); + AttachDecision tryAttachStringNumber(); + + void trackAttached(const char* name); + + public: + UnaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode mode, JSOp op, HandleValue val, + HandleValue res); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII ToPropertyKeyIRGenerator : public IRGenerator { + HandleValue val_; + + AttachDecision tryAttachInt32(); + AttachDecision tryAttachNumber(); + AttachDecision tryAttachString(); + AttachDecision tryAttachSymbol(); + + void trackAttached(const char* name); + + public: + ToPropertyKeyIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode mode, HandleValue val); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII BinaryArithIRGenerator : public IRGenerator { + JSOp op_; + HandleValue lhs_; + HandleValue rhs_; + HandleValue res_; + + void trackAttached(const char* name); + + AttachDecision tryAttachInt32(); + AttachDecision tryAttachDouble(); + AttachDecision tryAttachBitwise(); + AttachDecision tryAttachStringConcat(); + AttachDecision tryAttachStringObjectConcat(); + AttachDecision tryAttachStringNumberConcat(); + AttachDecision tryAttachStringBooleanConcat(); + AttachDecision tryAttachBigInt(); + AttachDecision tryAttachStringInt32Arith(); + + public: + BinaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode, JSOp op, HandleValue lhs, + HandleValue rhs, HandleValue res); + + AttachDecision tryAttachStub(); +}; + +class MOZ_RAII NewObjectIRGenerator : public IRGenerator { +#ifdef JS_CACHEIR_SPEW + JSOp op_; +#endif + HandleObject templateObject_; + + void trackAttached(const char* name); + + public: + NewObjectIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, + ICState::Mode, JSOp op, HandleObject templateObj); + + AttachDecision tryAttachStub(); +}; + +// Returns whether obj is a WindowProxy wrapping the script's global. +extern bool IsWindowProxyForScriptGlobal(JSScript* script, JSObject* obj); + +// Retrieve Xray JIT info set by the embedder. +extern JS::XrayJitInfo* GetXrayJitInfo(); + +} // namespace jit +} // namespace js + +#endif /* jit_CacheIR_h */ |