Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 :
7 : #ifndef jit_shared_Lowering_shared_h
8 : #define jit_shared_Lowering_shared_h
9 :
10 : // This file declares the structures that are used for attaching LIR to a
11 : // MIRGraph.
12 :
13 : #include "jit/LIR.h"
14 : #include "jit/MIRGenerator.h"
15 :
16 : namespace js {
17 : namespace jit {
18 :
19 : class MIRGenerator;
20 : class MIRGraph;
21 : class MDefinition;
22 : class MInstruction;
23 : class LOsiPoint;
24 :
25 : class LIRGeneratorShared : public MDefinitionVisitor
26 : {
27 : protected:
28 : MIRGenerator* gen;
29 : MIRGraph& graph;
30 : LIRGraph& lirGraph_;
31 : LBlock* current;
32 : MResumePoint* lastResumePoint_;
33 : LRecoverInfo* cachedRecoverInfo_;
34 : LOsiPoint* osiPoint_;
35 :
36 : public:
37 8 : LIRGeneratorShared(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
38 8 : : gen(gen),
39 : graph(graph),
40 : lirGraph_(lirGraph),
41 : lastResumePoint_(nullptr),
42 : cachedRecoverInfo_(nullptr),
43 8 : osiPoint_(nullptr)
44 8 : { }
45 :
46 403 : MIRGenerator* mir() {
47 403 : return gen;
48 : }
49 :
50 : // Needed to capture the abort error out of the visitInstruction methods.
51 1411 : bool errored() {
52 1411 : return gen->getOffThreadStatus().isErr();
53 : }
54 0 : void abort(AbortReason r, const char* message, ...) MOZ_FORMAT_PRINTF(3, 4) {
55 : va_list ap;
56 0 : va_start(ap, message);
57 0 : auto reason_ = gen->abortFmt(r, message, ap);
58 0 : va_end(ap);
59 0 : gen->setOffThreadStatus(reason_);
60 0 : }
61 : void abort(AbortReason r) {
62 : auto reason_ = gen->abort(r);
63 : gen->setOffThreadStatus(reason_);
64 : }
65 :
66 : protected:
67 :
68 : static void ReorderCommutative(MDefinition** lhsp, MDefinition** rhsp, MInstruction* ins);
69 : static bool ShouldReorderCommutative(MDefinition* lhs, MDefinition* rhs, MInstruction* ins);
70 :
71 : // A backend can decide that an instruction should be emitted at its uses,
72 : // rather than at its definition. To communicate this, set the
73 : // instruction's virtual register set to 0. When using the instruction,
74 : // its virtual register is temporarily reassigned. To know to clear it
75 : // after constructing the use information, the worklist bit is temporarily
76 : // unset.
77 : //
78 : // The backend can use the worklist bit to determine whether or not a
79 : // definition should be created.
80 : inline void emitAtUses(MInstruction* mir);
81 :
82 : // The lowest-level calls to use, those that do not wrap another call to
83 : // use(), must prefix grabbing virtual register IDs by these calls.
84 : inline void ensureDefined(MDefinition* mir);
85 :
86 : // These all create a use of a virtual register, with an optional
87 : // allocation policy.
88 : //
89 : // Some of these use functions have atStart variants.
90 : // - non-atStart variants will tell the register allocator that the input
91 : // allocation must be different from any Temp or Definition also needed for
92 : // this LInstruction.
93 : // - atStart variants relax that restriction and allow the input to be in
94 : // the same register as any Temp or output Definition used by the
95 : // LInstruction. Note that it doesn't *imply* this will actually happen,
96 : // but gives a hint to the register allocator that it can do it.
97 : //
98 : // TL;DR: Use non-atStart variants only if you need the input value after
99 : // writing to any temp or definitions, during code generation of this
100 : // LInstruction. Otherwise, use atStart variants, which will lower register
101 : // pressure.
102 : inline LUse use(MDefinition* mir, LUse policy);
103 : inline LUse use(MDefinition* mir);
104 : inline LUse useAtStart(MDefinition* mir);
105 : inline LUse useRegister(MDefinition* mir);
106 : inline LUse useRegisterAtStart(MDefinition* mir);
107 : inline LUse useFixed(MDefinition* mir, Register reg);
108 : inline LUse useFixed(MDefinition* mir, FloatRegister reg);
109 : inline LUse useFixed(MDefinition* mir, AnyRegister reg);
110 : inline LUse useFixedAtStart(MDefinition* mir, Register reg);
111 : inline LUse useFixedAtStart(MDefinition* mir, AnyRegister reg);
112 : inline LAllocation useOrConstant(MDefinition* mir);
113 : inline LAllocation useOrConstantAtStart(MDefinition* mir);
114 : // "Any" is architecture dependent, and will include registers and stack
115 : // slots on X86, and only registers on ARM.
116 : inline LAllocation useAny(MDefinition* mir);
117 : inline LAllocation useAnyOrConstant(MDefinition* mir);
118 : // "Storable" is architecture dependend, and will include registers and
119 : // constants on X86 and only registers on ARM. This is a generic "things
120 : // we can expect to write into memory in 1 instruction".
121 : inline LAllocation useStorable(MDefinition* mir);
122 : inline LAllocation useStorableAtStart(MDefinition* mir);
123 : inline LAllocation useKeepalive(MDefinition* mir);
124 : inline LAllocation useKeepaliveOrConstant(MDefinition* mir);
125 : inline LAllocation useRegisterOrConstant(MDefinition* mir);
126 : inline LAllocation useRegisterOrConstantAtStart(MDefinition* mir);
127 : inline LAllocation useRegisterOrZeroAtStart(MDefinition* mir);
128 : inline LAllocation useRegisterOrZero(MDefinition* mir);
129 : inline LAllocation useRegisterOrNonDoubleConstant(MDefinition* mir);
130 :
131 : inline LUse useRegisterForTypedLoad(MDefinition* mir, MIRType type);
132 :
133 : #ifdef JS_NUNBOX32
134 : inline LUse useType(MDefinition* mir, LUse::Policy policy);
135 : inline LUse usePayload(MDefinition* mir, LUse::Policy policy);
136 : inline LUse usePayloadAtStart(MDefinition* mir, LUse::Policy policy);
137 : inline LUse usePayloadInRegisterAtStart(MDefinition* mir);
138 :
139 : // Adds a box input to an instruction, setting operand |n| to the type and
140 : // |n+1| to the payload. Does not modify the operands, instead expecting a
141 : // policy to already be set.
142 : inline void fillBoxUses(LInstruction* lir, size_t n, MDefinition* mir);
143 : #endif
144 :
145 : // These create temporary register requests.
146 : inline LDefinition temp(LDefinition::Type type = LDefinition::GENERAL,
147 : LDefinition::Policy policy = LDefinition::REGISTER);
148 : inline LInt64Definition tempInt64(LDefinition::Policy policy = LDefinition::REGISTER);
149 : inline LDefinition tempFloat32();
150 : inline LDefinition tempDouble();
151 : inline LDefinition tempCopy(MDefinition* input, uint32_t reusedInput);
152 :
153 : // Note that the fixed register has a GENERAL type.
154 : inline LDefinition tempFixed(Register reg);
155 :
156 : template <size_t Ops, size_t Temps>
157 : inline void defineFixed(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir,
158 : const LAllocation& output);
159 :
160 : template <size_t Ops, size_t Temps>
161 : inline void defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
162 : LDefinition::Policy policy = LDefinition::REGISTER);
163 :
164 : template <size_t Ops, size_t Temps>
165 : inline void defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
166 : LDefinition::Policy policy = LDefinition::REGISTER);
167 :
168 : template <size_t Ops, size_t Temps>
169 : inline void defineInt64Fixed(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
170 : const LInt64Allocation& output);
171 :
172 : template <size_t Ops, size_t Temps>
173 : inline void defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefinition *mir,
174 : LDefinition::Policy policy = LDefinition::REGISTER);
175 :
176 : inline void defineSharedStubReturn(LInstruction* lir, MDefinition* mir);
177 : inline void defineReturn(LInstruction* lir, MDefinition* mir);
178 :
179 : template <size_t X>
180 : inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
181 : LDefinition::Policy policy = LDefinition::REGISTER);
182 : template <size_t X>
183 : inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
184 : const LDefinition& def);
185 :
186 : template <size_t Ops, size_t Temps>
187 : inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir,
188 : uint32_t operand);
189 :
190 : template <size_t Ops, size_t Temps>
191 : inline void defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
192 : MDefinition* mir, uint32_t operand);
193 :
194 : // Returns a box allocation for a Value-typed instruction.
195 : inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
196 : bool useAtStart = false);
197 :
198 : // Returns a box allocation. The use is either typed, a Value, or
199 : // a constant (if useConstant is true).
200 : inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
201 : inline LBoxAllocation useBoxOrTyped(MDefinition* mir);
202 :
203 : // Returns an int64 allocation for an Int64-typed instruction.
204 : inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
205 : inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
206 : inline LInt64Allocation useInt64AtStart(MDefinition* mir);
207 : inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
208 : inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
209 : inline LInt64Allocation useInt64RegisterOrConstant(MDefinition* mir, bool useAtStart = false);
210 : inline LInt64Allocation useInt64Fixed(MDefinition* mir, Register64 regs, bool useAtStart = false);
211 : inline LInt64Allocation useInt64FixedAtStart(MDefinition* mir, Register64 regs);
212 :
213 0 : LInt64Allocation useInt64RegisterAtStart(MDefinition* mir) {
214 0 : return useInt64Register(mir, /* useAtStart = */ true);
215 : }
216 0 : LInt64Allocation useInt64RegisterOrConstantAtStart(MDefinition* mir) {
217 0 : return useInt64RegisterOrConstant(mir, /* useAtStart = */ true);
218 : }
219 0 : LInt64Allocation useInt64OrConstantAtStart(MDefinition* mir) {
220 0 : return useInt64OrConstant(mir, /* useAtStart = */ true);
221 : }
222 :
223 : // Rather than defining a new virtual register, sets |ins| to have the same
224 : // virtual register as |as|.
225 : inline void redefine(MDefinition* ins, MDefinition* as);
226 :
227 : // Redefine a sin/cos call to sincos.
228 : inline void redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func);
229 :
230 1564 : TempAllocator& alloc() const {
231 1564 : return graph.alloc();
232 : }
233 :
234 1062 : uint32_t getVirtualRegister() {
235 1062 : uint32_t vreg = lirGraph_.getVirtualRegister();
236 :
237 : // If we run out of virtual registers, mark code generation as having
238 : // failed and return a dummy vreg. Include a + 1 here for NUNBOX32
239 : // platforms that expect Value vregs to be adjacent.
240 1062 : if (vreg + 1 >= MAX_VIRTUAL_REGISTERS) {
241 0 : abort(AbortReason::Alloc, "max virtual registers");
242 0 : return 1;
243 : }
244 1062 : return vreg;
245 : }
246 :
247 : template <typename T> void annotate(T* ins);
248 : template <typename T> void add(T* ins, MInstruction* mir = nullptr);
249 :
250 : void lowerTypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
251 : void defineTypedPhi(MPhi* phi, size_t lirIndex);
252 :
253 1411 : LOsiPoint* popOsiPoint() {
254 1411 : LOsiPoint* tmp = osiPoint_;
255 1411 : osiPoint_ = nullptr;
256 1411 : return tmp;
257 : }
258 :
259 : LRecoverInfo* getRecoverInfo(MResumePoint* rp);
260 : LSnapshot* buildSnapshot(LInstruction* ins, MResumePoint* rp, BailoutKind kind);
261 : bool assignPostSnapshot(MInstruction* mir, LInstruction* ins);
262 :
263 : // Marks this instruction as fallible, meaning that before it performs
264 : // effects (if any), it may check pre-conditions and bailout if they do not
265 : // hold. This function informs the register allocator that it will need to
266 : // capture appropriate state.
267 : void assignSnapshot(LInstruction* ins, BailoutKind kind);
268 :
269 : // Marks this instruction as needing to call into either the VM or GC. This
270 : // function may build a snapshot that captures the result of its own
271 : // instruction, and as such, should generally be called after define*().
272 : void assignSafepoint(LInstruction* ins, MInstruction* mir,
273 : BailoutKind kind = Bailout_DuringVMCall);
274 :
275 : public:
276 0 : void lowerConstantDouble(double d, MInstruction* mir) {
277 0 : define(new(alloc()) LDouble(d), mir);
278 0 : }
279 0 : void lowerConstantFloat32(float f, MInstruction* mir) {
280 0 : define(new(alloc()) LFloat32(f), mir);
281 0 : }
282 :
283 : void visitConstant(MConstant* ins) override;
284 : void visitWasmFloatConstant(MWasmFloatConstant* ins) override;
285 :
286 : // Whether to generate typed reads for element accesses with hole checks.
287 1 : static bool allowTypedElementHoleCheck() {
288 1 : return false;
289 : }
290 :
291 : // Whether to generate typed array accesses on statically known objects.
292 0 : static bool allowStaticTypedArrayAccesses() {
293 0 : return false;
294 : }
295 :
296 : // Provide NYI default implementations of the SIMD visitor functions.
297 : // Many targets don't implement SIMD at all, and we don't want to duplicate
298 : // these stubs in the specific sub-classes.
299 : // Some SIMD visitors are implemented in LIRGenerator in Lowering.cpp. These
300 : // shared implementations are not included here.
301 0 : void visitSimdInsertElement(MSimdInsertElement*) override { MOZ_CRASH("NYI"); }
302 0 : void visitSimdExtractElement(MSimdExtractElement*) override { MOZ_CRASH("NYI"); }
303 0 : void visitSimdBinaryArith(MSimdBinaryArith*) override { MOZ_CRASH("NYI"); }
304 0 : void visitSimdSelect(MSimdSelect*) override { MOZ_CRASH("NYI"); }
305 0 : void visitSimdSplat(MSimdSplat*) override { MOZ_CRASH("NYI"); }
306 0 : void visitSimdValueX4(MSimdValueX4*) override { MOZ_CRASH("NYI"); }
307 0 : void visitSimdBinarySaturating(MSimdBinarySaturating*) override { MOZ_CRASH("NYI"); }
308 0 : void visitSimdSwizzle(MSimdSwizzle*) override { MOZ_CRASH("NYI"); }
309 0 : void visitSimdShuffle(MSimdShuffle*) override { MOZ_CRASH("NYI"); }
310 0 : void visitSimdGeneralShuffle(MSimdGeneralShuffle*) override { MOZ_CRASH("NYI"); }
311 : };
312 :
313 : } // namespace jit
314 : } // namespace js
315 :
316 : #endif /* jit_shared_Lowering_shared_h */
|