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