Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/. */
#include "jit/WarpCacheIRTranspiler.h"
#include "mozilla/Casting.h"
#include "mozilla/Maybe.h"
#include "jsmath.h"
#include "jit/AtomicOp.h"
#include "jit/CacheIR.h"
#include "jit/CacheIRCompiler.h"
#include "jit/CacheIROpsGenerated.h"
#include "jit/CacheIRReader.h"
#include "jit/LIR.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "jit/WarpBuilder.h"
#include "jit/WarpBuilderShared.h"
#include "jit/WarpSnapshot.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "vm/BytecodeLocation.h"
#include "vm/TypeofEqOperand.h" // TypeofEqOperand
#include "wasm/WasmCode.h"
#include "gc/ObjectKind-inl.h"
#include "vm/NativeObject-inl.h"
#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
// The CacheIR transpiler generates MIR from Baseline CacheIR.
class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
WarpBuilder* builder_;
BytecodeLocation loc_;
const CacheIRStubInfo* stubInfo_;
const uint8_t* stubData_;
// Vector mapping OperandId to corresponding MDefinition.
using MDefinitionStackVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
MDefinitionStackVector operands_;
CallInfo* callInfo_;
// Array mapping call arguments to OperandId.
using ArgumentKindArray =
mozilla::EnumeratedArray<ArgumentKind, OperandId,
size_t(ArgumentKind::NumKinds)>;
ArgumentKindArray argumentOperandIds_;
void setArgumentId(ArgumentKind kind, OperandId id) {
MOZ_ASSERT(kind != ArgumentKind::Callee);
MOZ_ASSERT(!argumentOperandIds_[kind].valid());
argumentOperandIds_[kind] = id;
}
void updateArgumentsFromOperands();
#ifdef DEBUG
// Used to assert that there is only one effectful instruction
// per stub. And that this instruction has a resume point.
MInstruction* effectful_ = nullptr;
bool pushedResult_ = false;
#endif
inline void addUnchecked(MInstruction* ins) {
current->add(ins);
// If we have not set a more specific bailout kind, mark this instruction
// as transpiled CacheIR. If one of these instructions bails out, we
// expect to hit the baseline fallback stub and invalidate the Warp script
// in tryAttach.
if (ins->bailoutKind() == BailoutKind::Unknown) {
ins->setBailoutKind(BailoutKind::TranspiledCacheIR);
}
}
inline void add(MInstruction* ins) {
MOZ_ASSERT(!ins->isEffectful());
addUnchecked(ins);
}
inline void addEffectful(MInstruction* ins) {
MOZ_ASSERT(ins->isEffectful());
MOZ_ASSERT(!effectful_, "Can only have one effectful instruction");
addUnchecked(ins);
#ifdef DEBUG
effectful_ = ins;
#endif
}
// Bypasses all checks in addEffectful. Use with caution!
inline void addEffectfulUnsafe(MInstruction* ins) {
MOZ_ASSERT(ins->isEffectful());
addUnchecked(ins);
}
[[nodiscard]] bool resumeAfterUnchecked(MInstruction* ins) {
return WarpBuilderShared::resumeAfter(ins, loc_);
}
[[nodiscard]] bool resumeAfter(MInstruction* ins) {
MOZ_ASSERT(effectful_ == ins);
return resumeAfterUnchecked(ins);
}
// CacheIR instructions writing to the IC's result register (the *Result
// instructions) must call this to push the result onto the virtual stack.
void pushResult(MDefinition* result) {
MOZ_ASSERT(!pushedResult_, "Can't have more than one result");
current->push(result);
#ifdef DEBUG
pushedResult_ = true;
#endif
}
MDefinition* getOperand(OperandId id) const { return operands_[id.id()]; }
void setOperand(OperandId id, MDefinition* def) { operands_[id.id()] = def; }
[[nodiscard]] bool defineOperand(OperandId id, MDefinition* def) {
MOZ_ASSERT(id.id() == operands_.length());
return operands_.append(def);
}
uintptr_t readStubWord(uint32_t offset) {
return stubInfo_->getStubRawWord(stubData_, offset);
}
Shape* shapeStubField(uint32_t offset) {
return reinterpret_cast<Shape*>(readStubWord(offset));
}
GetterSetter* getterSetterStubField(uint32_t offset) {
return reinterpret_cast<GetterSetter*>(readStubWord(offset));
}
const JSClass* classStubField(uint32_t offset) {
return reinterpret_cast<const JSClass*>(readStubWord(offset));
}
JSString* stringStubField(uint32_t offset) {
return reinterpret_cast<JSString*>(readStubWord(offset));
}
JS::Symbol* symbolStubField(uint32_t offset) {
return reinterpret_cast<JS::Symbol*>(readStubWord(offset));
}
BaseScript* baseScriptStubField(uint32_t offset) {
return reinterpret_cast<BaseScript*>(readStubWord(offset));
}
const JSJitInfo* jitInfoStubField(uint32_t offset) {
return reinterpret_cast<const JSJitInfo*>(readStubWord(offset));
}
JSNative jsnativeStubField(uint32_t offset) {
return reinterpret_cast<JSNative>(readStubWord(offset));
}
JS::ExpandoAndGeneration* expandoAndGenerationField(uint32_t offset) {
return reinterpret_cast<JS::ExpandoAndGeneration*>(readStubWord(offset));
}
const wasm::FuncExport* wasmFuncExportField(uint32_t offset) {
return reinterpret_cast<const wasm::FuncExport*>(readStubWord(offset));
}
NativeIteratorListHead* nativeIteratorListHeadStubField(uint32_t offset) {
return reinterpret_cast<NativeIteratorListHead*>(readStubWord(offset));
}
size_t* fuseStubField(uint32_t offset) {
return reinterpret_cast<size_t*>(readStubWord(offset));
}
gc::Heap allocSiteInitialHeapField(uint32_t offset) {
uintptr_t word = readStubWord(offset);
MOZ_ASSERT(word == uintptr_t(gc::Heap::Default) ||
word == uintptr_t(gc::Heap::Tenured));
return gc::Heap(word);
}
const void* rawPointerField(uint32_t offset) {
return reinterpret_cast<const void*>(readStubWord(offset));
}
jsid idStubField(uint32_t offset) {
return jsid::fromRawBits(readStubWord(offset));
}
int32_t int32StubField(uint32_t offset) {
return static_cast<int32_t>(readStubWord(offset));
}
uint32_t uint32StubField(uint32_t offset) {
return static_cast<uint32_t>(readStubWord(offset));
}
uint64_t uint64StubField(uint32_t offset) {
return static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
}
Value valueStubField(uint32_t offset) {
uint64_t raw =
static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
Value val = Value::fromRawBits(raw);
MOZ_ASSERT_IF(val.isGCThing(), val.toGCThing()->isTenured());
return val;
}
double doubleStubField(uint32_t offset) {
uint64_t raw =
static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
return mozilla::BitwiseCast<double>(raw);
}
// This must only be called when the caller knows the object is tenured and
// not a nursery index.
JSObject* tenuredObjectStubField(uint32_t offset) {
WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
return field.toObject();
}
// Returns either MConstant or MNurseryIndex. See WarpObjectField.
MInstruction* objectStubField(uint32_t offset);
const JSClass* classForGuardClassKind(GuardClassKind kind);
[[nodiscard]] bool emitGuardTo(ValOperandId inputId, MIRType type);
[[nodiscard]] bool emitToString(OperandId inputId, StringOperandId resultId);
template <typename T>
[[nodiscard]] bool emitDoubleBinaryArithResult(NumberOperandId lhsId,
NumberOperandId rhsId);
template <typename T>
[[nodiscard]] bool emitInt32BinaryArithResult(Int32OperandId lhsId,
Int32OperandId rhsId);
template <typename T>
[[nodiscard]] bool emitBigIntBinaryArithResult(BigIntOperandId lhsId,
BigIntOperandId rhsId);
template <typename T>
[[nodiscard]] bool emitBigIntBinaryArithEffectfulResult(
BigIntOperandId lhsId, BigIntOperandId rhsId);
template <typename T>
[[nodiscard]] bool emitBigIntUnaryArithResult(BigIntOperandId inputId);
template <typename T>
[[nodiscard]] bool emitBigIntPtrBinaryArith(IntPtrOperandId lhsId,
IntPtrOperandId rhsId,
IntPtrOperandId resultId);
[[nodiscard]] bool emitCompareResult(JSOp op, OperandId lhsId,
OperandId rhsId,
MCompare::CompareType compareType);
[[nodiscard]] bool emitTruthyResult(OperandId inputId);
[[nodiscard]] bool emitNewIteratorResult(MNewIterator::Type type,
uint32_t templateObjectOffset);
MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
[[nodiscard]] MInstruction* convertToBoolean(MDefinition* input);
bool emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind kind,
ObjOperandId objId, uint32_t offsetOffset,
ValOperandId rhsId, uint32_t newShapeOffset);
MInstruction* emitTypedArrayLength(ArrayBufferViewKind viewKind,
MDefinition* obj);
MInstruction* emitDataViewLength(ArrayBufferViewKind viewKind,
MDefinition* obj);
void addDataViewData(ArrayBufferViewKind viewKind, MDefinition* obj,
Scalar::Type type, MDefinition** offset,
MInstruction** elements);
[[nodiscard]] bool emitAtomicsBinaryOp(
ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind,
AtomicOp op);
[[nodiscard]] bool emitLoadArgumentSlot(ValOperandId resultId,
uint32_t slotIndex);
// Calls are either Native (native function without a JitEntry),
// a DOM Native (native function with a JitInfo OpType::Method),
// or Scripted (scripted function or native function with a JitEntry).
enum class CallKind { Native, DOM, Scripted };
[[nodiscard]] bool updateCallInfo(MDefinition* callee, CallFlags flags);
[[nodiscard]] bool emitCallFunction(
ObjOperandId calleeId, Int32OperandId argcId,
mozilla::Maybe<ObjOperandId> thisObjId, CallFlags flags, CallKind kind,
mozilla::Maybe<uint32_t> siteOffset = mozilla::Nothing());
[[nodiscard]] bool emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
CallFlags flags);
MDefinition* convertWasmArg(MDefinition* arg, wasm::ValType::Kind kind);
WrappedFunction* maybeWrappedFunction(MDefinition* callee, CallKind kind,
uint16_t nargs, FunctionFlags flags);
WrappedFunction* maybeCallTarget(MDefinition* callee, CallKind kind);
bool maybeCreateThis(MDefinition* callee, CallFlags flags, CallKind kind);
[[nodiscard]] bool emitCallGetterResult(CallKind kind,
ValOperandId receiverId,
uint32_t getterOffset, bool sameRealm,
uint32_t nargsAndFlagsOffset);
[[nodiscard]] bool emitCallSetter(CallKind kind, ObjOperandId receiverId,
uint32_t setterOffset, ValOperandId rhsId,
bool sameRealm,
uint32_t nargsAndFlagsOffset);
#ifndef JS_CODEGEN_X86
[[nodiscard]] bool emitCallScriptedProxyGetShared(
MDefinition* target, MDefinition* receiver, MDefinition* handler,
MDefinition* id, MDefinition* trapDef, WrappedFunction* trap);
#endif