/* -*- 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 class OutOfLineCallVM; enum class SwitchTableType { Inline, OutOfLine }; template 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 void callVM(LInstruction* ins); template 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 void visitOutOfLineCallVM( OutOfLineCallVM* 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 void visitOutOfLineSwitch(OutOfLineSwitch* 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 void visitPostWriteBarrierCommon(LPostBarrierType* lir, OutOfLineCode* ool); template void visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool); void emitCallInvokeFunction(LInstruction* call, Register callereg, bool isConstructing, bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack); template void emitApplyGeneric(T* apply); template 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 void emitGetInlinedArgument(GetInlinedArgument* lir, Register index, ValueOperand output); using RegisterOrInt32 = mozilla::Variant; static RegisterOrInt32 ToRegisterOrInt32(const LAllocation* allocation); #ifdef DEBUG void emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin, const RegisterOrInt32& count, Register numActualArgs); #endif template 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 void emitGetNextEntryForIterator(LGetNextEntryForIterator* lir); template 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 void emitIonToWasmCallBase(LIonToWasmCallBase* 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 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 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 { 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 { 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 */