|
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_CodeGenerator_shared_h |
|
8 #define jit_shared_CodeGenerator_shared_h |
|
9 |
|
10 #include "mozilla/Alignment.h" |
|
11 |
|
12 #include "jit/IonFrames.h" |
|
13 #include "jit/IonMacroAssembler.h" |
|
14 #include "jit/LIR.h" |
|
15 #include "jit/MIRGenerator.h" |
|
16 #include "jit/MIRGraph.h" |
|
17 #include "jit/Safepoints.h" |
|
18 #include "jit/Snapshots.h" |
|
19 #include "jit/VMFunctions.h" |
|
20 #include "vm/ForkJoin.h" |
|
21 |
|
22 namespace js { |
|
23 namespace jit { |
|
24 |
|
25 class OutOfLineCode; |
|
26 class CodeGenerator; |
|
27 class MacroAssembler; |
|
28 class IonCache; |
|
29 class OutOfLineAbortPar; |
|
30 class OutOfLinePropagateAbortPar; |
|
31 |
|
32 template <class ArgSeq, class StoreOutputTo> |
|
33 class OutOfLineCallVM; |
|
34 |
|
35 class OutOfLineTruncateSlow; |
|
36 |
|
37 struct PatchableBackedgeInfo |
|
38 { |
|
39 CodeOffsetJump backedge; |
|
40 Label *loopHeader; |
|
41 Label *interruptCheck; |
|
42 |
|
43 PatchableBackedgeInfo(CodeOffsetJump backedge, Label *loopHeader, Label *interruptCheck) |
|
44 : backedge(backedge), loopHeader(loopHeader), interruptCheck(interruptCheck) |
|
45 {} |
|
46 }; |
|
47 |
|
48 struct ReciprocalMulConstants { |
|
49 int32_t multiplier; |
|
50 int32_t shiftAmount; |
|
51 }; |
|
52 |
|
53 class CodeGeneratorShared : public LInstructionVisitor |
|
54 { |
|
55 js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_; |
|
56 OutOfLineCode *oolIns; |
|
57 |
|
58 MacroAssembler &ensureMasm(MacroAssembler *masm); |
|
59 mozilla::Maybe<MacroAssembler> maybeMasm_; |
|
60 |
|
61 public: |
|
62 MacroAssembler &masm; |
|
63 |
|
64 protected: |
|
65 MIRGenerator *gen; |
|
66 LIRGraph &graph; |
|
67 LBlock *current; |
|
68 SnapshotWriter snapshots_; |
|
69 RecoverWriter recovers_; |
|
70 JitCode *deoptTable_; |
|
71 #ifdef DEBUG |
|
72 uint32_t pushedArgs_; |
|
73 #endif |
|
74 uint32_t lastOsiPointOffset_; |
|
75 SafepointWriter safepoints_; |
|
76 Label invalidate_; |
|
77 CodeOffsetLabel invalidateEpilogueData_; |
|
78 |
|
79 js::Vector<SafepointIndex, 0, SystemAllocPolicy> safepointIndices_; |
|
80 js::Vector<OsiIndex, 0, SystemAllocPolicy> osiIndices_; |
|
81 |
|
82 // Mapping from bailout table ID to an offset in the snapshot buffer. |
|
83 js::Vector<SnapshotOffset, 0, SystemAllocPolicy> bailouts_; |
|
84 |
|
85 // Allocated data space needed at runtime. |
|
86 js::Vector<uint8_t, 0, SystemAllocPolicy> runtimeData_; |
|
87 |
|
88 // Vector of information about generated polymorphic inline caches. |
|
89 js::Vector<uint32_t, 0, SystemAllocPolicy> cacheList_; |
|
90 |
|
91 // List of stack slots that have been pushed as arguments to an MCall. |
|
92 js::Vector<uint32_t, 0, SystemAllocPolicy> pushedArgumentSlots_; |
|
93 |
|
94 // Patchable backedges generated for loops. |
|
95 Vector<PatchableBackedgeInfo, 0, SystemAllocPolicy> patchableBackedges_; |
|
96 |
|
97 #ifdef JS_TRACE_LOGGING |
|
98 js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTraceLoggers_; |
|
99 js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTLScripts_; |
|
100 #endif |
|
101 |
|
102 // When profiling is enabled, this is the instrumentation manager which |
|
103 // maintains state of what script is currently being generated (for inline |
|
104 // scripts) and when instrumentation needs to be emitted or skipped. |
|
105 IonInstrumentation sps_; |
|
106 |
|
107 protected: |
|
108 // The offset of the first instruction of the OSR entry block from the |
|
109 // beginning of the code buffer. |
|
110 size_t osrEntryOffset_; |
|
111 |
|
112 TempAllocator &alloc() const { |
|
113 return graph.mir().alloc(); |
|
114 } |
|
115 |
|
116 inline void setOsrEntryOffset(size_t offset) { |
|
117 JS_ASSERT(osrEntryOffset_ == 0); |
|
118 osrEntryOffset_ = offset; |
|
119 } |
|
120 inline size_t getOsrEntryOffset() const { |
|
121 return osrEntryOffset_; |
|
122 } |
|
123 |
|
124 // The offset of the first instruction of the body. |
|
125 // This skips the arguments type checks. |
|
126 size_t skipArgCheckEntryOffset_; |
|
127 |
|
128 inline void setSkipArgCheckEntryOffset(size_t offset) { |
|
129 JS_ASSERT(skipArgCheckEntryOffset_ == 0); |
|
130 skipArgCheckEntryOffset_ = offset; |
|
131 } |
|
132 inline size_t getSkipArgCheckEntryOffset() const { |
|
133 return skipArgCheckEntryOffset_; |
|
134 } |
|
135 |
|
136 typedef js::Vector<SafepointIndex, 8, SystemAllocPolicy> SafepointIndices; |
|
137 |
|
138 bool markArgumentSlots(LSafepoint *safepoint); |
|
139 void dropArguments(unsigned argc); |
|
140 |
|
141 protected: |
|
142 // The initial size of the frame in bytes. These are bytes beyond the |
|
143 // constant header present for every Ion frame, used for pre-determined |
|
144 // spills. |
|
145 int32_t frameDepth_; |
|
146 |
|
147 // Frame class this frame's size falls into (see IonFrame.h). |
|
148 FrameSizeClass frameClass_; |
|
149 |
|
150 // For arguments to the current function. |
|
151 inline int32_t ArgToStackOffset(int32_t slot) const { |
|
152 return masm.framePushed() + |
|
153 (gen->compilingAsmJS() ? NativeFrameSize : sizeof(IonJSFrameLayout)) + |
|
154 slot; |
|
155 } |
|
156 |
|
157 // For the callee of the current function. |
|
158 inline int32_t CalleeStackOffset() const { |
|
159 return masm.framePushed() + IonJSFrameLayout::offsetOfCalleeToken(); |
|
160 } |
|
161 |
|
162 inline int32_t SlotToStackOffset(int32_t slot) const { |
|
163 JS_ASSERT(slot > 0 && slot <= int32_t(graph.localSlotCount())); |
|
164 int32_t offset = masm.framePushed() - slot; |
|
165 JS_ASSERT(offset >= 0); |
|
166 return offset; |
|
167 } |
|
168 inline int32_t StackOffsetToSlot(int32_t offset) const { |
|
169 // See: SlotToStackOffset. This is used to convert pushed arguments |
|
170 // to a slot index that safepoints can use. |
|
171 // |
|
172 // offset = framePushed - slot |
|
173 // offset + slot = framePushed |
|
174 // slot = framePushed - offset |
|
175 return masm.framePushed() - offset; |
|
176 } |
|
177 |
|
178 // For argument construction for calls. Argslots are Value-sized. |
|
179 inline int32_t StackOffsetOfPassedArg(int32_t slot) const { |
|
180 // A slot of 0 is permitted only to calculate %esp offset for calls. |
|
181 JS_ASSERT(slot >= 0 && slot <= int32_t(graph.argumentSlotCount())); |
|
182 int32_t offset = masm.framePushed() - |
|
183 graph.paddedLocalSlotsSize() - |
|
184 (slot * sizeof(Value)); |
|
185 |
|
186 // Passed arguments go below A function's local stack storage. |
|
187 // When arguments are being pushed, there is nothing important on the stack. |
|
188 // Therefore, It is safe to push the arguments down arbitrarily. Pushing |
|
189 // by sizeof(Value) is desirable since everything on the stack is a Value. |
|
190 // Note that paddedLocalSlotCount() aligns to at least a Value boundary |
|
191 // specifically to support this. |
|
192 JS_ASSERT(offset >= 0); |
|
193 JS_ASSERT(offset % sizeof(Value) == 0); |
|
194 return offset; |
|
195 } |
|
196 |
|
197 inline int32_t ToStackOffset(const LAllocation *a) const { |
|
198 if (a->isArgument()) |
|
199 return ArgToStackOffset(a->toArgument()->index()); |
|
200 return SlotToStackOffset(a->toStackSlot()->slot()); |
|
201 } |
|
202 |
|
203 uint32_t frameSize() const { |
|
204 return frameClass_ == FrameSizeClass::None() ? frameDepth_ : frameClass_.frameSize(); |
|
205 } |
|
206 |
|
207 protected: |
|
208 // Ensure the cache is an IonCache while expecting the size of the derived |
|
209 // class. We only need the cache list at GC time. Everyone else can just take |
|
210 // runtimeData offsets. |
|
211 size_t allocateCache(const IonCache &, size_t size) { |
|
212 size_t dataOffset = allocateData(size); |
|
213 masm.propagateOOM(cacheList_.append(dataOffset)); |
|
214 return dataOffset; |
|
215 } |
|
216 |
|
217 #ifdef CHECK_OSIPOINT_REGISTERS |
|
218 void resetOsiPointRegs(LSafepoint *safepoint); |
|
219 bool shouldVerifyOsiPointRegs(LSafepoint *safepoint); |
|
220 void verifyOsiPointRegs(LSafepoint *safepoint); |
|
221 #endif |
|
222 |
|
223 public: |
|
224 |
|
225 // When appending to runtimeData_, the vector might realloc, leaving pointers |
|
226 // int the origianl vector stale and unusable. DataPtr acts like a pointer, |
|
227 // but allows safety in the face of potentially realloc'ing vector appends. |
|
228 friend class DataPtr; |
|
229 template <typename T> |
|
230 class DataPtr |
|
231 { |
|
232 CodeGeneratorShared *cg_; |
|
233 size_t index_; |
|
234 |
|
235 T *lookup() { |
|
236 return reinterpret_cast<T *>(&cg_->runtimeData_[index_]); |
|
237 } |
|
238 public: |
|
239 DataPtr(CodeGeneratorShared *cg, size_t index) |
|
240 : cg_(cg), index_(index) { } |
|
241 |
|
242 T * operator ->() { |
|
243 return lookup(); |
|
244 } |
|
245 T * operator *() { |
|
246 return lookup(); |
|
247 } |
|
248 }; |
|
249 |
|
250 protected: |
|
251 |
|
252 size_t allocateData(size_t size) { |
|
253 JS_ASSERT(size % sizeof(void *) == 0); |
|
254 size_t dataOffset = runtimeData_.length(); |
|
255 masm.propagateOOM(runtimeData_.appendN(0, size)); |
|
256 return dataOffset; |
|
257 } |
|
258 |
|
259 template <typename T> |
|
260 inline size_t allocateCache(const T &cache) { |
|
261 size_t index = allocateCache(cache, sizeof(mozilla::AlignedStorage2<T>)); |
|
262 if (masm.oom()) |
|
263 return SIZE_MAX; |
|
264 // Use the copy constructor on the allocated space. |
|
265 JS_ASSERT(index == cacheList_.back()); |
|
266 new (&runtimeData_[index]) T(cache); |
|
267 return index; |
|
268 } |
|
269 |
|
270 protected: |
|
271 // Encodes an LSnapshot into the compressed snapshot buffer, returning |
|
272 // false on failure. |
|
273 bool encode(LRecoverInfo *recover); |
|
274 bool encode(LSnapshot *snapshot); |
|
275 bool encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint, uint32_t *startIndex); |
|
276 |
|
277 // Attempts to assign a BailoutId to a snapshot, if one isn't already set. |
|
278 // If the bailout table is full, this returns false, which is not a fatal |
|
279 // error (the code generator may use a slower bailout mechanism). |
|
280 bool assignBailoutId(LSnapshot *snapshot); |
|
281 |
|
282 // Encode all encountered safepoints in CG-order, and resolve |indices| for |
|
283 // safepoint offsets. |
|
284 void encodeSafepoints(); |
|
285 |
|
286 // Mark the safepoint on |ins| as corresponding to the current assembler location. |
|
287 // The location should be just after a call. |
|
288 bool markSafepoint(LInstruction *ins); |
|
289 bool markSafepointAt(uint32_t offset, LInstruction *ins); |
|
290 |
|
291 // Mark the OSI point |ins| as corresponding to the current |
|
292 // assembler location inside the |osiIndices_|. Return the assembler |
|
293 // location for the OSI point return location within |
|
294 // |returnPointOffset|. |
|
295 bool markOsiPoint(LOsiPoint *ins, uint32_t *returnPointOffset); |
|
296 |
|
297 // Ensure that there is enough room between the last OSI point and the |
|
298 // current instruction, such that: |
|
299 // (1) Invalidation will not overwrite the current instruction, and |
|
300 // (2) Overwriting the current instruction will not overwrite |
|
301 // an invalidation marker. |
|
302 void ensureOsiSpace(); |
|
303 |
|
304 OutOfLineCode *oolTruncateDouble(const FloatRegister &src, const Register &dest); |
|
305 bool emitTruncateDouble(const FloatRegister &src, const Register &dest); |
|
306 bool emitTruncateFloat32(const FloatRegister &src, const Register &dest); |
|
307 |
|
308 void emitPreBarrier(Register base, const LAllocation *index, MIRType type); |
|
309 void emitPreBarrier(Address address, MIRType type); |
|
310 |
|
311 inline bool isNextBlock(LBlock *block) { |
|
312 return current->mir()->id() + 1 == block->mir()->id(); |
|
313 } |
|
314 |
|
315 public: |
|
316 // Save and restore all volatile registers to/from the stack, excluding the |
|
317 // specified register(s), before a function call made using callWithABI and |
|
318 // after storing the function call's return value to an output register. |
|
319 // (The only registers that don't need to be saved/restored are 1) the |
|
320 // temporary register used to store the return value of the function call, |
|
321 // if there is one [otherwise that stored value would be overwritten]; and |
|
322 // 2) temporary registers whose values aren't needed in the rest of the LIR |
|
323 // instruction [this is purely an optimization]. All other volatiles must |
|
324 // be saved and restored in case future LIR instructions need those values.) |
|
325 void saveVolatile(Register output) { |
|
326 RegisterSet regs = RegisterSet::Volatile(); |
|
327 regs.takeUnchecked(output); |
|
328 masm.PushRegsInMask(regs); |
|
329 } |
|
330 void restoreVolatile(Register output) { |
|
331 RegisterSet regs = RegisterSet::Volatile(); |
|
332 regs.takeUnchecked(output); |
|
333 masm.PopRegsInMask(regs); |
|
334 } |
|
335 void saveVolatile(FloatRegister output) { |
|
336 RegisterSet regs = RegisterSet::Volatile(); |
|
337 regs.takeUnchecked(output); |
|
338 masm.PushRegsInMask(regs); |
|
339 } |
|
340 void restoreVolatile(FloatRegister output) { |
|
341 RegisterSet regs = RegisterSet::Volatile(); |
|
342 regs.takeUnchecked(output); |
|
343 masm.PopRegsInMask(regs); |
|
344 } |
|
345 void saveVolatile(RegisterSet temps) { |
|
346 masm.PushRegsInMask(RegisterSet::VolatileNot(temps)); |
|
347 } |
|
348 void restoreVolatile(RegisterSet temps) { |
|
349 masm.PopRegsInMask(RegisterSet::VolatileNot(temps)); |
|
350 } |
|
351 void saveVolatile() { |
|
352 masm.PushRegsInMask(RegisterSet::Volatile()); |
|
353 } |
|
354 void restoreVolatile() { |
|
355 masm.PopRegsInMask(RegisterSet::Volatile()); |
|
356 } |
|
357 |
|
358 // These functions have to be called before and after any callVM and before |
|
359 // any modifications of the stack. Modification of the stack made after |
|
360 // these calls should update the framePushed variable, needed by the exit |
|
361 // frame produced by callVM. |
|
362 inline void saveLive(LInstruction *ins); |
|
363 inline void restoreLive(LInstruction *ins); |
|
364 inline void restoreLiveIgnore(LInstruction *ins, RegisterSet reg); |
|
365 |
|
366 // Save/restore all registers that are both live and volatile. |
|
367 inline void saveLiveVolatile(LInstruction *ins); |
|
368 inline void restoreLiveVolatile(LInstruction *ins); |
|
369 |
|
370 template <typename T> |
|
371 void pushArg(const T &t) { |
|
372 masm.Push(t); |
|
373 #ifdef DEBUG |
|
374 pushedArgs_++; |
|
375 #endif |
|
376 } |
|
377 |
|
378 void storeResultTo(const Register ®) { |
|
379 masm.storeCallResult(reg); |
|
380 } |
|
381 |
|
382 void storeFloatResultTo(const FloatRegister ®) { |
|
383 masm.storeCallFloatResult(reg); |
|
384 } |
|
385 |
|
386 template <typename T> |
|
387 void storeResultValueTo(const T &t) { |
|
388 masm.storeCallResultValue(t); |
|
389 } |
|
390 |
|
391 bool callVM(const VMFunction &f, LInstruction *ins, const Register *dynStack = nullptr); |
|
392 |
|
393 template <class ArgSeq, class StoreOutputTo> |
|
394 inline OutOfLineCode *oolCallVM(const VMFunction &fun, LInstruction *ins, const ArgSeq &args, |
|
395 const StoreOutputTo &out); |
|
396 |
|
397 bool callVM(const VMFunctionsModal &f, LInstruction *ins, const Register *dynStack = nullptr) { |
|
398 return callVM(f[gen->info().executionMode()], ins, dynStack); |
|
399 } |
|
400 |
|
401 template <class ArgSeq, class StoreOutputTo> |
|
402 inline OutOfLineCode *oolCallVM(const VMFunctionsModal &f, LInstruction *ins, |
|
403 const ArgSeq &args, const StoreOutputTo &out) |
|
404 { |
|
405 return oolCallVM(f[gen->info().executionMode()], ins, args, out); |
|
406 } |
|
407 |
|
408 bool addCache(LInstruction *lir, size_t cacheIndex); |
|
409 size_t addCacheLocations(const CacheLocationList &locs, size_t *numLocs); |
|
410 ReciprocalMulConstants computeDivisionConstants(int d); |
|
411 |
|
412 protected: |
|
413 bool addOutOfLineCode(OutOfLineCode *code); |
|
414 bool hasOutOfLineCode() { return !outOfLineCode_.empty(); } |
|
415 bool generateOutOfLineCode(); |
|
416 |
|
417 Label *labelForBackedgeWithImplicitCheck(MBasicBlock *mir); |
|
418 |
|
419 // Generate a jump to the start of the specified block, adding information |
|
420 // if this is a loop backedge. Use this in place of jumping directly to |
|
421 // mir->lir()->label(), or use getJumpLabelForBranch() if a label to use |
|
422 // directly is needed. |
|
423 void jumpToBlock(MBasicBlock *mir); |
|
424 void jumpToBlock(MBasicBlock *mir, Assembler::Condition cond); |
|
425 |
|
426 private: |
|
427 void generateInvalidateEpilogue(); |
|
428 |
|
429 public: |
|
430 CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); |
|
431 |
|
432 public: |
|
433 template <class ArgSeq, class StoreOutputTo> |
|
434 bool visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo> *ool); |
|
435 |
|
436 bool visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool); |
|
437 |
|
438 bool omitOverRecursedCheck() const; |
|
439 |
|
440 public: |
|
441 bool callTraceLIR(uint32_t blockIndex, LInstruction *lir, const char *bailoutName = nullptr); |
|
442 |
|
443 // Parallel aborts: |
|
444 // |
|
445 // Parallel aborts work somewhat differently from sequential |
|
446 // bailouts. When an abort occurs, we first invoke |
|
447 // ReportAbortPar() and then we return JS_ION_ERROR. Each |
|
448 // call on the stack will check for this error return and |
|
449 // propagate it upwards until the C++ code that invoked the ion |
|
450 // code is reached. |
|
451 // |
|
452 // The snapshot that is provided to `oolAbortPar` is currently |
|
453 // only used for error reporting, so that we can provide feedback |
|
454 // to the user about which instruction aborted and (perhaps) why. |
|
455 OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock, |
|
456 jsbytecode *bytecode); |
|
457 OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, LInstruction *lir); |
|
458 OutOfLinePropagateAbortPar *oolPropagateAbortPar(LInstruction *lir); |
|
459 virtual bool visitOutOfLineAbortPar(OutOfLineAbortPar *ool) = 0; |
|
460 virtual bool visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool) = 0; |
|
461 |
|
462 #ifdef JS_TRACE_LOGGING |
|
463 protected: |
|
464 bool emitTracelogScript(bool isStart); |
|
465 bool emitTracelogTree(bool isStart, uint32_t textId); |
|
466 |
|
467 public: |
|
468 bool emitTracelogScriptStart() { |
|
469 return emitTracelogScript(/* isStart =*/ true); |
|
470 } |
|
471 bool emitTracelogScriptStop() { |
|
472 return emitTracelogScript(/* isStart =*/ false); |
|
473 } |
|
474 bool emitTracelogStartEvent(uint32_t textId) { |
|
475 return emitTracelogTree(/* isStart =*/ true, textId); |
|
476 } |
|
477 bool emitTracelogStopEvent(uint32_t textId) { |
|
478 return emitTracelogTree(/* isStart =*/ false, textId); |
|
479 } |
|
480 #endif |
|
481 }; |
|
482 |
|
483 // An out-of-line path is generated at the end of the function. |
|
484 class OutOfLineCode : public TempObject |
|
485 { |
|
486 Label entry_; |
|
487 Label rejoin_; |
|
488 uint32_t framePushed_; |
|
489 jsbytecode *pc_; |
|
490 JSScript *script_; |
|
491 |
|
492 public: |
|
493 OutOfLineCode() |
|
494 : framePushed_(0), |
|
495 pc_(nullptr), |
|
496 script_(nullptr) |
|
497 { } |
|
498 |
|
499 virtual bool generate(CodeGeneratorShared *codegen) = 0; |
|
500 |
|
501 Label *entry() { |
|
502 return &entry_; |
|
503 } |
|
504 virtual void bind(MacroAssembler *masm) { |
|
505 masm->bind(entry()); |
|
506 } |
|
507 Label *rejoin() { |
|
508 return &rejoin_; |
|
509 } |
|
510 void setFramePushed(uint32_t framePushed) { |
|
511 framePushed_ = framePushed; |
|
512 } |
|
513 uint32_t framePushed() const { |
|
514 return framePushed_; |
|
515 } |
|
516 void setSource(JSScript *script, jsbytecode *pc) { |
|
517 script_ = script; |
|
518 pc_ = pc; |
|
519 } |
|
520 jsbytecode *pc() { |
|
521 return pc_; |
|
522 } |
|
523 JSScript *script() { |
|
524 return script_; |
|
525 } |
|
526 }; |
|
527 |
|
528 // For OOL paths that want a specific-typed code generator. |
|
529 template <typename T> |
|
530 class OutOfLineCodeBase : public OutOfLineCode |
|
531 { |
|
532 public: |
|
533 virtual bool generate(CodeGeneratorShared *codegen) { |
|
534 return accept(static_cast<T *>(codegen)); |
|
535 } |
|
536 |
|
537 public: |
|
538 virtual bool accept(T *codegen) = 0; |
|
539 }; |
|
540 |
|
541 // ArgSeq store arguments for OutOfLineCallVM. |
|
542 // |
|
543 // OutOfLineCallVM are created with "oolCallVM" function. The third argument of |
|
544 // this function is an instance of a class which provides a "generate" function |
|
545 // to call the "pushArg" needed by the VMFunction call. The list of argument |
|
546 // can be created by using the ArgList function which create an empty list of |
|
547 // arguments. Arguments are added to this list by using the comma operator. |
|
548 // The type of the argument list is returned by the comma operator, and due to |
|
549 // templates arguments, it is quite painful to write by hand. It is recommended |
|
550 // to use it directly as argument of a template function which would get its |
|
551 // arguments infered by the compiler (such as oolCallVM). The list of arguments |
|
552 // must be written in the same order as if you were calling the function in C++. |
|
553 // |
|
554 // Example: |
|
555 // (ArgList(), ToRegister(lir->lhs()), ToRegister(lir->rhs())) |
|
556 |
|
557 template <class SeqType, typename LastType> |
|
558 class ArgSeq : public SeqType |
|
559 { |
|
560 private: |
|
561 typedef ArgSeq<SeqType, LastType> ThisType; |
|
562 LastType last_; |
|
563 |
|
564 public: |
|
565 ArgSeq(const SeqType &seq, const LastType &last) |
|
566 : SeqType(seq), |
|
567 last_(last) |
|
568 { } |
|
569 |
|
570 template <typename NextType> |
|
571 inline ArgSeq<ThisType, NextType> |
|
572 operator, (const NextType &last) const { |
|
573 return ArgSeq<ThisType, NextType>(*this, last); |
|
574 } |
|
575 |
|
576 inline void generate(CodeGeneratorShared *codegen) const { |
|
577 codegen->pushArg(last_); |
|
578 this->SeqType::generate(codegen); |
|
579 } |
|
580 }; |
|
581 |
|
582 // Mark the end of an argument list. |
|
583 template <> |
|
584 class ArgSeq<void, void> |
|
585 { |
|
586 private: |
|
587 typedef ArgSeq<void, void> ThisType; |
|
588 |
|
589 public: |
|
590 ArgSeq() { } |
|
591 ArgSeq(const ThisType &) { } |
|
592 |
|
593 template <typename NextType> |
|
594 inline ArgSeq<ThisType, NextType> |
|
595 operator, (const NextType &last) const { |
|
596 return ArgSeq<ThisType, NextType>(*this, last); |
|
597 } |
|
598 |
|
599 inline void generate(CodeGeneratorShared *codegen) const { |
|
600 } |
|
601 }; |
|
602 |
|
603 inline ArgSeq<void, void> |
|
604 ArgList() |
|
605 { |
|
606 return ArgSeq<void, void>(); |
|
607 } |
|
608 |
|
609 // Store wrappers, to generate the right move of data after the VM call. |
|
610 |
|
611 struct StoreNothing |
|
612 { |
|
613 inline void generate(CodeGeneratorShared *codegen) const { |
|
614 } |
|
615 inline RegisterSet clobbered() const { |
|
616 return RegisterSet(); // No register gets clobbered |
|
617 } |
|
618 }; |
|
619 |
|
620 class StoreRegisterTo |
|
621 { |
|
622 private: |
|
623 Register out_; |
|
624 |
|
625 public: |
|
626 StoreRegisterTo(const Register &out) |
|
627 : out_(out) |
|
628 { } |
|
629 |
|
630 inline void generate(CodeGeneratorShared *codegen) const { |
|
631 codegen->storeResultTo(out_); |
|
632 } |
|
633 inline RegisterSet clobbered() const { |
|
634 RegisterSet set = RegisterSet(); |
|
635 set.add(out_); |
|
636 return set; |
|
637 } |
|
638 }; |
|
639 |
|
640 class StoreFloatRegisterTo |
|
641 { |
|
642 private: |
|
643 FloatRegister out_; |
|
644 |
|
645 public: |
|
646 StoreFloatRegisterTo(const FloatRegister &out) |
|
647 : out_(out) |
|
648 { } |
|
649 |
|
650 inline void generate(CodeGeneratorShared *codegen) const { |
|
651 codegen->storeFloatResultTo(out_); |
|
652 } |
|
653 inline RegisterSet clobbered() const { |
|
654 RegisterSet set = RegisterSet(); |
|
655 set.add(out_); |
|
656 return set; |
|
657 } |
|
658 }; |
|
659 |
|
660 template <typename Output> |
|
661 class StoreValueTo_ |
|
662 { |
|
663 private: |
|
664 Output out_; |
|
665 |
|
666 public: |
|
667 StoreValueTo_(const Output &out) |
|
668 : out_(out) |
|
669 { } |
|
670 |
|
671 inline void generate(CodeGeneratorShared *codegen) const { |
|
672 codegen->storeResultValueTo(out_); |
|
673 } |
|
674 inline RegisterSet clobbered() const { |
|
675 RegisterSet set = RegisterSet(); |
|
676 set.add(out_); |
|
677 return set; |
|
678 } |
|
679 }; |
|
680 |
|
681 template <typename Output> |
|
682 StoreValueTo_<Output> StoreValueTo(const Output &out) |
|
683 { |
|
684 return StoreValueTo_<Output>(out); |
|
685 } |
|
686 |
|
687 template <class ArgSeq, class StoreOutputTo> |
|
688 class OutOfLineCallVM : public OutOfLineCodeBase<CodeGeneratorShared> |
|
689 { |
|
690 private: |
|
691 LInstruction *lir_; |
|
692 const VMFunction &fun_; |
|
693 ArgSeq args_; |
|
694 StoreOutputTo out_; |
|
695 |
|
696 public: |
|
697 OutOfLineCallVM(LInstruction *lir, const VMFunction &fun, const ArgSeq &args, |
|
698 const StoreOutputTo &out) |
|
699 : lir_(lir), |
|
700 fun_(fun), |
|
701 args_(args), |
|
702 out_(out) |
|
703 { } |
|
704 |
|
705 bool accept(CodeGeneratorShared *codegen) { |
|
706 return codegen->visitOutOfLineCallVM(this); |
|
707 } |
|
708 |
|
709 LInstruction *lir() const { return lir_; } |
|
710 const VMFunction &function() const { return fun_; } |
|
711 const ArgSeq &args() const { return args_; } |
|
712 const StoreOutputTo &out() const { return out_; } |
|
713 }; |
|
714 |
|
715 template <class ArgSeq, class StoreOutputTo> |
|
716 inline OutOfLineCode * |
|
717 CodeGeneratorShared::oolCallVM(const VMFunction &fun, LInstruction *lir, const ArgSeq &args, |
|
718 const StoreOutputTo &out) |
|
719 { |
|
720 OutOfLineCode *ool = new(alloc()) OutOfLineCallVM<ArgSeq, StoreOutputTo>(lir, fun, args, out); |
|
721 if (!addOutOfLineCode(ool)) |
|
722 return nullptr; |
|
723 return ool; |
|
724 } |
|
725 |
|
726 template <class ArgSeq, class StoreOutputTo> |
|
727 bool |
|
728 CodeGeneratorShared::visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo> *ool) |
|
729 { |
|
730 LInstruction *lir = ool->lir(); |
|
731 |
|
732 saveLive(lir); |
|
733 ool->args().generate(this); |
|
734 if (!callVM(ool->function(), lir)) |
|
735 return false; |
|
736 ool->out().generate(this); |
|
737 restoreLiveIgnore(lir, ool->out().clobbered()); |
|
738 masm.jump(ool->rejoin()); |
|
739 return true; |
|
740 } |
|
741 |
|
742 // Initiate a parallel abort. The snapshot is used to record the |
|
743 // cause. |
|
744 class OutOfLineAbortPar : public OutOfLineCode |
|
745 { |
|
746 private: |
|
747 ParallelBailoutCause cause_; |
|
748 MBasicBlock *basicBlock_; |
|
749 jsbytecode *bytecode_; |
|
750 |
|
751 public: |
|
752 OutOfLineAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock, jsbytecode *bytecode) |
|
753 : cause_(cause), |
|
754 basicBlock_(basicBlock), |
|
755 bytecode_(bytecode) |
|
756 { } |
|
757 |
|
758 ParallelBailoutCause cause() { |
|
759 return cause_; |
|
760 } |
|
761 |
|
762 MBasicBlock *basicBlock() { |
|
763 return basicBlock_; |
|
764 } |
|
765 |
|
766 jsbytecode *bytecode() { |
|
767 return bytecode_; |
|
768 } |
|
769 |
|
770 bool generate(CodeGeneratorShared *codegen); |
|
771 }; |
|
772 |
|
773 // Used when some callee has aborted. |
|
774 class OutOfLinePropagateAbortPar : public OutOfLineCode |
|
775 { |
|
776 private: |
|
777 LInstruction *lir_; |
|
778 |
|
779 public: |
|
780 OutOfLinePropagateAbortPar(LInstruction *lir) |
|
781 : lir_(lir) |
|
782 { } |
|
783 |
|
784 LInstruction *lir() { return lir_; } |
|
785 |
|
786 bool generate(CodeGeneratorShared *codegen); |
|
787 }; |
|
788 |
|
789 extern const VMFunction InterruptCheckInfo; |
|
790 |
|
791 } // namespace jit |
|
792 } // namespace js |
|
793 |
|
794 #endif /* jit_shared_CodeGenerator_shared_h */ |