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_TrialInlining_h
8 #define jit_TrialInlining_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
17 #include "NamespaceImports.h"
19 #include "gc/Barrier.h"
20 #include "jit/CacheIR.h"
21 #include "jit/ICStubSpace.h"
22 #include "js/RootingAPI.h"
23 #include "js/TypeDecls.h"
24 #include "js/UniquePtr.h"
25 #include "js/Vector.h"
26 #include "vm/JSScript.h"
29 * [SMDOC] Trial Inlining
31 * WarpBuilder relies on transpiling CacheIR. When inlining scripted
32 * functions in WarpBuilder, we want our ICs to be as monomorphic as
33 * possible. Functions with multiple callers complicate this. An IC in
34 * such a function might be monomorphic for any given caller, but
35 * polymorphic overall. This make the input to WarpBuilder less precise.
37 * To solve this problem, we do trial inlining. During baseline
38 * execution, we identify call sites for which it would be useful to
39 * have more precise inlining data. For each such call site, we
40 * allocate a fresh ICScript and replace the existing call IC with a
41 * new specialized IC that invokes the callee using the new
42 * ICScript. Other callers of the callee will continue using the
43 * default ICScript. When we eventually Warp-compile the script, we
44 * can generate code for the callee using the IC information in our
45 * private ICScript, which is specialized for its caller.
47 * The same approach can be used to inline recursively.
50 class JS_PUBLIC_API JSTracer
;
51 struct JS_PUBLIC_API JSContext
;
61 class BytecodeLocation
;
73 * An InliningRoot is owned by a JitScript. In turn, it owns the set
74 * of ICScripts that are candidates for being inlined in that JitScript.
78 explicit InliningRoot(JSContext
* cx
, JSScript
* owningScript
)
79 : owningScript_(owningScript
),
81 totalBytecodeSize_(owningScript
->length()) {}
83 JitScriptICStubSpace
* jitScriptStubSpace() { return &jitScriptStubSpace_
; }
85 void trace(JSTracer
* trc
);
86 void traceWeak(JSTracer
* trc
);
88 bool addInlinedScript(js::UniquePtr
<ICScript
> icScript
);
90 uint32_t numInlinedScripts() const { return inlinedScripts_
.length(); }
92 void purgeOptimizedStubs(Zone
* zone
);
93 void resetWarmUpCounts(uint32_t count
);
95 JSScript
* owningScript() const { return owningScript_
; }
97 size_t totalBytecodeSize() const { return totalBytecodeSize_
; }
99 void addToTotalBytecodeSize(size_t size
) { totalBytecodeSize_
+= size
; }
102 JitScriptICStubSpace jitScriptStubSpace_
= {};
103 HeapPtr
<JSScript
*> owningScript_
;
104 js::Vector
<js::UniquePtr
<ICScript
>> inlinedScripts_
;
106 // Bytecode size of outer script and all inlined scripts.
107 size_t totalBytecodeSize_
;
110 class InlinableOpData
{
112 JSFunction
* target
= nullptr;
113 ICScript
* icScript
= nullptr;
114 const uint8_t* endOfSharedPrefix
= nullptr;
117 class InlinableCallData
: public InlinableOpData
{
119 ObjOperandId calleeOperand
;
123 class InlinableGetterData
: public InlinableOpData
{
125 ValOperandId receiverOperand
;
126 bool sameRealm
= false;
129 class InlinableSetterData
: public InlinableOpData
{
131 ObjOperandId receiverOperand
;
132 ValOperandId rhsOperand
;
133 bool sameRealm
= false;
136 mozilla::Maybe
<InlinableOpData
> FindInlinableOpData(ICCacheIRStub
* stub
,
137 BytecodeLocation loc
);
139 mozilla::Maybe
<InlinableCallData
> FindInlinableCallData(ICCacheIRStub
* stub
);
140 mozilla::Maybe
<InlinableGetterData
> FindInlinableGetterData(
141 ICCacheIRStub
* stub
);
142 mozilla::Maybe
<InlinableSetterData
> FindInlinableSetterData(
143 ICCacheIRStub
* stub
);
145 enum class TrialInliningDecision
{
151 class MOZ_RAII TrialInliner
{
153 TrialInliner(JSContext
* cx
, HandleScript script
, ICScript
* icScript
)
154 : cx_(cx
), script_(script
), icScript_(icScript
) {}
156 JSContext
* cx() { return cx_
; }
158 [[nodiscard
]] bool tryInlining();
159 [[nodiscard
]] bool maybeInlineCall(ICEntry
& entry
, ICFallbackStub
* fallback
,
160 BytecodeLocation loc
);
161 [[nodiscard
]] bool maybeInlineGetter(ICEntry
& entry
, ICFallbackStub
* fallback
,
162 BytecodeLocation loc
, CacheKind kind
);
163 [[nodiscard
]] bool maybeInlineSetter(ICEntry
& entry
, ICFallbackStub
* fallback
,
164 BytecodeLocation loc
, CacheKind kind
);
166 static bool canInline(JSFunction
* target
, HandleScript caller
,
167 BytecodeLocation loc
);
170 ICCacheIRStub
* maybeSingleStub(const ICEntry
& entry
);
171 void cloneSharedPrefix(ICCacheIRStub
* stub
, const uint8_t* endOfPrefix
,
172 CacheIRWriter
& writer
);
173 ICScript
* createInlinedICScript(JSFunction
* target
, BytecodeLocation loc
);
174 [[nodiscard
]] bool replaceICStub(ICEntry
& entry
, ICFallbackStub
* fallback
,
175 CacheIRWriter
& writer
, CacheKind kind
);
177 TrialInliningDecision
getInliningDecision(JSFunction
* target
,
179 BytecodeLocation loc
);
181 InliningRoot
* getOrCreateInliningRoot();
182 InliningRoot
* maybeGetInliningRoot() const;
183 size_t inliningRootTotalBytecodeSize() const;
186 HandleScript script_
;
190 bool DoTrialInlining(JSContext
* cx
, BaselineFrame
* frame
);
195 #endif /* jit_TrialInlining_h */