1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef jit_CodeGenerator_h
8 #define jit_CodeGenerator_h
10 #include "jit/PerfSpewer.h"
11 #include "js/ScalarType.h" // js::Scalar::Type
13 #if defined(JS_CODEGEN_X86)
14 # include "jit/x86/CodeGenerator-x86.h"
15 #elif defined(JS_CODEGEN_X64)
16 # include "jit/x64/CodeGenerator-x64.h"
17 #elif defined(JS_CODEGEN_ARM)
18 # include "jit/arm/CodeGenerator-arm.h"
19 #elif defined(JS_CODEGEN_ARM64)
20 # include "jit/arm64/CodeGenerator-arm64.h"
21 #elif defined(JS_CODEGEN_MIPS32)
22 # include "jit/mips32/CodeGenerator-mips32.h"
23 #elif defined(JS_CODEGEN_MIPS64)
24 # include "jit/mips64/CodeGenerator-mips64.h"
25 #elif defined(JS_CODEGEN_LOONG64)
26 # include "jit/loong64/CodeGenerator-loong64.h"
27 #elif defined(JS_CODEGEN_RISCV64)
28 # include "jit/riscv64/CodeGenerator-riscv64.h"
29 #elif defined(JS_CODEGEN_WASM32)
30 # include "jit/wasm32/CodeGenerator-wasm32.h"
31 #elif defined(JS_CODEGEN_NONE)
32 # include "jit/none/CodeGenerator-none.h"
34 # error "Unknown architecture!"
48 template <typename Fn
, Fn fn
, class ArgSeq
, class StoreOutputTo
>
49 class OutOfLineCallVM
;
51 enum class SwitchTableType
{ Inline
, OutOfLine
};
53 template <SwitchTableType tableType
>
54 class OutOfLineSwitch
;
55 class OutOfLineTestObject
;
56 class OutOfLineNewArray
;
57 class OutOfLineNewObject
;
58 class CheckOverRecursedFailure
;
59 class OutOfLineUnboxFloatingPoint
;
60 class OutOfLineStoreElementHole
;
61 class OutOfLineTypeOfV
;
62 class OutOfLineTypeOfIsNonPrimitiveV
;
63 class OutOfLineTypeOfIsNonPrimitiveO
;
64 class OutOfLineUpdateCache
;
65 class OutOfLineICFallback
;
66 class OutOfLineCallPostWriteBarrier
;
67 class OutOfLineCallPostWriteElementBarrier
;
68 class OutOfLineElementPostWriteBarrier
;
69 class OutOfLineIsCallable
;
70 class OutOfLineIsConstructor
;
71 class OutOfLineRegExpMatcher
;
72 class OutOfLineRegExpSearcher
;
73 class OutOfLineRegExpExecMatch
;
74 class OutOfLineRegExpExecTest
;
75 class OutOfLineRegExpPrototypeOptimizable
;
76 class OutOfLineRegExpInstanceOptimizable
;
77 class OutOfLineNaNToZero
;
78 class OutOfLineResumableWasmTrap
;
79 class OutOfLineAbortingWasmTrap
;
80 class OutOfLineGuardNumberToIntPtrIndex
;
81 class OutOfLineBoxNonStrictThis
;
82 class OutOfLineArrayPush
;
83 class OutOfLineAtomizeSlot
;
84 class OutOfLineWasmCallPostWriteBarrier
;
85 class OutOfLineWasmNewStruct
;
87 class CodeGenerator final
: public CodeGeneratorSpecific
{
88 [[nodiscard
]] bool generateBody();
90 ConstantOrRegister
toConstantOrRegister(LInstruction
* lir
, size_t n
,
93 #ifdef CHECK_OSIPOINT_REGISTERS
94 void resetOsiPointRegs(LSafepoint
* safepoint
);
95 bool shouldVerifyOsiPointRegs(LSafepoint
* safepoint
);
96 void verifyOsiPointRegs(LSafepoint
* safepoint
);
99 void callVMInternal(VMFunctionId id
, LInstruction
* ins
);
101 template <typename Fn
, Fn fn
>
102 void callVM(LInstruction
* ins
);
104 template <typename Fn
, Fn fn
, class ArgSeq
, class StoreOutputTo
>
105 inline OutOfLineCode
* oolCallVM(LInstruction
* ins
, const ArgSeq
& args
,
106 const StoreOutputTo
& out
);
108 template <typename LCallIns
>
109 void emitCallNative(LCallIns
* call
, JSNative native
);
112 CodeGenerator(MIRGenerator
* gen
, LIRGraph
* graph
,
113 MacroAssembler
* masm
= nullptr);
116 [[nodiscard
]] bool generate();
117 [[nodiscard
]] bool generateWasm(
118 wasm::CallIndirectId callIndirectId
, wasm::BytecodeOffset trapOffset
,
119 const wasm::ArgTypeVector
& argTys
, const RegisterOffsets
& trapExitLayout
,
120 size_t trapExitLayoutNumWords
, wasm::FuncOffsets
* offsets
,
121 wasm::StackMaps
* stackMaps
, wasm::Decoder
* decoder
);
123 [[nodiscard
]] bool link(JSContext
* cx
, const WarpSnapshot
* snapshot
);
125 void emitOOLTestObject(Register objreg
, Label
* ifTruthy
, Label
* ifFalsy
,
128 void emitTypeOfCheck(JSValueType type
, Register tag
, Register output
,
129 Label
* done
, Label
* oolObject
);
130 void emitTypeOfJSType(JSValueType type
, Register output
);
131 void emitTypeOfObject(Register obj
, Register output
, Label
* done
);
132 void emitTypeOfIsObject(MTypeOfIs
* mir
, Register obj
, Register output
,
133 Label
* success
, Label
* fail
, Label
* slowCheck
);
134 void emitTypeOfIsObjectOOL(MTypeOfIs
* mir
, Register obj
, Register output
);
136 template <typename Fn
, Fn fn
, class ArgSeq
, class StoreOutputTo
>
137 void visitOutOfLineCallVM(
138 OutOfLineCallVM
<Fn
, fn
, ArgSeq
, StoreOutputTo
>* ool
);
140 void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher
* ool
);
141 void visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher
* ool
);
142 void visitOutOfLineRegExpExecMatch(OutOfLineRegExpExecMatch
* ool
);
143 void visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest
* ool
);
144 void visitOutOfLineRegExpPrototypeOptimizable(
145 OutOfLineRegExpPrototypeOptimizable
* ool
);
146 void visitOutOfLineRegExpInstanceOptimizable(
147 OutOfLineRegExpInstanceOptimizable
* ool
);
149 void visitOutOfLineTypeOfV(OutOfLineTypeOfV
* ool
);
150 void visitOutOfLineTypeOfIsNonPrimitiveV(OutOfLineTypeOfIsNonPrimitiveV
* ool
);
151 void visitOutOfLineTypeOfIsNonPrimitiveO(OutOfLineTypeOfIsNonPrimitiveO
* ool
);
153 template <SwitchTableType tableType
>
154 void visitOutOfLineSwitch(OutOfLineSwitch
<tableType
>* ool
);
156 void visitOutOfLineIsCallable(OutOfLineIsCallable
* ool
);
157 void visitOutOfLineIsConstructor(OutOfLineIsConstructor
* ool
);
159 void visitOutOfLineNaNToZero(OutOfLineNaNToZero
* ool
);
161 void visitOutOfLineResumableWasmTrap(OutOfLineResumableWasmTrap
* ool
);
162 void visitOutOfLineAbortingWasmTrap(OutOfLineAbortingWasmTrap
* ool
);
163 void visitCheckOverRecursedFailure(CheckOverRecursedFailure
* ool
);
165 void visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint
* ool
);
166 void visitOutOfLineStoreElementHole(OutOfLineStoreElementHole
* ool
);
168 void visitOutOfLineBoxNonStrictThis(OutOfLineBoxNonStrictThis
* ool
);
170 void visitOutOfLineICFallback(OutOfLineICFallback
* ool
);
172 void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier
* ool
);
173 void visitOutOfLineCallPostWriteElementBarrier(
174 OutOfLineCallPostWriteElementBarrier
* ool
);
176 void visitOutOfLineElementPostWriteBarrier(
177 OutOfLineElementPostWriteBarrier
* ool
);
179 void visitOutOfLineNewArray(OutOfLineNewArray
* ool
);
180 void visitOutOfLineNewObject(OutOfLineNewObject
* ool
);
182 void visitOutOfLineGuardNumberToIntPtrIndex(
183 OutOfLineGuardNumberToIntPtrIndex
* ool
);
185 void visitOutOfLineArrayPush(OutOfLineArrayPush
* ool
);
187 void visitOutOfLineAtomizeSlot(OutOfLineAtomizeSlot
* ool
);
189 void visitOutOfLineWasmCallPostWriteBarrier(
190 OutOfLineWasmCallPostWriteBarrier
* ool
);
192 void callWasmStructAllocFun(LInstruction
* lir
, wasm::SymbolicAddress fun
,
193 Register typeDefData
, Register output
);
194 void visitOutOfLineWasmNewStruct(OutOfLineWasmNewStruct
* ool
);
197 void emitPostWriteBarrier(const LAllocation
* obj
);
198 void emitPostWriteBarrier(Register objreg
);
199 void emitPostWriteBarrierS(Address address
, Register prev
, Register next
);
201 void emitElementPostWriteBarrier(MInstruction
* mir
,
202 const LiveRegisterSet
& liveVolatileRegs
,
203 Register obj
, const LAllocation
* index
,
205 const ConstantOrRegister
& val
,
206 int32_t indexDiff
= 0);
208 template <class LPostBarrierType
, MIRType nurseryType
>
209 void visitPostWriteBarrierCommon(LPostBarrierType
* lir
, OutOfLineCode
* ool
);
210 template <class LPostBarrierType
>
211 void visitPostWriteBarrierCommonV(LPostBarrierType
* lir
, OutOfLineCode
* ool
);
213 void emitCallInvokeFunction(LInstruction
* call
, Register callereg
,
214 bool isConstructing
, bool ignoresReturnValue
,
215 uint32_t argc
, uint32_t unusedStack
);
216 template <typename T
>
217 void emitApplyGeneric(T
* apply
);
218 template <typename T
>
219 void emitCallInvokeFunction(T
* apply
);
220 void emitAllocateSpaceForApply(Register argcreg
, Register scratch
);
221 void emitAllocateSpaceForConstructAndPushNewTarget(
222 Register argcreg
, Register newTargetAndScratch
);
223 void emitCopyValuesForApply(Register argvSrcBase
, Register argvIndex
,
224 Register copyreg
, size_t argvSrcOffset
,
225 size_t argvDstOffset
);
226 void emitRestoreStackPointerFromFP();
227 void emitPushArguments(Register argcreg
, Register scratch
, Register copyreg
,
228 uint32_t extraFormals
);
229 void emitPushArrayAsArguments(Register tmpArgc
, Register srcBaseAndArgc
,
230 Register scratch
, size_t argvSrcOffset
);
231 void emitPushArguments(LApplyArgsGeneric
* apply
, Register scratch
);
232 void emitPushArguments(LApplyArgsObj
* apply
, Register scratch
);
233 void emitPushArguments(LApplyArrayGeneric
* apply
, Register scratch
);
234 void emitPushArguments(LConstructArgsGeneric
* construct
, Register scratch
);
235 void emitPushArguments(LConstructArrayGeneric
* construct
, Register scratch
);
237 template <class GetInlinedArgument
>
238 void emitGetInlinedArgument(GetInlinedArgument
* lir
, Register index
,
239 ValueOperand output
);
241 void emitMaybeAtomizeSlot(LInstruction
* ins
, Register stringReg
,
242 Address slotAddr
, TypedOrValueRegister dest
);
244 using RegisterOrInt32
= mozilla::Variant
<Register
, int32_t>;
246 static RegisterOrInt32
ToRegisterOrInt32(const LAllocation
* allocation
);
249 void emitAssertArgumentsSliceBounds(const RegisterOrInt32
& begin
,
250 const RegisterOrInt32
& count
,
251 Register numActualArgs
);
254 template <class ArgumentsSlice
>
255 void emitNewArray(ArgumentsSlice
* lir
, const RegisterOrInt32
& count
,
256 Register output
, Register temp
);
258 void visitNewArrayCallVM(LNewArray
* lir
);
259 void visitNewObjectVMCall(LNewObject
* lir
);
261 void emitConcat(LInstruction
* lir
, Register lhs
, Register rhs
,
264 void emitInstanceOf(LInstruction
* ins
, Register protoReg
);
266 void loadJSScriptForBlock(MBasicBlock
* block
, Register reg
);
267 void loadOutermostJSScript(Register reg
);
270 void emitAssertResultV(const ValueOperand output
, const MDefinition
* mir
);
271 void emitAssertGCThingResult(Register input
, const MDefinition
* mir
);
275 void emitDebugForceBailing(LInstruction
* lir
);
278 IonScriptCounts
* extractScriptCounts() {
279 IonScriptCounts
* counts
= scriptCounts_
;
280 scriptCounts_
= nullptr; // prevent delete in dtor
284 void addGetPropertyCache(LInstruction
* ins
, LiveRegisterSet liveRegs
,
285 TypedOrValueRegister value
,
286 const ConstantOrRegister
& id
, ValueOperand output
);
287 void addSetPropertyCache(LInstruction
* ins
, LiveRegisterSet liveRegs
,
288 Register objReg
, Register temp
,
289 const ConstantOrRegister
& id
,
290 const ConstantOrRegister
& value
, bool strict
);
292 template <class IteratorObject
, class OrderedHashTable
>
293 void emitGetNextEntryForIterator(LGetNextEntryForIterator
* lir
);
295 template <class OrderedHashTable
>
296 void emitLoadIteratorValues(Register result
, Register temp
, Register front
);
298 void emitStringToInt64(LInstruction
* lir
, Register input
, Register64 output
);
300 OutOfLineCode
* createBigIntOutOfLine(LInstruction
* lir
, Scalar::Type type
,
301 Register64 input
, Register output
);
303 void emitCreateBigInt(LInstruction
* lir
, Scalar::Type type
, Register64 input
,
304 Register output
, Register maybeTemp
);
306 template <size_t NumDefs
>
307 void emitIonToWasmCallBase(LIonToWasmCallBase
<NumDefs
>* lir
);
309 IonScriptCounts
* maybeCreateScriptCounts();
311 void emitWasmCompareAndSelect(LWasmCompareAndSelect
* ins
);
313 void testValueTruthyForType(JSValueType type
, ScratchTagScope
& tag
,
314 const ValueOperand
& value
, Register tempToUnbox
,
315 Register temp
, FloatRegister floatTemp
,
316 Label
* ifTruthy
, Label
* ifFalsy
,
317 OutOfLineTestObject
* ool
, bool skipTypeTest
);
319 // Test whether value is truthy or not and jump to the corresponding label.
320 // The control flow falls through when the object is truthy, as an
322 void testValueTruthy(const ValueOperand
& value
, Register tempToUnbox
,
323 Register temp
, FloatRegister floatTemp
,
324 const TypeDataList
& observedTypes
, Label
* ifTruthy
,
325 Label
* ifFalsy
, OutOfLineTestObject
* ool
);
327 // This function behaves like testObjectEmulatesUndefined with the exception
328 // that it can choose to let control flow fall through when the object
329 // doesn't emulate undefined, as an optimization. Use the regular
330 // testObjectEmulatesUndefined when it's required to branch to one of the
332 void testObjectEmulatesUndefinedKernel(Register objreg
,
333 Label
* ifEmulatesUndefined
,
334 Label
* ifDoesntEmulateUndefined
,
336 OutOfLineTestObject
* ool
);
338 // Test whether an object emulates |undefined|. If it does, jump to
339 // |ifEmulatesUndefined|; the caller is responsible for binding this label.
340 // If it doesn't, fall through; the label |ifDoesntEmulateUndefined| (which
341 // must be initially unbound) will be bound at this point.
342 void branchTestObjectEmulatesUndefined(Register objreg
,
343 Label
* ifEmulatesUndefined
,
344 Label
* ifDoesntEmulateUndefined
,
346 OutOfLineTestObject
* ool
);
348 // Test whether an object emulates |undefined|, and jump to the
349 // corresponding label.
351 // This method should be used when subsequent code can't be laid out in a
352 // straight line; if it can, branchTest* should be used instead.
353 void testObjectEmulatesUndefined(Register objreg
, Label
* ifEmulatesUndefined
,
354 Label
* ifDoesntEmulateUndefined
,
355 Register scratch
, OutOfLineTestObject
* ool
);
357 void emitStoreElementTyped(const LAllocation
* value
, MIRType valueType
,
358 Register elements
, const LAllocation
* index
);
360 // Bailout if an element about to be written to is a hole.
361 void emitStoreHoleCheck(Register elements
, const LAllocation
* index
,
362 LSnapshot
* snapshot
);
364 void emitAssertRangeI(MIRType type
, const Range
* r
, Register input
);
365 void emitAssertRangeD(const Range
* r
, FloatRegister input
,
368 void maybeEmitGlobalBarrierCheck(const LAllocation
* maybeGlobal
,
371 void incrementWarmUpCounter(AbsoluteAddress warmUpCount
, JSScript
* script
,
374 Vector
<CodeOffset
, 0, JitAllocPolicy
> ionScriptLabels_
;
376 // Used to bake in a pointer into the IonScript's list of nursery objects, for
377 // MNurseryObject codegen.
378 struct NurseryObjectLabel
{
380 uint32_t nurseryIndex
;
381 NurseryObjectLabel(CodeOffset offset
, uint32_t nurseryIndex
)
382 : offset(offset
), nurseryIndex(nurseryIndex
) {}
384 Vector
<NurseryObjectLabel
, 0, JitAllocPolicy
> ionNurseryObjectLabels_
;
386 void branchIfInvalidated(Register temp
, Label
* invalidated
);
389 void emitDebugResultChecks(LInstruction
* ins
);
390 void emitGCThingResultChecks(LInstruction
* lir
, MDefinition
* mir
);
391 void emitValueResultChecks(LInstruction
* lir
, MDefinition
* mir
);
394 // Script counts created during code generation.
395 IonScriptCounts
* scriptCounts_
;
397 IonPerfSpewer perfSpewer_
;
399 // Bit mask of JitZone stubs that are to be read-barriered.
400 uint32_t zoneStubsToReadBarrier_
;
402 #ifdef FUZZING_JS_FUZZILLI
403 void emitFuzzilliHashDouble(FloatRegister floatDouble
, Register scratch
,
405 void emitFuzzilliHashObject(LInstruction
* lir
, Register obj
, Register output
);
406 void emitFuzzilliHashBigInt(Register bigInt
, Register output
);
409 #define LIR_OP(op) void visit##op(L##op* ins);
410 LIR_OPCODE_LIST(LIR_OP
)
413 // In debug mode, we need to validate that we've not made a mistake with the
415 void assertObjectDoesNotEmulateUndefined(Register input
, Register temp
,
416 const MInstruction
* mir
);
418 // Enumerates the fuses that a code generation can depend on. These will
419 // be mapped to an actual fuse by validateAndRegisterFuseDependencies.
420 enum class FuseDependencyKind
{
421 HasSeenObjectEmulateUndefinedFuse
,
424 // The set of fuses this code generation depends on.
425 mozilla::EnumSet
<FuseDependencyKind
> fuseDependencies
;
427 // Register a dependency on the HasSeenObjectEmulateUndefined fuse.
428 void addHasSeenObjectEmulateUndefinedFuseDependency() {
429 fuseDependencies
+= FuseDependencyKind::HasSeenObjectEmulateUndefinedFuse
;
432 // Called during linking on main-thread: Ensures that the fuses are still
433 // intact, and registers a script dependency on a specific fuse before
434 // finishing compilation.
435 void validateAndRegisterFuseDependencies(JSContext
* cx
, HandleScript script
,
438 // Return true if the fuse is intact, andd if the fuse is intact note the
440 bool hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted() {
441 if (!JitOptions
.useHasSeenEmulatesUndefinedFuse
) {
442 // if we're not active, simply pretend the fuse is popped.
446 bool intact
= gen
->outerInfo().hasSeenObjectEmulateUndefinedFuseIntact();
448 addHasSeenObjectEmulateUndefinedFuseDependency();
454 class OutOfLineResumableWasmTrap
: public OutOfLineCodeBase
<CodeGenerator
> {
457 wasm::BytecodeOffset bytecodeOffset_
;
461 OutOfLineResumableWasmTrap(LInstruction
* lir
, size_t framePushed
,
462 wasm::BytecodeOffset bytecodeOffset
,
465 framePushed_(framePushed
),
466 bytecodeOffset_(bytecodeOffset
),
469 void accept(CodeGenerator
* codegen
) override
{
470 codegen
->visitOutOfLineResumableWasmTrap(this);
472 LInstruction
* lir() const { return lir_
; }
473 size_t framePushed() const { return framePushed_
; }
474 wasm::BytecodeOffset
bytecodeOffset() const { return bytecodeOffset_
; }
475 wasm::Trap
trap() const { return trap_
; }
478 class OutOfLineAbortingWasmTrap
: public OutOfLineCodeBase
<CodeGenerator
> {
479 wasm::BytecodeOffset bytecodeOffset_
;
483 OutOfLineAbortingWasmTrap(wasm::BytecodeOffset bytecodeOffset
,
485 : bytecodeOffset_(bytecodeOffset
), trap_(trap
) {}
487 void accept(CodeGenerator
* codegen
) override
{
488 codegen
->visitOutOfLineAbortingWasmTrap(this);
490 wasm::BytecodeOffset
bytecodeOffset() const { return bytecodeOffset_
; }
491 wasm::Trap
trap() const { return trap_
; }
497 #endif /* jit_CodeGenerator_h */