summaryrefslogtreecommitdiffstats
path: root/js/src/jit/CodeGenerator.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/CodeGenerator.h')
-rw-r--r--js/src/jit/CodeGenerator.h427
1 files changed, 427 insertions, 0 deletions
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
new file mode 100644
index 0000000000..a56dbd8b22
--- /dev/null
+++ b/js/src/jit/CodeGenerator.h
@@ -0,0 +1,427 @@
+/* -*- 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_CodeGenerator_h
+#define jit_CodeGenerator_h
+
+#include "jit/PerfSpewer.h"
+#include "js/ScalarType.h" // js::Scalar::Type
+
+#if defined(JS_CODEGEN_X86)
+# include "jit/x86/CodeGenerator-x86.h"
+#elif defined(JS_CODEGEN_X64)
+# include "jit/x64/CodeGenerator-x64.h"
+#elif defined(JS_CODEGEN_ARM)
+# include "jit/arm/CodeGenerator-arm.h"
+#elif defined(JS_CODEGEN_ARM64)
+# include "jit/arm64/CodeGenerator-arm64.h"
+#elif defined(JS_CODEGEN_MIPS32)
+# include "jit/mips32/CodeGenerator-mips32.h"
+#elif defined(JS_CODEGEN_MIPS64)
+# include "jit/mips64/CodeGenerator-mips64.h"
+#elif defined(JS_CODEGEN_LOONG64)
+# include "jit/loong64/CodeGenerator-loong64.h"
+#elif defined(JS_CODEGEN_WASM32)
+# include "jit/wasm32/CodeGenerator-wasm32.h"
+#elif defined(JS_CODEGEN_NONE)
+# include "jit/none/CodeGenerator-none.h"
+#else
+# error "Unknown architecture!"
+#endif
+
+namespace js {
+
+namespace wasm {
+class Decoder;
+class StackMaps;
+} // namespace wasm
+
+namespace jit {
+
+class WarpSnapshot;
+
+template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
+class OutOfLineCallVM;
+
+enum class SwitchTableType { Inline, OutOfLine };
+
+template <SwitchTableType tableType>
+class OutOfLineSwitch;
+class OutOfLineTestObject;
+class OutOfLineNewArray;
+class OutOfLineNewObject;
+class CheckOverRecursedFailure;
+class OutOfLineUnboxFloatingPoint;
+class OutOfLineStoreElementHole;
+class OutOfLineTypeOfV;
+class OutOfLineTypeOfIsNonPrimitiveV;
+class OutOfLineTypeOfIsNonPrimitiveO;
+class OutOfLineUpdateCache;
+class OutOfLineICFallback;
+class OutOfLineCallPostWriteBarrier;
+class OutOfLineCallPostWriteElementBarrier;
+class OutOfLineIsCallable;
+class OutOfLineIsConstructor;
+class OutOfLineRegExpMatcher;
+class OutOfLineRegExpSearcher;
+class OutOfLineRegExpTester;
+class OutOfLineRegExpPrototypeOptimizable;
+class OutOfLineRegExpInstanceOptimizable;
+class OutOfLineNaNToZero;
+class OutOfLineResumableWasmTrap;
+class OutOfLineAbortingWasmTrap;
+class OutOfLineZeroIfNaN;
+class OutOfLineGuardNumberToIntPtrIndex;
+class OutOfLineBoxNonStrictThis;
+class OutOfLineArrayPush;
+
+class CodeGenerator final : public CodeGeneratorSpecific {
+ [[nodiscard]] bool generateBody();
+
+ ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n,
+ MIRType type);
+
+#ifdef CHECK_OSIPOINT_REGISTERS
+ void resetOsiPointRegs(LSafepoint* safepoint);
+ bool shouldVerifyOsiPointRegs(LSafepoint* safepoint);
+ void verifyOsiPointRegs(LSafepoint* safepoint);
+#endif
+
+ void callVMInternal(VMFunctionId id, LInstruction* ins);
+
+ template <typename Fn, Fn fn>
+ void callVM(LInstruction* ins);
+
+ template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
+ inline OutOfLineCode* oolCallVM(LInstruction* ins, const ArgSeq& args,
+ const StoreOutputTo& out);
+
+ public:
+ CodeGenerator(MIRGenerator* gen, LIRGraph* graph,
+ MacroAssembler* masm = nullptr);
+ ~CodeGenerator();
+
+ [[nodiscard]] bool generate();
+ [[nodiscard]] bool generateWasm(
+ wasm::CallIndirectId callIndirectId, wasm::BytecodeOffset trapOffset,
+ const wasm::ArgTypeVector& argTys, const RegisterOffsets& trapExitLayout,
+ size_t trapExitLayoutNumWords, wasm::FuncOffsets* offsets,
+ wasm::StackMaps* stackMaps, wasm::Decoder* decoder);
+
+ [[nodiscard]] bool link(JSContext* cx, const WarpSnapshot* snapshot);
+
+ void emitOOLTestObject(Register objreg, Label* ifTruthy, Label* ifFalsy,
+ Register scratch);
+ void emitIntToString(Register input, Register output, Label* ool);
+
+ void emitTypeOfCheck(JSValueType type, Register tag, Register output,
+ Label* done, Label* oolObject);
+ void emitTypeOfJSType(JSValueType type, Register output);
+ void emitTypeOfObject(Register obj, Register output, Label* done);
+ void emitTypeOfIsObject(MTypeOfIs* mir, Register obj, Register output,
+ Label* success, Label* fail, Label* slowCheck);
+ void emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj, Register output);
+
+ template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
+ void visitOutOfLineCallVM(
+ OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool);
+
+ void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
+ void visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool);
+ void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
+ void visitOutOfLineRegExpPrototypeOptimizable(
+ OutOfLineRegExpPrototypeOptimizable* ool);
+ void visitOutOfLineRegExpInstanceOptimizable(
+ OutOfLineRegExpInstanceOptimizable* ool);
+
+ void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool);
+ void visitOutOfLineTypeOfIsNonPrimitiveV(OutOfLineTypeOfIsNonPrimitiveV* ool);
+ void visitOutOfLineTypeOfIsNonPrimitiveO(OutOfLineTypeOfIsNonPrimitiveO* ool);
+
+ template <SwitchTableType tableType>
+ void visitOutOfLineSwitch(OutOfLineSwitch<tableType>* ool);
+
+ void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
+ void visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool);
+
+ void visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool);
+ void visitOutOfLineZeroIfNaN(OutOfLineZeroIfNaN* ool);
+
+ void visitOutOfLineResumableWasmTrap(OutOfLineResumableWasmTrap* ool);
+ void visitOutOfLineAbortingWasmTrap(OutOfLineAbortingWasmTrap* ool);
+ void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
+
+ void visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint* ool);
+ void visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool);
+
+ void visitOutOfLineBoxNonStrictThis(OutOfLineBoxNonStrictThis* ool);
+
+ void visitOutOfLineICFallback(OutOfLineICFallback* ool);
+
+ void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool);
+ void visitOutOfLineCallPostWriteElementBarrier(
+ OutOfLineCallPostWriteElementBarrier* ool);
+
+ void visitOutOfLineNewArray(OutOfLineNewArray* ool);
+ void visitOutOfLineNewObject(OutOfLineNewObject* ool);
+
+ void visitOutOfLineGuardNumberToIntPtrIndex(
+ OutOfLineGuardNumberToIntPtrIndex* ool);
+
+ void visitOutOfLineArrayPush(OutOfLineArrayPush* ool);
+
+ private:
+ void emitPostWriteBarrier(const LAllocation* obj);
+ void emitPostWriteBarrier(Register objreg);
+ void emitPostWriteBarrierS(Address address, Register prev, Register next);
+
+ template <class LPostBarrierType, MIRType nurseryType>
+ void visitPostWriteBarrierCommon(LPostBarrierType* lir, OutOfLineCode* ool);
+ template <class LPostBarrierType>
+ void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool);
+
+ void emitCallInvokeFunction(LInstruction* call, Register callereg,
+ bool isConstructing, bool ignoresReturnValue,
+ uint32_t argc, uint32_t unusedStack);
+ template <typename T>
+ void emitApplyGeneric(T* apply);
+ template <typename T>
+ void emitCallInvokeFunction(T* apply);
+ void emitAllocateSpaceForApply(Register argcreg, Register scratch);
+ void emitAllocateSpaceForConstructAndPushNewTarget(
+ Register argcreg, Register newTargetAndScratch);
+ void emitCopyValuesForApply(Register argvSrcBase, Register argvIndex,
+ Register copyreg, size_t argvSrcOffset,
+ size_t argvDstOffset);
+ void emitRestoreStackPointerFromFP();
+ void emitPushArguments(Register argcreg, Register scratch, Register copyreg,
+ uint32_t extraFormals);
+ void emitPushArrayAsArguments(Register tmpArgc, Register srcBaseAndArgc,
+ Register scratch, size_t argvSrcOffset);
+ void emitPushArguments(LApplyArgsGeneric* apply, Register scratch);
+ void emitPushArguments(LApplyArgsObj* apply, Register scratch);
+ void emitPushArguments(LApplyArrayGeneric* apply, Register scratch);
+ void emitPushArguments(LConstructArgsGeneric* construct, Register scratch);
+ void emitPushArguments(LConstructArrayGeneric* construct, Register scratch);
+
+ template <class GetInlinedArgument>
+ void emitGetInlinedArgument(GetInlinedArgument* lir, Register index,
+ ValueOperand output);
+
+ using RegisterOrInt32 = mozilla::Variant<Register, int32_t>;
+
+ static RegisterOrInt32 ToRegisterOrInt32(const LAllocation* allocation);
+
+#ifdef DEBUG
+ void emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin,
+ const RegisterOrInt32& count,
+ Register numActualArgs);
+#endif
+
+ template <class ArgumentsSlice>
+ void emitNewArray(ArgumentsSlice* lir, const RegisterOrInt32& count,
+ Register output, Register temp);
+
+ void visitNewArrayCallVM(LNewArray* lir);
+ void visitNewObjectVMCall(LNewObject* lir);
+
+ void emitConcat(LInstruction* lir, Register lhs, Register rhs,
+ Register output);
+
+ void emitInstanceOf(LInstruction* ins, Register protoReg);
+
+ void loadJSScriptForBlock(MBasicBlock* block, Register reg);
+ void loadOutermostJSScript(Register reg);
+
+#ifdef DEBUG
+ void emitAssertResultV(const ValueOperand output, const MDefinition* mir);
+ void emitAssertGCThingResult(Register input, const MDefinition* mir);
+#endif
+
+#ifdef DEBUG
+ void emitDebugForceBailing(LInstruction* lir);
+#endif
+
+ IonScriptCounts* extractScriptCounts() {
+ IonScriptCounts* counts = scriptCounts_;
+ scriptCounts_ = nullptr; // prevent delete in dtor
+ return counts;
+ }
+
+ void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
+ TypedOrValueRegister value,
+ const ConstantOrRegister& id, ValueOperand output);
+ void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
+ Register objReg, Register temp,
+ const ConstantOrRegister& id,
+ const ConstantOrRegister& value, bool strict);
+
+ template <class IteratorObject, class OrderedHashTable>
+ void emitGetNextEntryForIterator(LGetNextEntryForIterator* lir);
+
+ template <class OrderedHashTable>
+ void emitLoadIteratorValues(Register result, Register temp, Register front);
+
+ void emitStringToInt64(LInstruction* lir, Register input, Register64 output);
+
+ OutOfLineCode* createBigIntOutOfLine(LInstruction* lir, Scalar::Type type,
+ Register64 input, Register output);
+
+ void emitCreateBigInt(LInstruction* lir, Scalar::Type type, Register64 input,
+ Register output, Register maybeTemp);
+
+ template <size_t NumDefs>
+ void emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir);
+
+ IonScriptCounts* maybeCreateScriptCounts();
+
+ void emitWasmCompareAndSelect(LWasmCompareAndSelect* ins);
+
+ void testValueTruthyForType(JSValueType type, ScratchTagScope& tag,
+ const ValueOperand& value, Register tempToUnbox,
+ Register temp, FloatRegister floatTemp,
+ Label* ifTruthy, Label* ifFalsy,
+ OutOfLineTestObject* ool, bool skipTypeTest);
+
+ // Test whether value is truthy or not and jump to the corresponding label.
+ // The control flow falls through when the object is truthy, as an
+ // optimization.
+ void testValueTruthy(const ValueOperand& value, Register tempToUnbox,
+ Register temp, FloatRegister floatTemp,
+ const TypeDataList& observedTypes, Label* ifTruthy,
+ Label* ifFalsy, OutOfLineTestObject* ool);
+
+ // This function behaves like testObjectEmulatesUndefined with the exception
+ // that it can choose to let control flow fall through when the object
+ // doesn't emulate undefined, as an optimization. Use the regular
+ // testObjectEmulatesUndefined when it's required to branch to one of the
+ // two labels.
+ void testObjectEmulatesUndefinedKernel(Register objreg,
+ Label* ifEmulatesUndefined,
+ Label* ifDoesntEmulateUndefined,
+ Register scratch,
+ OutOfLineTestObject* ool);
+
+ // Test whether an object emulates |undefined|. If it does, jump to
+ // |ifEmulatesUndefined|; the caller is responsible for binding this label.
+ // If it doesn't, fall through; the label |ifDoesntEmulateUndefined| (which
+ // must be initially unbound) will be bound at this point.
+ void branchTestObjectEmulatesUndefined(Register objreg,
+ Label* ifEmulatesUndefined,
+ Label* ifDoesntEmulateUndefined,
+ Register scratch,
+ OutOfLineTestObject* ool);
+
+ // Test whether an object emulates |undefined|, and jump to the
+ // corresponding label.
+ //
+ // This method should be used when subsequent code can't be laid out in a
+ // straight line; if it can, branchTest* should be used instead.
+ void testObjectEmulatesUndefined(Register objreg, Label* ifEmulatesUndefined,
+ Label* ifDoesntEmulateUndefined,
+ Register scratch, OutOfLineTestObject* ool);
+
+ void emitStoreElementTyped(const LAllocation* value, MIRType valueType,
+ Register elements, const LAllocation* index);
+
+ // Bailout if an element about to be written to is a hole.
+ void emitStoreHoleCheck(Register elements, const LAllocation* index,
+ LSnapshot* snapshot);
+
+ void emitAssertRangeI(MIRType type, const Range* r, Register input);
+ void emitAssertRangeD(const Range* r, FloatRegister input,
+ FloatRegister temp);
+
+ void maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal,
+ OutOfLineCode* ool);
+
+ void incrementWarmUpCounter(AbsoluteAddress warmUpCount, JSScript* script,
+ Register tmp);
+
+ Vector<CodeOffset, 0, JitAllocPolicy> ionScriptLabels_;
+
+ // Used to bake in a pointer into the IonScript's list of nursery objects, for
+ // MNurseryObject codegen.
+ struct NurseryObjectLabel {
+ CodeOffset offset;
+ uint32_t nurseryIndex;
+ NurseryObjectLabel(CodeOffset offset, uint32_t nurseryIndex)
+ : offset(offset), nurseryIndex(nurseryIndex) {}
+ };
+ Vector<NurseryObjectLabel, 0, JitAllocPolicy> ionNurseryObjectLabels_;
+
+ void branchIfInvalidated(Register temp, Label* invalidated);
+
+#ifdef DEBUG
+ void emitDebugResultChecks(LInstruction* ins);
+ void emitGCThingResultChecks(LInstruction* lir, MDefinition* mir);
+ void emitValueResultChecks(LInstruction* lir, MDefinition* mir);
+#endif
+
+ // Script counts created during code generation.
+ IonScriptCounts* scriptCounts_;
+
+ IonPerfSpewer perfSpewer_;
+
+ // Bit mask of JitRealm stubs that are to be read-barriered.
+ uint32_t realmStubsToReadBarrier_;
+
+#ifdef FUZZING_JS_FUZZILLI
+ void emitFuzzilliHashDouble(FloatRegister floatDouble, Register scratch,
+ Register output);
+ void emitFuzzilliHashObject(LInstruction* lir, Register obj, Register output);
+ void emitFuzzilliHashBigInt(Register bigInt, Register output);
+#endif
+
+#define LIR_OP(op) void visit##op(L##op* ins);
+ LIR_OPCODE_LIST(LIR_OP)
+#undef LIR_OP
+};
+
+class OutOfLineResumableWasmTrap : public OutOfLineCodeBase<CodeGenerator> {
+ LInstruction* lir_;
+ size_t framePushed_;
+ wasm::BytecodeOffset bytecodeOffset_;
+ wasm::Trap trap_;
+
+ public:
+ OutOfLineResumableWasmTrap(LInstruction* lir, size_t framePushed,
+ wasm::BytecodeOffset bytecodeOffset,
+ wasm::Trap trap)
+ : lir_(lir),
+ framePushed_(framePushed),
+ bytecodeOffset_(bytecodeOffset),
+ trap_(trap) {}
+
+ void accept(CodeGenerator* codegen) override {
+ codegen->visitOutOfLineResumableWasmTrap(this);
+ }
+ LInstruction* lir() const { return lir_; }
+ size_t framePushed() const { return framePushed_; }
+ wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
+ wasm::Trap trap() const { return trap_; }
+};
+
+class OutOfLineAbortingWasmTrap : public OutOfLineCodeBase<CodeGenerator> {
+ wasm::BytecodeOffset bytecodeOffset_;
+ wasm::Trap trap_;
+
+ public:
+ OutOfLineAbortingWasmTrap(wasm::BytecodeOffset bytecodeOffset,
+ wasm::Trap trap)
+ : bytecodeOffset_(bytecodeOffset), trap_(trap) {}
+
+ void accept(CodeGenerator* codegen) override {
+ codegen->visitOutOfLineAbortingWasmTrap(this);
+ }
+ wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
+ wasm::Trap trap() const { return trap_; }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_CodeGenerator_h */