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