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_CacheIRCompiler_h
8 : #define jit_CacheIRCompiler_h
9 :
10 : #include "jit/CacheIR.h"
11 :
12 : namespace js {
13 : namespace jit {
14 :
15 : // The ops below are defined in CacheIRCompiler and codegen is shared between
16 : // BaselineCacheIRCompiler and IonCacheIRCompiler.
17 : #define CACHE_IR_SHARED_OPS(_) \
18 : _(GuardIsObject) \
19 : _(GuardIsObjectOrNull) \
20 : _(GuardIsString) \
21 : _(GuardIsSymbol) \
22 : _(GuardIsInt32Index) \
23 : _(GuardType) \
24 : _(GuardClass) \
25 : _(GuardIsNativeFunction) \
26 : _(GuardIsProxy) \
27 : _(GuardIsCrossCompartmentWrapper) \
28 : _(GuardNotDOMProxy) \
29 : _(GuardSpecificInt32Immediate) \
30 : _(GuardMagicValue) \
31 : _(GuardNoUnboxedExpando) \
32 : _(GuardAndLoadUnboxedExpando) \
33 : _(GuardNoDetachedTypedObjects) \
34 : _(GuardNoDenseElements) \
35 : _(GuardAndGetIndexFromString) \
36 : _(LoadProto) \
37 : _(LoadEnclosingEnvironment) \
38 : _(LoadWrapperTarget) \
39 : _(LoadDOMExpandoValue) \
40 : _(LoadDOMExpandoValueIgnoreGeneration)\
41 : _(LoadUndefinedResult) \
42 : _(LoadBooleanResult) \
43 : _(LoadInt32ArrayLengthResult) \
44 : _(LoadUnboxedArrayLengthResult) \
45 : _(LoadArgumentsObjectLengthResult) \
46 : _(LoadFunctionLengthResult) \
47 : _(LoadStringLengthResult) \
48 : _(LoadStringCharResult) \
49 : _(LoadArgumentsObjectArgResult) \
50 : _(LoadDenseElementResult) \
51 : _(LoadDenseElementHoleResult) \
52 : _(LoadDenseElementExistsResult) \
53 : _(LoadDenseElementHoleExistsResult) \
54 : _(LoadUnboxedArrayElementResult) \
55 : _(LoadTypedElementResult) \
56 : _(LoadObjectResult) \
57 : _(LoadTypeOfObjectResult) \
58 : _(CompareStringResult) \
59 : _(CompareObjectResult) \
60 : _(CompareSymbolResult) \
61 : _(CallPrintString) \
62 : _(Breakpoint) \
63 : _(MegamorphicLoadSlotByValueResult) \
64 : _(MegamorphicHasOwnResult) \
65 : _(WrapResult)
66 :
67 : // Represents a Value on the Baseline frame's expression stack. Slot 0 is the
68 : // value on top of the stack (the most recently pushed value), slot 1 is the
69 : // value pushed before that, etc.
70 : class BaselineFrameSlot
71 : {
72 : uint32_t slot_;
73 :
74 : public:
75 27 : explicit BaselineFrameSlot(uint32_t slot) : slot_(slot) {}
76 27 : uint32_t slot() const { return slot_; }
77 :
78 142 : bool operator==(const BaselineFrameSlot& other) const { return slot_ == other.slot_; }
79 : bool operator!=(const BaselineFrameSlot& other) const { return slot_ != other.slot_; }
80 : };
81 :
82 : // OperandLocation represents the location of an OperandId. The operand is
83 : // either in a register or on the stack, and is either boxed or unboxed.
84 : class OperandLocation
85 : {
86 : public:
87 : enum Kind {
88 : Uninitialized = 0,
89 : PayloadReg,
90 : DoubleReg,
91 : ValueReg,
92 : PayloadStack,
93 : ValueStack,
94 : BaselineFrame,
95 : Constant,
96 : };
97 :
98 : private:
99 : Kind kind_;
100 :
101 : union Data {
102 : struct {
103 : Register reg;
104 : JSValueType type;
105 : } payloadReg;
106 : FloatRegister doubleReg;
107 : ValueOperand valueReg;
108 : struct {
109 : uint32_t stackPushed;
110 : JSValueType type;
111 : } payloadStack;
112 : uint32_t valueStackPushed;
113 : BaselineFrameSlot baselineFrameSlot;
114 : Value constant;
115 :
116 1076 : Data() : valueStackPushed(0) {}
117 : };
118 : Data data_;
119 :
120 : public:
121 1076 : OperandLocation() : kind_(Uninitialized) {}
122 :
123 5489 : Kind kind() const { return kind_; }
124 :
125 2565 : void setUninitialized() {
126 2565 : kind_ = Uninitialized;
127 2565 : }
128 :
129 3784 : ValueOperand valueReg() const {
130 3784 : MOZ_ASSERT(kind_ == ValueReg);
131 3784 : return data_.valueReg;
132 : }
133 3465 : Register payloadReg() const {
134 3465 : MOZ_ASSERT(kind_ == PayloadReg);
135 3465 : return data_.payloadReg.reg;
136 : }
137 0 : FloatRegister doubleReg() const {
138 0 : MOZ_ASSERT(kind_ == DoubleReg);
139 0 : return data_.doubleReg;
140 : }
141 58 : uint32_t payloadStack() const {
142 58 : MOZ_ASSERT(kind_ == PayloadStack);
143 58 : return data_.payloadStack.stackPushed;
144 : }
145 7 : uint32_t valueStack() const {
146 7 : MOZ_ASSERT(kind_ == ValueStack);
147 7 : return data_.valueStackPushed;
148 : }
149 1689 : JSValueType payloadType() const {
150 1689 : if (kind_ == PayloadReg)
151 1642 : return data_.payloadReg.type;
152 47 : MOZ_ASSERT(kind_ == PayloadStack);
153 47 : return data_.payloadStack.type;
154 : }
155 0 : Value constant() const {
156 0 : MOZ_ASSERT(kind_ == Constant);
157 0 : return data_.constant;
158 : }
159 308 : BaselineFrameSlot baselineFrameSlot() const {
160 308 : MOZ_ASSERT(kind_ == BaselineFrame);
161 308 : return data_.baselineFrameSlot;
162 : }
163 :
164 746 : void setPayloadReg(Register reg, JSValueType type) {
165 746 : kind_ = PayloadReg;
166 746 : data_.payloadReg.reg = reg;
167 746 : data_.payloadReg.type = type;
168 746 : }
169 0 : void setDoubleReg(FloatRegister reg) {
170 0 : kind_ = DoubleReg;
171 0 : data_.doubleReg = reg;
172 0 : }
173 920 : void setValueReg(ValueOperand reg) {
174 920 : kind_ = ValueReg;
175 920 : data_.valueReg = reg;
176 920 : }
177 17 : void setPayloadStack(uint32_t stackPushed, JSValueType type) {
178 17 : kind_ = PayloadStack;
179 17 : data_.payloadStack.stackPushed = stackPushed;
180 17 : data_.payloadStack.type = type;
181 17 : }
182 18 : void setValueStack(uint32_t stackPushed) {
183 18 : kind_ = ValueStack;
184 18 : data_.valueStackPushed = stackPushed;
185 18 : }
186 0 : void setConstant(const Value& v) {
187 0 : kind_ = Constant;
188 0 : data_.constant = v;
189 0 : }
190 48 : void setBaselineFrame(BaselineFrameSlot slot) {
191 48 : kind_ = BaselineFrame;
192 48 : data_.baselineFrameSlot = slot;
193 48 : }
194 :
195 16 : bool isInRegister() const { return kind_ == PayloadReg || kind_ == ValueReg; }
196 0 : bool isOnStack() const { return kind_ == PayloadStack || kind_ == ValueStack; }
197 :
198 0 : size_t stackPushed() const {
199 0 : if (kind_ == PayloadStack)
200 0 : return data_.payloadStack.stackPushed;
201 0 : MOZ_ASSERT(kind_ == ValueStack);
202 0 : return data_.valueStackPushed;
203 : }
204 0 : size_t stackSizeInBytes() const {
205 0 : if (kind_ == PayloadStack)
206 0 : return sizeof(uintptr_t);
207 0 : MOZ_ASSERT(kind_ == ValueStack);
208 0 : return sizeof(js::Value);
209 : }
210 0 : void adjustStackPushed(int32_t diff) {
211 0 : if (kind_ == PayloadStack) {
212 0 : data_.payloadStack.stackPushed += diff;
213 0 : return;
214 : }
215 0 : MOZ_ASSERT(kind_ == ValueStack);
216 0 : data_.valueStackPushed += diff;
217 : }
218 :
219 200 : bool aliasesReg(Register reg) const {
220 200 : if (kind_ == PayloadReg)
221 18 : return payloadReg() == reg;
222 182 : if (kind_ == ValueReg)
223 182 : return valueReg().aliases(reg);
224 0 : return false;
225 : }
226 106 : bool aliasesReg(ValueOperand reg) const {
227 : #if defined(JS_NUNBOX32)
228 : return aliasesReg(reg.typeReg()) || aliasesReg(reg.payloadReg());
229 : #else
230 106 : return aliasesReg(reg.valueReg());
231 : #endif
232 : }
233 :
234 : bool aliasesReg(const OperandLocation& other) const;
235 :
236 : bool operator==(const OperandLocation& other) const;
237 1096 : bool operator!=(const OperandLocation& other) const { return !operator==(other); }
238 : };
239 :
240 : struct SpilledRegister
241 : {
242 : Register reg;
243 : uint32_t stackPushed;
244 :
245 1 : SpilledRegister(Register reg, uint32_t stackPushed)
246 1 : : reg(reg), stackPushed(stackPushed)
247 1 : {}
248 0 : bool operator==(const SpilledRegister& other) const {
249 0 : return reg == other.reg && stackPushed == other.stackPushed;
250 : }
251 0 : bool operator!=(const SpilledRegister& other) const { return !(*this == other); }
252 : };
253 :
254 : using SpilledRegisterVector = Vector<SpilledRegister, 2, SystemAllocPolicy>;
255 :
256 : // Class to track and allocate registers while emitting IC code.
257 266 : class MOZ_RAII CacheRegisterAllocator
258 : {
259 : // The original location of the inputs to the cache.
260 : Vector<OperandLocation, 4, SystemAllocPolicy> origInputLocations_;
261 :
262 : // The current location of each operand.
263 : Vector<OperandLocation, 8, SystemAllocPolicy> operandLocations_;
264 :
265 : // Free lists for value- and payload-slots on stack
266 : Vector<uint32_t, 2, SystemAllocPolicy> freeValueSlots_;
267 : Vector<uint32_t, 2, SystemAllocPolicy> freePayloadSlots_;
268 :
269 : // The registers allocated while emitting the current CacheIR op.
270 : // This prevents us from allocating a register and then immediately
271 : // clobbering it for something else, while we're still holding on to it.
272 : LiveGeneralRegisterSet currentOpRegs_;
273 :
274 : const AllocatableGeneralRegisterSet allocatableRegs_;
275 :
276 : // Registers that are currently unused and available.
277 : AllocatableGeneralRegisterSet availableRegs_;
278 :
279 : // Registers that are available, but before use they must be saved and
280 : // then restored when returning from the stub.
281 : AllocatableGeneralRegisterSet availableRegsAfterSpill_;
282 :
283 : // Registers we took from availableRegsAfterSpill_ and spilled to the stack.
284 : SpilledRegisterVector spilledRegs_;
285 :
286 : // The number of bytes pushed on the native stack.
287 : uint32_t stackPushed_;
288 :
289 : // The index of the CacheIR instruction we're currently emitting.
290 : uint32_t currentInstruction_;
291 :
292 : const CacheIRWriter& writer_;
293 :
294 : CacheRegisterAllocator(const CacheRegisterAllocator&) = delete;
295 : CacheRegisterAllocator& operator=(const CacheRegisterAllocator&) = delete;
296 :
297 : void freeDeadOperandLocations(MacroAssembler& masm);
298 :
299 : void spillOperandToStack(MacroAssembler& masm, OperandLocation* loc);
300 : void spillOperandToStackOrRegister(MacroAssembler& masm, OperandLocation* loc);
301 :
302 : void popPayload(MacroAssembler& masm, OperandLocation* loc, Register dest);
303 : void popValue(MacroAssembler& masm, OperandLocation* loc, ValueOperand dest);
304 :
305 : public:
306 : friend class AutoScratchRegister;
307 : friend class AutoScratchRegisterExcluding;
308 :
309 266 : explicit CacheRegisterAllocator(const CacheIRWriter& writer)
310 798 : : allocatableRegs_(GeneralRegisterSet::All()),
311 : stackPushed_(0),
312 : currentInstruction_(0),
313 798 : writer_(writer)
314 266 : {}
315 :
316 : MOZ_MUST_USE bool init();
317 :
318 266 : void initAvailableRegs(const AllocatableGeneralRegisterSet& available) {
319 266 : availableRegs_ = available;
320 266 : }
321 : void initAvailableRegsAfterSpill();
322 :
323 : void fixupAliasedInputs(MacroAssembler& masm);
324 :
325 1772 : OperandLocation operandLocation(size_t i) const {
326 1772 : return operandLocations_[i];
327 : }
328 1036 : void setOperandLocation(size_t i, const OperandLocation& loc) {
329 1036 : operandLocations_[i] = loc;
330 1036 : }
331 :
332 : OperandLocation origInputLocation(size_t i) const {
333 : return origInputLocations_[i];
334 : }
335 374 : void initInputLocation(size_t i, ValueOperand reg) {
336 374 : origInputLocations_[i].setValueReg(reg);
337 374 : operandLocations_[i].setValueReg(reg);
338 374 : }
339 47 : void initInputLocation(size_t i, Register reg, JSValueType type) {
340 47 : origInputLocations_[i].setPayloadReg(reg, type);
341 47 : operandLocations_[i].setPayloadReg(reg, type);
342 47 : }
343 0 : void initInputLocation(size_t i, FloatRegister reg) {
344 0 : origInputLocations_[i].setDoubleReg(reg);
345 0 : operandLocations_[i].setDoubleReg(reg);
346 0 : }
347 0 : void initInputLocation(size_t i, const Value& v) {
348 0 : origInputLocations_[i].setConstant(v);
349 0 : operandLocations_[i].setConstant(v);
350 0 : }
351 24 : void initInputLocation(size_t i, BaselineFrameSlot slot) {
352 24 : origInputLocations_[i].setBaselineFrame(slot);
353 24 : operandLocations_[i].setBaselineFrame(slot);
354 24 : }
355 :
356 : void initInputLocation(size_t i, const TypedOrValueRegister& reg);
357 : void initInputLocation(size_t i, const ConstantOrRegister& value);
358 :
359 967 : const SpilledRegisterVector& spilledRegs() const { return spilledRegs_; }
360 :
361 579 : MOZ_MUST_USE bool setSpilledRegs(const SpilledRegisterVector& regs) {
362 579 : spilledRegs_.clear();
363 579 : return spilledRegs_.appendAll(regs);
364 : }
365 :
366 1609 : void nextOp() {
367 1609 : currentOpRegs_.clear();
368 1609 : currentInstruction_++;
369 1609 : }
370 :
371 1061 : uint32_t stackPushed() const {
372 1061 : return stackPushed_;
373 : }
374 579 : void setStackPushed(uint32_t pushed) {
375 579 : stackPushed_ = pushed;
376 579 : }
377 :
378 : bool isAllocatable(Register reg) const {
379 : return allocatableRegs_.has(reg);
380 : }
381 :
382 : // Allocates a new register.
383 : Register allocateRegister(MacroAssembler& masm);
384 : ValueOperand allocateValueRegister(MacroAssembler& masm);
385 :
386 : void allocateFixedRegister(MacroAssembler& masm, Register reg);
387 : void allocateFixedValueRegister(MacroAssembler& masm, ValueOperand reg);
388 :
389 : // Releases a register so it can be reused later.
390 901 : void releaseRegister(Register reg) {
391 901 : MOZ_ASSERT(currentOpRegs_.has(reg));
392 901 : availableRegs_.add(reg);
393 901 : currentOpRegs_.take(reg);
394 901 : }
395 178 : void releaseValueRegister(ValueOperand reg) {
396 : #ifdef JS_NUNBOX32
397 : releaseRegister(reg.payloadReg());
398 : releaseRegister(reg.typeReg());
399 : #else
400 178 : releaseRegister(reg.valueReg());
401 : #endif
402 178 : }
403 :
404 : // Removes spilled values from the native stack. This should only be
405 : // called after all registers have been allocated.
406 : void discardStack(MacroAssembler& masm);
407 :
408 : Address addressOf(MacroAssembler& masm, BaselineFrameSlot slot) const;
409 :
410 : // Returns the register for the given operand. If the operand is currently
411 : // not in a register, it will load it into one.
412 : ValueOperand useValueRegister(MacroAssembler& masm, ValOperandId val);
413 : ValueOperand useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg);
414 : Register useRegister(MacroAssembler& masm, TypedOperandId typedId);
415 :
416 : ConstantOrRegister useConstantOrRegister(MacroAssembler& masm, ValOperandId val);
417 :
418 : // Allocates an output register for the given operand.
419 : Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
420 : ValueOperand defineValueRegister(MacroAssembler& masm, ValOperandId val);
421 :
422 : // Returns |val|'s JSValueType or JSVAL_TYPE_UNKNOWN.
423 : JSValueType knownType(ValOperandId val) const;
424 :
425 : // Emits code to restore registers and stack to the state at the start of
426 : // the stub.
427 : void restoreInputState(MacroAssembler& masm, bool discardStack = true);
428 :
429 : // Returns the set of registers storing the IC input operands.
430 : GeneralRegisterSet inputRegisterSet() const;
431 :
432 : void saveIonLiveRegisters(MacroAssembler& masm, LiveRegisterSet liveRegs,
433 : Register scratch, IonScript* ionScript);
434 : void restoreIonLiveRegisters(MacroAssembler& masm, LiveRegisterSet liveRegs);
435 : };
436 :
437 : // RAII class to allocate a scratch register and release it when we're done
438 : // with it.
439 : class MOZ_RAII AutoScratchRegister
440 : {
441 : CacheRegisterAllocator& alloc_;
442 : Register reg_;
443 :
444 : AutoScratchRegister(const AutoScratchRegister&) = delete;
445 : void operator=(const AutoScratchRegister&) = delete;
446 :
447 : public:
448 713 : AutoScratchRegister(CacheRegisterAllocator& alloc, MacroAssembler& masm,
449 : Register reg = InvalidReg)
450 713 : : alloc_(alloc)
451 : {
452 713 : if (reg != InvalidReg) {
453 58 : alloc.allocateFixedRegister(masm, reg);
454 58 : reg_ = reg;
455 : } else {
456 655 : reg_ = alloc.allocateRegister(masm);
457 : }
458 713 : MOZ_ASSERT(alloc_.currentOpRegs_.has(reg_));
459 713 : }
460 1426 : ~AutoScratchRegister() {
461 713 : alloc_.releaseRegister(reg_);
462 713 : }
463 :
464 4 : Register get() const { return reg_; }
465 2072 : operator Register() const { return reg_; }
466 : };
467 :
468 : // Like AutoScratchRegister, but lets the caller specify a register that should
469 : // not be allocated here.
470 : class MOZ_RAII AutoScratchRegisterExcluding
471 : {
472 : CacheRegisterAllocator& alloc_;
473 : Register reg_;
474 :
475 : public:
476 9 : AutoScratchRegisterExcluding(CacheRegisterAllocator& alloc, MacroAssembler& masm,
477 : Register excluding)
478 9 : : alloc_(alloc)
479 : {
480 9 : MOZ_ASSERT(excluding != InvalidReg);
481 :
482 9 : reg_ = alloc.allocateRegister(masm);
483 :
484 9 : if (reg_ == excluding) {
485 : // We need a different register, so try again.
486 0 : reg_ = alloc.allocateRegister(masm);
487 0 : MOZ_ASSERT(reg_ != excluding);
488 0 : alloc_.releaseRegister(excluding);
489 : }
490 :
491 9 : MOZ_ASSERT(alloc_.currentOpRegs_.has(reg_));
492 9 : }
493 18 : ~AutoScratchRegisterExcluding() {
494 9 : alloc_.releaseRegister(reg_);
495 9 : }
496 78 : operator Register() const { return reg_; }
497 : };
498 :
499 : // The FailurePath class stores everything we need to generate a failure path
500 : // at the end of the IC code. The failure path restores the input registers, if
501 : // needed, and jumps to the next stub.
502 1546 : class FailurePath
503 : {
504 : Vector<OperandLocation, 4, SystemAllocPolicy> inputs_;
505 : SpilledRegisterVector spilledRegs_;
506 : NonAssertingLabel label_;
507 : uint32_t stackPushed_;
508 :
509 : public:
510 967 : FailurePath() = default;
511 :
512 579 : FailurePath(FailurePath&& other)
513 579 : : inputs_(Move(other.inputs_)),
514 579 : spilledRegs_(Move(other.spilledRegs_)),
515 : label_(other.label_),
516 1158 : stackPushed_(other.stackPushed_)
517 579 : {}
518 :
519 1695 : Label* label() { return &label_; }
520 :
521 967 : void setStackPushed(uint32_t i) { stackPushed_ = i; }
522 579 : uint32_t stackPushed() const { return stackPushed_; }
523 :
524 1772 : MOZ_MUST_USE bool appendInput(const OperandLocation& loc) {
525 1772 : return inputs_.append(loc);
526 : }
527 1036 : OperandLocation input(size_t i) const {
528 1036 : return inputs_[i];
529 : }
530 :
531 579 : const SpilledRegisterVector& spilledRegs() const { return spilledRegs_; }
532 :
533 967 : MOZ_MUST_USE bool setSpilledRegs(const SpilledRegisterVector& regs) {
534 967 : MOZ_ASSERT(spilledRegs_.empty());
535 967 : return spilledRegs_.appendAll(regs);
536 : }
537 :
538 : // If canShareFailurePath(other) returns true, the same machine code will
539 : // be emitted for two failure paths, so we can share them.
540 : bool canShareFailurePath(const FailurePath& other) const;
541 : };
542 :
543 : class AutoOutputRegister;
544 :
545 : // Base class for BaselineCacheIRCompiler and IonCacheIRCompiler.
546 266 : class MOZ_RAII CacheIRCompiler
547 : {
548 : protected:
549 : friend class AutoOutputRegister;
550 :
551 : enum class Mode { Baseline, Ion };
552 :
553 : JSContext* cx_;
554 : CacheIRReader reader;
555 : const CacheIRWriter& writer_;
556 : MacroAssembler masm;
557 :
558 : CacheRegisterAllocator allocator;
559 : Vector<FailurePath, 4, SystemAllocPolicy> failurePaths;
560 :
561 : // Float registers that are live. Registers not in this set can be
562 : // clobbered and don't need to be saved before performing a VM call.
563 : // Doing this for non-float registers is a bit more complicated because
564 : // the IC register allocator allocates GPRs.
565 : LiveFloatRegisterSet liveFloatRegs_;
566 :
567 : Maybe<TypedOrValueRegister> outputUnchecked_;
568 : Mode mode_;
569 :
570 : // Whether this IC may read double values from uint32 arrays.
571 : Maybe<bool> allowDoubleResult_;
572 :
573 266 : CacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, Mode mode)
574 266 : : cx_(cx),
575 : reader(writer),
576 : writer_(writer),
577 : allocator(writer_),
578 532 : liveFloatRegs_(FloatRegisterSet::All()),
579 798 : mode_(mode)
580 : {
581 266 : MOZ_ASSERT(!writer.failed());
582 266 : }
583 :
584 : MOZ_MUST_USE bool addFailurePath(FailurePath** failure);
585 : MOZ_MUST_USE bool emitFailurePath(size_t i);
586 :
587 : // Returns the set of volatile float registers that are live. These
588 : // registers need to be saved when making non-GC calls with callWithABI.
589 139 : FloatRegisterSet liveVolatileFloatRegs() const {
590 139 : return FloatRegisterSet::Intersect(liveFloatRegs_.set(), FloatRegisterSet::Volatile());
591 : }
592 :
593 : void emitLoadTypedObjectResultShared(const Address& fieldAddr, Register scratch,
594 : TypedThingLayout layout, uint32_t typeDescr,
595 : const AutoOutputRegister& output);
596 :
597 : void emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
598 : const Address& dest, Register scratch);
599 :
600 : private:
601 : void emitPostBarrierShared(Register obj, const ConstantOrRegister& val, Register scratch,
602 : Register maybeIndex);
603 :
604 52 : void emitPostBarrierShared(Register obj, ValueOperand val, Register scratch,
605 : Register maybeIndex) {
606 52 : emitPostBarrierShared(obj, ConstantOrRegister(val), scratch, maybeIndex);
607 52 : }
608 :
609 : protected:
610 : template <typename T>
611 45 : void emitPostBarrierSlot(Register obj, const T& val, Register scratch) {
612 45 : emitPostBarrierShared(obj, val, scratch, InvalidReg);
613 45 : }
614 :
615 : template <typename T>
616 7 : void emitPostBarrierElement(Register obj, const T& val, Register scratch, Register index) {
617 7 : MOZ_ASSERT(index != InvalidReg);
618 7 : emitPostBarrierShared(obj, val, scratch, index);
619 7 : }
620 :
621 : bool emitComparePointerResultShared(bool symbol);
622 :
623 : #define DEFINE_SHARED_OP(op) MOZ_MUST_USE bool emit##op();
624 : CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
625 : #undef DEFINE_SHARED_OP
626 : };
627 :
628 : // Ensures the IC's output register is available for writing.
629 : class MOZ_RAII AutoOutputRegister
630 : {
631 : TypedOrValueRegister output_;
632 : CacheRegisterAllocator& alloc_;
633 :
634 : AutoOutputRegister(const AutoOutputRegister&) = delete;
635 : void operator=(const AutoOutputRegister&) = delete;
636 :
637 : public:
638 : explicit AutoOutputRegister(CacheIRCompiler& compiler);
639 : ~AutoOutputRegister();
640 :
641 138 : Register maybeReg() const {
642 138 : if (output_.hasValue())
643 137 : return output_.valueReg().scratchReg();
644 1 : if (!output_.typedReg().isFloat())
645 1 : return output_.typedReg().gpr();
646 0 : return InvalidReg;
647 : }
648 :
649 48 : bool hasValue() const { return output_.hasValue(); }
650 210 : ValueOperand valueReg() const { return output_.valueReg(); }
651 1 : AnyRegister typedReg() const { return output_.typedReg(); }
652 :
653 1 : JSValueType type() const {
654 1 : MOZ_ASSERT(!hasValue());
655 1 : return ValueTypeFromMIRType(output_.type());
656 : }
657 :
658 24 : operator TypedOrValueRegister() const { return output_; }
659 : };
660 :
661 : // Like AutoScratchRegister, but reuse a register of |output| if possible.
662 138 : class MOZ_RAII AutoScratchRegisterMaybeOutput
663 : {
664 : mozilla::Maybe<AutoScratchRegister> scratch_;
665 : Register scratchReg_;
666 :
667 : AutoScratchRegisterMaybeOutput(const AutoScratchRegisterMaybeOutput&) = delete;
668 : void operator=(const AutoScratchRegisterMaybeOutput&) = delete;
669 :
670 : public:
671 138 : AutoScratchRegisterMaybeOutput(CacheRegisterAllocator& alloc, MacroAssembler& masm,
672 : const AutoOutputRegister& output)
673 138 : {
674 138 : scratchReg_ = output.maybeReg();
675 138 : if (scratchReg_ == InvalidReg) {
676 0 : scratch_.emplace(alloc, masm);
677 0 : scratchReg_ = scratch_.ref();
678 : }
679 138 : }
680 :
681 372 : operator Register() const { return scratchReg_; }
682 : };
683 :
684 : // See the 'Sharing Baseline stub code' comment in CacheIR.h for a description
685 : // of this class.
686 : class CacheIRStubInfo
687 : {
688 : // These fields don't require 8 bits, but GCC complains if these fields are
689 : // smaller than the size of the enums.
690 : CacheKind kind_ : 8;
691 : ICStubEngine engine_ : 8;
692 : bool makesGCCalls_ : 1;
693 : uint8_t stubDataOffset_;
694 :
695 : const uint8_t* code_;
696 : uint32_t length_;
697 : const uint8_t* fieldTypes_;
698 :
699 251 : CacheIRStubInfo(CacheKind kind, ICStubEngine engine, bool makesGCCalls,
700 : uint32_t stubDataOffset, const uint8_t* code, uint32_t codeLength,
701 : const uint8_t* fieldTypes)
702 251 : : kind_(kind),
703 : engine_(engine),
704 : makesGCCalls_(makesGCCalls),
705 : stubDataOffset_(stubDataOffset),
706 : code_(code),
707 : length_(codeLength),
708 251 : fieldTypes_(fieldTypes)
709 : {
710 251 : MOZ_ASSERT(kind_ == kind, "Kind must fit in bitfield");
711 251 : MOZ_ASSERT(engine_ == engine, "Engine must fit in bitfield");
712 251 : MOZ_ASSERT(stubDataOffset_ == stubDataOffset, "stubDataOffset must fit in uint8_t");
713 251 : }
714 :
715 : CacheIRStubInfo(const CacheIRStubInfo&) = delete;
716 : CacheIRStubInfo& operator=(const CacheIRStubInfo&) = delete;
717 :
718 : public:
719 6131 : CacheKind kind() const { return kind_; }
720 6131 : ICStubEngine engine() const { return engine_; }
721 7990 : bool makesGCCalls() const { return makesGCCalls_; }
722 :
723 6419 : const uint8_t* code() const { return code_; }
724 6275 : uint32_t codeLength() const { return length_; }
725 15372 : uint32_t stubDataOffset() const { return stubDataOffset_; }
726 :
727 : size_t stubDataSize() const;
728 :
729 47743 : StubField::Type fieldType(uint32_t i) const { return (StubField::Type)fieldTypes_[i]; }
730 :
731 : static CacheIRStubInfo* New(CacheKind kind, ICStubEngine engine, bool canMakeCalls,
732 : uint32_t stubDataOffset, const CacheIRWriter& writer);
733 :
734 : template <class Stub, class T>
735 : js::GCPtr<T>& getStubField(Stub* stub, uint32_t field) const;
736 :
737 : template <class T>
738 59 : js::GCPtr<T>& getStubField(ICStub* stub, uint32_t field) const {
739 59 : return getStubField<ICStub, T>(stub, field);
740 : }
741 :
742 : void copyStubData(ICStub* src, ICStub* dest) const;
743 : };
744 :
745 : template <typename T>
746 : void TraceCacheIRStub(JSTracer* trc, T* stub, const CacheIRStubInfo* stubInfo);
747 :
748 : } // namespace jit
749 : } // namespace js
750 :
751 : #endif /* jit_CacheIRCompiler_h */
|