1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_COMPILER_INSTRUCTION_H_
6 #define V8_COMPILER_INSTRUCTION_H_
7
8 #include <deque>
9 #include <iosfwd>
10 #include <map>
11 #include <set>
12
13 #include "src/base/compiler-specific.h"
14 #include "src/compiler/common-operator.h"
15 #include "src/compiler/frame.h"
16 #include "src/compiler/instruction-codes.h"
17 #include "src/compiler/opcodes.h"
18 #include "src/double.h"
19 #include "src/globals.h"
20 #include "src/macro-assembler.h"
21 #include "src/register-configuration.h"
22 #include "src/source-position.h"
23 #include "src/zone/zone-allocator.h"
24
25 namespace v8 {
26 namespace internal {
27 namespace compiler {
28
29 class Schedule;
30 class SourcePositionTable;
31
32 class V8_EXPORT_PRIVATE InstructionOperand {
33 public:
34 static const int kInvalidVirtualRegister = -1;
35
36 enum Kind {
37 INVALID,
38 UNALLOCATED,
39 CONSTANT,
40 IMMEDIATE,
41 // Location operand kinds.
42 EXPLICIT,
43 ALLOCATED,
44 FIRST_LOCATION_OPERAND_KIND = EXPLICIT
45 // Location operand kinds must be last.
46 };
47
InstructionOperand()48 InstructionOperand() : InstructionOperand(INVALID) {}
49
kind()50 Kind kind() const { return KindField::decode(value_); }
51
52 #define INSTRUCTION_OPERAND_PREDICATE(name, type) \
53 bool Is##name() const { return kind() == type; }
54 INSTRUCTION_OPERAND_PREDICATE(Invalid, INVALID)
55 // UnallocatedOperands are place-holder operands created before register
56 // allocation. They later are assigned registers and become AllocatedOperands.
57 INSTRUCTION_OPERAND_PREDICATE(Unallocated, UNALLOCATED)
58 // Constant operands participate in register allocation. They are allocated to
59 // registers but have a special "spilling" behavior. When a ConstantOperand
60 // value must be rematerialized, it is loaded from an immediate constant
61 // rather from an unspilled slot.
62 INSTRUCTION_OPERAND_PREDICATE(Constant, CONSTANT)
63 // ImmediateOperands do not participate in register allocation and are only
64 // embedded directly in instructions, e.g. small integers and on some
65 // platforms Objects.
66 INSTRUCTION_OPERAND_PREDICATE(Immediate, IMMEDIATE)
67 // ExplicitOperands do not participate in register allocation. They are
68 // created by the instruction selector for direct access to registers and
69 // stack slots, completely bypassing the register allocator. They are never
70 // associated with a virtual register
71 INSTRUCTION_OPERAND_PREDICATE(Explicit, EXPLICIT)
72 // AllocatedOperands are registers or stack slots that are assigned by the
73 // register allocator and are always associated with a virtual register.
74 INSTRUCTION_OPERAND_PREDICATE(Allocated, ALLOCATED)
75 #undef INSTRUCTION_OPERAND_PREDICATE
76
77 inline bool IsAnyLocationOperand() const;
78 inline bool IsLocationOperand() const;
79 inline bool IsFPLocationOperand() const;
80 inline bool IsAnyRegister() const;
81 inline bool IsRegister() const;
82 inline bool IsFPRegister() const;
83 inline bool IsFloatRegister() const;
84 inline bool IsDoubleRegister() const;
85 inline bool IsSimd128Register() const;
86 inline bool IsAnyStackSlot() const;
87 inline bool IsStackSlot() const;
88 inline bool IsFPStackSlot() const;
89 inline bool IsFloatStackSlot() const;
90 inline bool IsDoubleStackSlot() const;
91 inline bool IsSimd128StackSlot() const;
92
93 template <typename SubKindOperand>
New(Zone * zone,const SubKindOperand & op)94 static SubKindOperand* New(Zone* zone, const SubKindOperand& op) {
95 void* buffer = zone->New(sizeof(op));
96 return new (buffer) SubKindOperand(op);
97 }
98
ReplaceWith(InstructionOperand * dest,const InstructionOperand * src)99 static void ReplaceWith(InstructionOperand* dest,
100 const InstructionOperand* src) {
101 *dest = *src;
102 }
103
Equals(const InstructionOperand & that)104 bool Equals(const InstructionOperand& that) const {
105 return this->value_ == that.value_;
106 }
107
Compare(const InstructionOperand & that)108 bool Compare(const InstructionOperand& that) const {
109 return this->value_ < that.value_;
110 }
111
EqualsCanonicalized(const InstructionOperand & that)112 bool EqualsCanonicalized(const InstructionOperand& that) const {
113 return this->GetCanonicalizedValue() == that.GetCanonicalizedValue();
114 }
115
CompareCanonicalized(const InstructionOperand & that)116 bool CompareCanonicalized(const InstructionOperand& that) const {
117 return this->GetCanonicalizedValue() < that.GetCanonicalizedValue();
118 }
119
120 bool InterferesWith(const InstructionOperand& other) const;
121
122 // APIs to aid debugging. For general-stream APIs, use operator<<
123 void Print(const RegisterConfiguration* config) const;
124 void Print() const;
125
126 protected:
InstructionOperand(Kind kind)127 explicit InstructionOperand(Kind kind) : value_(KindField::encode(kind)) {}
128
129 inline uint64_t GetCanonicalizedValue() const;
130
131 class KindField : public BitField64<Kind, 0, 3> {};
132
133 uint64_t value_;
134 };
135
136
137 typedef ZoneVector<InstructionOperand> InstructionOperandVector;
138
139
140 struct PrintableInstructionOperand {
141 const RegisterConfiguration* register_configuration_;
142 InstructionOperand op_;
143 };
144
145
146 std::ostream& operator<<(std::ostream& os,
147 const PrintableInstructionOperand& op);
148
149
150 #define INSTRUCTION_OPERAND_CASTS(OperandType, OperandKind) \
151 \
152 static OperandType* cast(InstructionOperand* op) { \
153 DCHECK_EQ(OperandKind, op->kind()); \
154 return static_cast<OperandType*>(op); \
155 } \
156 \
157 static const OperandType* cast(const InstructionOperand* op) { \
158 DCHECK_EQ(OperandKind, op->kind()); \
159 return static_cast<const OperandType*>(op); \
160 } \
161 \
162 static OperandType cast(const InstructionOperand& op) { \
163 DCHECK_EQ(OperandKind, op.kind()); \
164 return *static_cast<const OperandType*>(&op); \
165 }
166
167 class UnallocatedOperand final : public InstructionOperand {
168 public:
169 enum BasicPolicy { FIXED_SLOT, EXTENDED_POLICY };
170
171 enum ExtendedPolicy {
172 NONE,
173 REGISTER_OR_SLOT,
174 REGISTER_OR_SLOT_OR_CONSTANT,
175 FIXED_REGISTER,
176 FIXED_FP_REGISTER,
177 MUST_HAVE_REGISTER,
178 MUST_HAVE_SLOT,
179 SAME_AS_FIRST_INPUT
180 };
181
182 // Lifetime of operand inside the instruction.
183 enum Lifetime {
184 // USED_AT_START operand is guaranteed to be live only at instruction start.
185 // The register allocator is free to assign the same register to some other
186 // operand used inside instruction (i.e. temporary or output).
187 USED_AT_START,
188
189 // USED_AT_END operand is treated as live until the end of instruction.
190 // This means that register allocator will not reuse its register for any
191 // other operand inside instruction.
192 USED_AT_END
193 };
194
UnallocatedOperand(ExtendedPolicy policy,int virtual_register)195 UnallocatedOperand(ExtendedPolicy policy, int virtual_register)
196 : UnallocatedOperand(virtual_register) {
197 value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
198 value_ |= ExtendedPolicyField::encode(policy);
199 value_ |= LifetimeField::encode(USED_AT_END);
200 }
201
UnallocatedOperand(BasicPolicy policy,int index,int virtual_register)202 UnallocatedOperand(BasicPolicy policy, int index, int virtual_register)
203 : UnallocatedOperand(virtual_register) {
204 DCHECK(policy == FIXED_SLOT);
205 value_ |= BasicPolicyField::encode(policy);
206 value_ |= static_cast<int64_t>(index) << FixedSlotIndexField::kShift;
207 DCHECK(this->fixed_slot_index() == index);
208 }
209
UnallocatedOperand(ExtendedPolicy policy,int index,int virtual_register)210 UnallocatedOperand(ExtendedPolicy policy, int index, int virtual_register)
211 : UnallocatedOperand(virtual_register) {
212 DCHECK(policy == FIXED_REGISTER || policy == FIXED_FP_REGISTER);
213 value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
214 value_ |= ExtendedPolicyField::encode(policy);
215 value_ |= LifetimeField::encode(USED_AT_END);
216 value_ |= FixedRegisterField::encode(index);
217 }
218
UnallocatedOperand(ExtendedPolicy policy,Lifetime lifetime,int virtual_register)219 UnallocatedOperand(ExtendedPolicy policy, Lifetime lifetime,
220 int virtual_register)
221 : UnallocatedOperand(virtual_register) {
222 value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
223 value_ |= ExtendedPolicyField::encode(policy);
224 value_ |= LifetimeField::encode(lifetime);
225 }
226
UnallocatedOperand(int reg_id,int slot_id,int virtual_register)227 UnallocatedOperand(int reg_id, int slot_id, int virtual_register)
228 : UnallocatedOperand(FIXED_REGISTER, reg_id, virtual_register) {
229 value_ |= HasSecondaryStorageField::encode(true);
230 value_ |= SecondaryStorageField::encode(slot_id);
231 }
232
UnallocatedOperand(const UnallocatedOperand & other,int virtual_register)233 UnallocatedOperand(const UnallocatedOperand& other, int virtual_register) {
234 DCHECK_NE(kInvalidVirtualRegister, virtual_register);
235 value_ = VirtualRegisterField::update(
236 other.value_, static_cast<uint32_t>(virtual_register));
237 }
238
239 // Predicates for the operand policy.
HasRegisterOrSlotPolicy()240 bool HasRegisterOrSlotPolicy() const {
241 return basic_policy() == EXTENDED_POLICY &&
242 extended_policy() == REGISTER_OR_SLOT;
243 }
HasRegisterOrSlotOrConstantPolicy()244 bool HasRegisterOrSlotOrConstantPolicy() const {
245 return basic_policy() == EXTENDED_POLICY &&
246 extended_policy() == REGISTER_OR_SLOT_OR_CONSTANT;
247 }
HasFixedPolicy()248 bool HasFixedPolicy() const {
249 return basic_policy() == FIXED_SLOT ||
250 extended_policy() == FIXED_REGISTER ||
251 extended_policy() == FIXED_FP_REGISTER;
252 }
HasRegisterPolicy()253 bool HasRegisterPolicy() const {
254 return basic_policy() == EXTENDED_POLICY &&
255 extended_policy() == MUST_HAVE_REGISTER;
256 }
HasSlotPolicy()257 bool HasSlotPolicy() const {
258 return basic_policy() == EXTENDED_POLICY &&
259 extended_policy() == MUST_HAVE_SLOT;
260 }
HasSameAsInputPolicy()261 bool HasSameAsInputPolicy() const {
262 return basic_policy() == EXTENDED_POLICY &&
263 extended_policy() == SAME_AS_FIRST_INPUT;
264 }
HasFixedSlotPolicy()265 bool HasFixedSlotPolicy() const { return basic_policy() == FIXED_SLOT; }
HasFixedRegisterPolicy()266 bool HasFixedRegisterPolicy() const {
267 return basic_policy() == EXTENDED_POLICY &&
268 extended_policy() == FIXED_REGISTER;
269 }
HasFixedFPRegisterPolicy()270 bool HasFixedFPRegisterPolicy() const {
271 return basic_policy() == EXTENDED_POLICY &&
272 extended_policy() == FIXED_FP_REGISTER;
273 }
HasSecondaryStorage()274 bool HasSecondaryStorage() const {
275 return basic_policy() == EXTENDED_POLICY &&
276 extended_policy() == FIXED_REGISTER &&
277 HasSecondaryStorageField::decode(value_);
278 }
GetSecondaryStorage()279 int GetSecondaryStorage() const {
280 DCHECK(HasSecondaryStorage());
281 return SecondaryStorageField::decode(value_);
282 }
283
284 // [basic_policy]: Distinguish between FIXED_SLOT and all other policies.
basic_policy()285 BasicPolicy basic_policy() const {
286 return BasicPolicyField::decode(value_);
287 }
288
289 // [extended_policy]: Only for non-FIXED_SLOT. The finer-grained policy.
extended_policy()290 ExtendedPolicy extended_policy() const {
291 DCHECK(basic_policy() == EXTENDED_POLICY);
292 return ExtendedPolicyField::decode(value_);
293 }
294
295 // [fixed_slot_index]: Only for FIXED_SLOT.
fixed_slot_index()296 int fixed_slot_index() const {
297 DCHECK(HasFixedSlotPolicy());
298 return static_cast<int>(static_cast<int64_t>(value_) >>
299 FixedSlotIndexField::kShift);
300 }
301
302 // [fixed_register_index]: Only for FIXED_REGISTER or FIXED_FP_REGISTER.
fixed_register_index()303 int fixed_register_index() const {
304 DCHECK(HasFixedRegisterPolicy() || HasFixedFPRegisterPolicy());
305 return FixedRegisterField::decode(value_);
306 }
307
308 // [virtual_register]: The virtual register ID for this operand.
virtual_register()309 int32_t virtual_register() const {
310 return static_cast<int32_t>(VirtualRegisterField::decode(value_));
311 }
312
313 // [lifetime]: Only for non-FIXED_SLOT.
IsUsedAtStart()314 bool IsUsedAtStart() const {
315 DCHECK(basic_policy() == EXTENDED_POLICY);
316 return LifetimeField::decode(value_) == USED_AT_START;
317 }
318
319 INSTRUCTION_OPERAND_CASTS(UnallocatedOperand, UNALLOCATED);
320
321 // The encoding used for UnallocatedOperand operands depends on the policy
322 // that is
323 // stored within the operand. The FIXED_SLOT policy uses a compact encoding
324 // because it accommodates a larger pay-load.
325 //
326 // For FIXED_SLOT policy:
327 // +------------------------------------------------+
328 // | slot_index | 0 | virtual_register | 001 |
329 // +------------------------------------------------+
330 //
331 // For all other (extended) policies:
332 // +-----------------------------------------------------+
333 // | reg_index | L | PPP | 1 | virtual_register | 001 |
334 // +-----------------------------------------------------+
335 // L ... Lifetime
336 // P ... Policy
337 //
338 // The slot index is a signed value which requires us to decode it manually
339 // instead of using the BitField utility class.
340
341 STATIC_ASSERT(KindField::kSize == 3);
342
343 class VirtualRegisterField : public BitField64<uint32_t, 3, 32> {};
344
345 // BitFields for all unallocated operands.
346 class BasicPolicyField : public BitField64<BasicPolicy, 35, 1> {};
347
348 // BitFields specific to BasicPolicy::FIXED_SLOT.
349 class FixedSlotIndexField : public BitField64<int, 36, 28> {};
350
351 // BitFields specific to BasicPolicy::EXTENDED_POLICY.
352 class ExtendedPolicyField : public BitField64<ExtendedPolicy, 36, 3> {};
353 class LifetimeField : public BitField64<Lifetime, 39, 1> {};
354 class HasSecondaryStorageField : public BitField64<bool, 40, 1> {};
355 class FixedRegisterField : public BitField64<int, 41, 6> {};
356 class SecondaryStorageField : public BitField64<int, 47, 3> {};
357
358 private:
UnallocatedOperand(int virtual_register)359 explicit UnallocatedOperand(int virtual_register)
360 : InstructionOperand(UNALLOCATED) {
361 value_ |=
362 VirtualRegisterField::encode(static_cast<uint32_t>(virtual_register));
363 }
364 };
365
366
367 class ConstantOperand : public InstructionOperand {
368 public:
ConstantOperand(int virtual_register)369 explicit ConstantOperand(int virtual_register)
370 : InstructionOperand(CONSTANT) {
371 value_ |=
372 VirtualRegisterField::encode(static_cast<uint32_t>(virtual_register));
373 }
374
virtual_register()375 int32_t virtual_register() const {
376 return static_cast<int32_t>(VirtualRegisterField::decode(value_));
377 }
378
New(Zone * zone,int virtual_register)379 static ConstantOperand* New(Zone* zone, int virtual_register) {
380 return InstructionOperand::New(zone, ConstantOperand(virtual_register));
381 }
382
383 INSTRUCTION_OPERAND_CASTS(ConstantOperand, CONSTANT);
384
385 STATIC_ASSERT(KindField::kSize == 3);
386 class VirtualRegisterField : public BitField64<uint32_t, 3, 32> {};
387 };
388
389
390 class ImmediateOperand : public InstructionOperand {
391 public:
392 enum ImmediateType { INLINE, INDEXED };
393
ImmediateOperand(ImmediateType type,int32_t value)394 explicit ImmediateOperand(ImmediateType type, int32_t value)
395 : InstructionOperand(IMMEDIATE) {
396 value_ |= TypeField::encode(type);
397 value_ |= static_cast<int64_t>(value) << ValueField::kShift;
398 }
399
type()400 ImmediateType type() const { return TypeField::decode(value_); }
401
inline_value()402 int32_t inline_value() const {
403 DCHECK_EQ(INLINE, type());
404 return static_cast<int64_t>(value_) >> ValueField::kShift;
405 }
406
indexed_value()407 int32_t indexed_value() const {
408 DCHECK_EQ(INDEXED, type());
409 return static_cast<int64_t>(value_) >> ValueField::kShift;
410 }
411
New(Zone * zone,ImmediateType type,int32_t value)412 static ImmediateOperand* New(Zone* zone, ImmediateType type, int32_t value) {
413 return InstructionOperand::New(zone, ImmediateOperand(type, value));
414 }
415
416 INSTRUCTION_OPERAND_CASTS(ImmediateOperand, IMMEDIATE);
417
418 STATIC_ASSERT(KindField::kSize == 3);
419 class TypeField : public BitField64<ImmediateType, 3, 1> {};
420 class ValueField : public BitField64<int32_t, 32, 32> {};
421 };
422
423
424 class LocationOperand : public InstructionOperand {
425 public:
426 enum LocationKind { REGISTER, STACK_SLOT };
427
LocationOperand(InstructionOperand::Kind operand_kind,LocationOperand::LocationKind location_kind,MachineRepresentation rep,int index)428 LocationOperand(InstructionOperand::Kind operand_kind,
429 LocationOperand::LocationKind location_kind,
430 MachineRepresentation rep, int index)
431 : InstructionOperand(operand_kind) {
432 DCHECK_IMPLIES(location_kind == REGISTER, index >= 0);
433 DCHECK(IsSupportedRepresentation(rep));
434 value_ |= LocationKindField::encode(location_kind);
435 value_ |= RepresentationField::encode(rep);
436 value_ |= static_cast<int64_t>(index) << IndexField::kShift;
437 }
438
index()439 int index() const {
440 DCHECK(IsStackSlot() || IsFPStackSlot());
441 return static_cast<int64_t>(value_) >> IndexField::kShift;
442 }
443
register_code()444 int register_code() const {
445 DCHECK(IsRegister() || IsFPRegister());
446 return static_cast<int64_t>(value_) >> IndexField::kShift;
447 }
448
GetRegister()449 Register GetRegister() const {
450 DCHECK(IsRegister());
451 return Register::from_code(register_code());
452 }
453
GetFloatRegister()454 FloatRegister GetFloatRegister() const {
455 DCHECK(IsFloatRegister());
456 return FloatRegister::from_code(register_code());
457 }
458
GetDoubleRegister()459 DoubleRegister GetDoubleRegister() const {
460 // On platforms where FloatRegister, DoubleRegister, and Simd128Register
461 // are all the same type, it's convenient to treat everything as a
462 // DoubleRegister, so be lax about type checking here.
463 DCHECK(IsFPRegister());
464 return DoubleRegister::from_code(register_code());
465 }
466
GetSimd128Register()467 Simd128Register GetSimd128Register() const {
468 DCHECK(IsSimd128Register());
469 return Simd128Register::from_code(register_code());
470 }
471
location_kind()472 LocationKind location_kind() const {
473 return LocationKindField::decode(value_);
474 }
475
representation()476 MachineRepresentation representation() const {
477 return RepresentationField::decode(value_);
478 }
479
IsSupportedRepresentation(MachineRepresentation rep)480 static bool IsSupportedRepresentation(MachineRepresentation rep) {
481 switch (rep) {
482 case MachineRepresentation::kWord32:
483 case MachineRepresentation::kWord64:
484 case MachineRepresentation::kFloat32:
485 case MachineRepresentation::kFloat64:
486 case MachineRepresentation::kSimd128:
487 case MachineRepresentation::kTaggedSigned:
488 case MachineRepresentation::kTaggedPointer:
489 case MachineRepresentation::kTagged:
490 return true;
491 case MachineRepresentation::kBit:
492 case MachineRepresentation::kWord8:
493 case MachineRepresentation::kWord16:
494 case MachineRepresentation::kNone:
495 return false;
496 }
497 UNREACHABLE();
498 }
499
500 // Return true if the locations can be moved to one another.
501 bool IsCompatible(LocationOperand* op);
502
cast(InstructionOperand * op)503 static LocationOperand* cast(InstructionOperand* op) {
504 DCHECK(op->IsAnyLocationOperand());
505 return static_cast<LocationOperand*>(op);
506 }
507
cast(const InstructionOperand * op)508 static const LocationOperand* cast(const InstructionOperand* op) {
509 DCHECK(op->IsAnyLocationOperand());
510 return static_cast<const LocationOperand*>(op);
511 }
512
cast(const InstructionOperand & op)513 static LocationOperand cast(const InstructionOperand& op) {
514 DCHECK(op.IsAnyLocationOperand());
515 return *static_cast<const LocationOperand*>(&op);
516 }
517
518 STATIC_ASSERT(KindField::kSize == 3);
519 class LocationKindField : public BitField64<LocationKind, 3, 2> {};
520 class RepresentationField : public BitField64<MachineRepresentation, 5, 8> {};
521 class IndexField : public BitField64<int32_t, 35, 29> {};
522 };
523
524 class V8_EXPORT_PRIVATE ExplicitOperand
NON_EXPORTED_BASE(LocationOperand)525 : public NON_EXPORTED_BASE(LocationOperand) {
526 public:
527 ExplicitOperand(LocationKind kind, MachineRepresentation rep, int index);
528
529 static ExplicitOperand* New(Zone* zone, LocationKind kind,
530 MachineRepresentation rep, int index) {
531 return InstructionOperand::New(zone, ExplicitOperand(kind, rep, index));
532 }
533
534 INSTRUCTION_OPERAND_CASTS(ExplicitOperand, EXPLICIT);
535 };
536
537
538 class AllocatedOperand : public LocationOperand {
539 public:
AllocatedOperand(LocationKind kind,MachineRepresentation rep,int index)540 AllocatedOperand(LocationKind kind, MachineRepresentation rep, int index)
541 : LocationOperand(ALLOCATED, kind, rep, index) {}
542
New(Zone * zone,LocationKind kind,MachineRepresentation rep,int index)543 static AllocatedOperand* New(Zone* zone, LocationKind kind,
544 MachineRepresentation rep, int index) {
545 return InstructionOperand::New(zone, AllocatedOperand(kind, rep, index));
546 }
547
548 INSTRUCTION_OPERAND_CASTS(AllocatedOperand, ALLOCATED);
549 };
550
551
552 #undef INSTRUCTION_OPERAND_CASTS
553
IsAnyLocationOperand()554 bool InstructionOperand::IsAnyLocationOperand() const {
555 return this->kind() >= FIRST_LOCATION_OPERAND_KIND;
556 }
557
IsLocationOperand()558 bool InstructionOperand::IsLocationOperand() const {
559 return IsAnyLocationOperand() &&
560 !IsFloatingPoint(LocationOperand::cast(this)->representation());
561 }
562
IsFPLocationOperand()563 bool InstructionOperand::IsFPLocationOperand() const {
564 return IsAnyLocationOperand() &&
565 IsFloatingPoint(LocationOperand::cast(this)->representation());
566 }
567
IsAnyRegister()568 bool InstructionOperand::IsAnyRegister() const {
569 return IsAnyLocationOperand() &&
570 LocationOperand::cast(this)->location_kind() ==
571 LocationOperand::REGISTER;
572 }
573
574
IsRegister()575 bool InstructionOperand::IsRegister() const {
576 return IsAnyRegister() &&
577 !IsFloatingPoint(LocationOperand::cast(this)->representation());
578 }
579
IsFPRegister()580 bool InstructionOperand::IsFPRegister() const {
581 return IsAnyRegister() &&
582 IsFloatingPoint(LocationOperand::cast(this)->representation());
583 }
584
IsFloatRegister()585 bool InstructionOperand::IsFloatRegister() const {
586 return IsAnyRegister() &&
587 LocationOperand::cast(this)->representation() ==
588 MachineRepresentation::kFloat32;
589 }
590
IsDoubleRegister()591 bool InstructionOperand::IsDoubleRegister() const {
592 return IsAnyRegister() &&
593 LocationOperand::cast(this)->representation() ==
594 MachineRepresentation::kFloat64;
595 }
596
IsSimd128Register()597 bool InstructionOperand::IsSimd128Register() const {
598 return IsAnyRegister() && LocationOperand::cast(this)->representation() ==
599 MachineRepresentation::kSimd128;
600 }
601
IsAnyStackSlot()602 bool InstructionOperand::IsAnyStackSlot() const {
603 return IsAnyLocationOperand() &&
604 LocationOperand::cast(this)->location_kind() ==
605 LocationOperand::STACK_SLOT;
606 }
607
IsStackSlot()608 bool InstructionOperand::IsStackSlot() const {
609 return IsAnyStackSlot() &&
610 !IsFloatingPoint(LocationOperand::cast(this)->representation());
611 }
612
IsFPStackSlot()613 bool InstructionOperand::IsFPStackSlot() const {
614 return IsAnyStackSlot() &&
615 IsFloatingPoint(LocationOperand::cast(this)->representation());
616 }
617
IsFloatStackSlot()618 bool InstructionOperand::IsFloatStackSlot() const {
619 return IsAnyLocationOperand() &&
620 LocationOperand::cast(this)->location_kind() ==
621 LocationOperand::STACK_SLOT &&
622 LocationOperand::cast(this)->representation() ==
623 MachineRepresentation::kFloat32;
624 }
625
IsDoubleStackSlot()626 bool InstructionOperand::IsDoubleStackSlot() const {
627 return IsAnyLocationOperand() &&
628 LocationOperand::cast(this)->location_kind() ==
629 LocationOperand::STACK_SLOT &&
630 LocationOperand::cast(this)->representation() ==
631 MachineRepresentation::kFloat64;
632 }
633
IsSimd128StackSlot()634 bool InstructionOperand::IsSimd128StackSlot() const {
635 return IsAnyLocationOperand() &&
636 LocationOperand::cast(this)->location_kind() ==
637 LocationOperand::STACK_SLOT &&
638 LocationOperand::cast(this)->representation() ==
639 MachineRepresentation::kSimd128;
640 }
641
GetCanonicalizedValue()642 uint64_t InstructionOperand::GetCanonicalizedValue() const {
643 if (IsAnyLocationOperand()) {
644 MachineRepresentation canonical = MachineRepresentation::kNone;
645 if (IsFPRegister()) {
646 if (kSimpleFPAliasing) {
647 // We treat all FP register operands the same for simple aliasing.
648 canonical = MachineRepresentation::kFloat64;
649 } else {
650 // We need to distinguish FP register operands of different reps when
651 // aliasing is not simple (e.g. ARM).
652 canonical = LocationOperand::cast(this)->representation();
653 }
654 }
655 return InstructionOperand::KindField::update(
656 LocationOperand::RepresentationField::update(this->value_, canonical),
657 LocationOperand::EXPLICIT);
658 }
659 return this->value_;
660 }
661
662 // Required for maps that don't care about machine type.
663 struct CompareOperandModuloType {
operatorCompareOperandModuloType664 bool operator()(const InstructionOperand& a,
665 const InstructionOperand& b) const {
666 return a.CompareCanonicalized(b);
667 }
668 };
669
670 class V8_EXPORT_PRIVATE MoveOperands final
NON_EXPORTED_BASE(ZoneObject)671 : public NON_EXPORTED_BASE(ZoneObject) {
672 public:
673 MoveOperands(const InstructionOperand& source,
674 const InstructionOperand& destination)
675 : source_(source), destination_(destination) {
676 DCHECK(!source.IsInvalid() && !destination.IsInvalid());
677 }
678
679 const InstructionOperand& source() const { return source_; }
680 InstructionOperand& source() { return source_; }
681 void set_source(const InstructionOperand& operand) { source_ = operand; }
682
683 const InstructionOperand& destination() const { return destination_; }
684 InstructionOperand& destination() { return destination_; }
685 void set_destination(const InstructionOperand& operand) {
686 destination_ = operand;
687 }
688
689 // The gap resolver marks moves as "in-progress" by clearing the
690 // destination (but not the source).
691 bool IsPending() const {
692 return destination_.IsInvalid() && !source_.IsInvalid();
693 }
694 void SetPending() { destination_ = InstructionOperand(); }
695
696 // A move is redundant if it's been eliminated or if its source and
697 // destination are the same.
698 bool IsRedundant() const {
699 DCHECK_IMPLIES(!destination_.IsInvalid(), !destination_.IsConstant());
700 return IsEliminated() || source_.EqualsCanonicalized(destination_);
701 }
702
703 // We clear both operands to indicate move that's been eliminated.
704 void Eliminate() { source_ = destination_ = InstructionOperand(); }
705 bool IsEliminated() const {
706 DCHECK_IMPLIES(source_.IsInvalid(), destination_.IsInvalid());
707 return source_.IsInvalid();
708 }
709
710 // APIs to aid debugging. For general-stream APIs, use operator<<
711 void Print(const RegisterConfiguration* config) const;
712 void Print() const;
713
714 private:
715 InstructionOperand source_;
716 InstructionOperand destination_;
717
718 DISALLOW_COPY_AND_ASSIGN(MoveOperands);
719 };
720
721
722 struct PrintableMoveOperands {
723 const RegisterConfiguration* register_configuration_;
724 const MoveOperands* move_operands_;
725 };
726
727
728 std::ostream& operator<<(std::ostream& os, const PrintableMoveOperands& mo);
729
730 class V8_EXPORT_PRIVATE ParallelMove final
NON_EXPORTED_BASE(ZoneVector<MoveOperands * >)731 : public NON_EXPORTED_BASE(ZoneVector<MoveOperands *>),
732 public NON_EXPORTED_BASE(ZoneObject) {
733 public:
734 explicit ParallelMove(Zone* zone) : ZoneVector<MoveOperands*>(zone) {
735 reserve(4);
736 }
737
738 MoveOperands* AddMove(const InstructionOperand& from,
739 const InstructionOperand& to) {
740 Zone* zone = get_allocator().zone();
741 return AddMove(from, to, zone);
742 }
743
744 MoveOperands* AddMove(const InstructionOperand& from,
745 const InstructionOperand& to,
746 Zone* operand_allocation_zone) {
747 MoveOperands* move = new (operand_allocation_zone) MoveOperands(from, to);
748 push_back(move);
749 return move;
750 }
751
752 bool IsRedundant() const;
753
754 // Prepare this ParallelMove to insert move as if it happened in a subsequent
755 // ParallelMove. move->source() may be changed. Any MoveOperands added to
756 // to_eliminate must be Eliminated.
757 void PrepareInsertAfter(MoveOperands* move,
758 ZoneVector<MoveOperands*>* to_eliminate) const;
759
760 private:
761 DISALLOW_COPY_AND_ASSIGN(ParallelMove);
762 };
763
764
765 struct PrintableParallelMove {
766 const RegisterConfiguration* register_configuration_;
767 const ParallelMove* parallel_move_;
768 };
769
770
771 std::ostream& operator<<(std::ostream& os, const PrintableParallelMove& pm);
772
773
774 class ReferenceMap final : public ZoneObject {
775 public:
ReferenceMap(Zone * zone)776 explicit ReferenceMap(Zone* zone)
777 : reference_operands_(8, zone), instruction_position_(-1) {}
778
reference_operands()779 const ZoneVector<InstructionOperand>& reference_operands() const {
780 return reference_operands_;
781 }
instruction_position()782 int instruction_position() const { return instruction_position_; }
783
set_instruction_position(int pos)784 void set_instruction_position(int pos) {
785 DCHECK_EQ(-1, instruction_position_);
786 instruction_position_ = pos;
787 }
788
789 void RecordReference(const AllocatedOperand& op);
790
791 private:
792 friend std::ostream& operator<<(std::ostream& os, const ReferenceMap& pm);
793
794 ZoneVector<InstructionOperand> reference_operands_;
795 int instruction_position_;
796 };
797
798 std::ostream& operator<<(std::ostream& os, const ReferenceMap& pm);
799
800 class InstructionBlock;
801
802 class V8_EXPORT_PRIVATE Instruction final {
803 public:
OutputCount()804 size_t OutputCount() const { return OutputCountField::decode(bit_field_); }
OutputAt(size_t i)805 const InstructionOperand* OutputAt(size_t i) const {
806 DCHECK(i < OutputCount());
807 return &operands_[i];
808 }
OutputAt(size_t i)809 InstructionOperand* OutputAt(size_t i) {
810 DCHECK(i < OutputCount());
811 return &operands_[i];
812 }
813
HasOutput()814 bool HasOutput() const { return OutputCount() > 0; }
Output()815 const InstructionOperand* Output() const { return OutputAt(0); }
Output()816 InstructionOperand* Output() { return OutputAt(0); }
817
InputCount()818 size_t InputCount() const { return InputCountField::decode(bit_field_); }
InputAt(size_t i)819 const InstructionOperand* InputAt(size_t i) const {
820 DCHECK(i < InputCount());
821 return &operands_[OutputCount() + i];
822 }
InputAt(size_t i)823 InstructionOperand* InputAt(size_t i) {
824 DCHECK(i < InputCount());
825 return &operands_[OutputCount() + i];
826 }
827
TempCount()828 size_t TempCount() const { return TempCountField::decode(bit_field_); }
TempAt(size_t i)829 const InstructionOperand* TempAt(size_t i) const {
830 DCHECK(i < TempCount());
831 return &operands_[OutputCount() + InputCount() + i];
832 }
TempAt(size_t i)833 InstructionOperand* TempAt(size_t i) {
834 DCHECK(i < TempCount());
835 return &operands_[OutputCount() + InputCount() + i];
836 }
837
opcode()838 InstructionCode opcode() const { return opcode_; }
arch_opcode()839 ArchOpcode arch_opcode() const { return ArchOpcodeField::decode(opcode()); }
addressing_mode()840 AddressingMode addressing_mode() const {
841 return AddressingModeField::decode(opcode());
842 }
flags_mode()843 FlagsMode flags_mode() const { return FlagsModeField::decode(opcode()); }
flags_condition()844 FlagsCondition flags_condition() const {
845 return FlagsConditionField::decode(opcode());
846 }
847
New(Zone * zone,InstructionCode opcode)848 static Instruction* New(Zone* zone, InstructionCode opcode) {
849 return New(zone, opcode, 0, nullptr, 0, nullptr, 0, nullptr);
850 }
851
New(Zone * zone,InstructionCode opcode,size_t output_count,InstructionOperand * outputs,size_t input_count,InstructionOperand * inputs,size_t temp_count,InstructionOperand * temps)852 static Instruction* New(Zone* zone, InstructionCode opcode,
853 size_t output_count, InstructionOperand* outputs,
854 size_t input_count, InstructionOperand* inputs,
855 size_t temp_count, InstructionOperand* temps) {
856 DCHECK_LE(0, opcode);
857 DCHECK(output_count == 0 || outputs != nullptr);
858 DCHECK(input_count == 0 || inputs != nullptr);
859 DCHECK(temp_count == 0 || temps != nullptr);
860 // TODO(jarin/mstarzinger): Handle this gracefully. See crbug.com/582702.
861 CHECK(InputCountField::is_valid(input_count));
862
863 size_t total_extra_ops = output_count + input_count + temp_count;
864 if (total_extra_ops != 0) total_extra_ops--;
865 int size = static_cast<int>(
866 RoundUp(sizeof(Instruction), sizeof(InstructionOperand)) +
867 total_extra_ops * sizeof(InstructionOperand));
868 return new (zone->New(size)) Instruction(
869 opcode, output_count, outputs, input_count, inputs, temp_count, temps);
870 }
871
MarkAsCall()872 Instruction* MarkAsCall() {
873 bit_field_ = IsCallField::update(bit_field_, true);
874 return this;
875 }
IsCall()876 bool IsCall() const { return IsCallField::decode(bit_field_); }
NeedsReferenceMap()877 bool NeedsReferenceMap() const { return IsCall(); }
HasReferenceMap()878 bool HasReferenceMap() const { return reference_map_ != nullptr; }
879
ClobbersRegisters()880 bool ClobbersRegisters() const { return IsCall(); }
ClobbersTemps()881 bool ClobbersTemps() const { return IsCall(); }
ClobbersDoubleRegisters()882 bool ClobbersDoubleRegisters() const { return IsCall(); }
reference_map()883 ReferenceMap* reference_map() const { return reference_map_; }
884
set_reference_map(ReferenceMap * map)885 void set_reference_map(ReferenceMap* map) {
886 DCHECK(NeedsReferenceMap());
887 DCHECK(!reference_map_);
888 reference_map_ = map;
889 }
890
OverwriteWithNop()891 void OverwriteWithNop() {
892 opcode_ = ArchOpcodeField::encode(kArchNop);
893 bit_field_ = 0;
894 reference_map_ = nullptr;
895 }
896
IsNop()897 bool IsNop() const { return arch_opcode() == kArchNop; }
898
IsDeoptimizeCall()899 bool IsDeoptimizeCall() const {
900 return arch_opcode() == ArchOpcode::kArchDeoptimize ||
901 FlagsModeField::decode(opcode()) == kFlags_deoptimize ||
902 FlagsModeField::decode(opcode()) == kFlags_deoptimize_and_poison;
903 }
904
IsTrap()905 bool IsTrap() const {
906 return FlagsModeField::decode(opcode()) == kFlags_trap;
907 }
908
IsJump()909 bool IsJump() const { return arch_opcode() == ArchOpcode::kArchJmp; }
IsRet()910 bool IsRet() const { return arch_opcode() == ArchOpcode::kArchRet; }
IsTailCall()911 bool IsTailCall() const {
912 return arch_opcode() == ArchOpcode::kArchTailCallCodeObject ||
913 arch_opcode() == ArchOpcode::kArchTailCallCodeObjectFromJSFunction ||
914 arch_opcode() == ArchOpcode::kArchTailCallAddress ||
915 arch_opcode() == ArchOpcode::kArchTailCallWasm;
916 }
IsThrow()917 bool IsThrow() const {
918 return arch_opcode() == ArchOpcode::kArchThrowTerminator;
919 }
920
921 enum GapPosition {
922 START,
923 END,
924 FIRST_GAP_POSITION = START,
925 LAST_GAP_POSITION = END
926 };
927
GetOrCreateParallelMove(GapPosition pos,Zone * zone)928 ParallelMove* GetOrCreateParallelMove(GapPosition pos, Zone* zone) {
929 if (parallel_moves_[pos] == nullptr) {
930 parallel_moves_[pos] = new (zone) ParallelMove(zone);
931 }
932 return parallel_moves_[pos];
933 }
934
GetParallelMove(GapPosition pos)935 ParallelMove* GetParallelMove(GapPosition pos) {
936 return parallel_moves_[pos];
937 }
938
GetParallelMove(GapPosition pos)939 const ParallelMove* GetParallelMove(GapPosition pos) const {
940 return parallel_moves_[pos];
941 }
942
943 bool AreMovesRedundant() const;
944
parallel_moves()945 ParallelMove* const* parallel_moves() const { return ¶llel_moves_[0]; }
parallel_moves()946 ParallelMove** parallel_moves() { return ¶llel_moves_[0]; }
947
948 // The block_id may be invalidated in JumpThreading. It is only important for
949 // register allocation, to avoid searching for blocks from instruction
950 // indexes.
block()951 InstructionBlock* block() const { return block_; }
set_block(InstructionBlock * block)952 void set_block(InstructionBlock* block) {
953 DCHECK_NOT_NULL(block);
954 block_ = block;
955 }
956
957 // APIs to aid debugging. For general-stream APIs, use operator<<
958 void Print(const RegisterConfiguration* config) const;
959 void Print() const;
960
961 typedef BitField<size_t, 0, 8> OutputCountField;
962 typedef BitField<size_t, 8, 16> InputCountField;
963 typedef BitField<size_t, 24, 6> TempCountField;
964
965 static const size_t kMaxOutputCount = OutputCountField::kMax;
966 static const size_t kMaxInputCount = InputCountField::kMax;
967 static const size_t kMaxTempCount = TempCountField::kMax;
968
969 private:
970 explicit Instruction(InstructionCode opcode);
971
972 Instruction(InstructionCode opcode, size_t output_count,
973 InstructionOperand* outputs, size_t input_count,
974 InstructionOperand* inputs, size_t temp_count,
975 InstructionOperand* temps);
976
977 typedef BitField<bool, 30, 1> IsCallField;
978
979 InstructionCode opcode_;
980 uint32_t bit_field_;
981 ParallelMove* parallel_moves_[2];
982 ReferenceMap* reference_map_;
983 InstructionBlock* block_;
984 InstructionOperand operands_[1];
985
986 DISALLOW_COPY_AND_ASSIGN(Instruction);
987 };
988
989
990 struct PrintableInstruction {
991 const RegisterConfiguration* register_configuration_;
992 const Instruction* instr_;
993 };
994 std::ostream& operator<<(std::ostream& os, const PrintableInstruction& instr);
995
996
997 class RpoNumber final {
998 public:
999 static const int kInvalidRpoNumber = -1;
ToInt()1000 int ToInt() const {
1001 DCHECK(IsValid());
1002 return index_;
1003 }
ToSize()1004 size_t ToSize() const {
1005 DCHECK(IsValid());
1006 return static_cast<size_t>(index_);
1007 }
IsValid()1008 bool IsValid() const { return index_ >= 0; }
FromInt(int index)1009 static RpoNumber FromInt(int index) { return RpoNumber(index); }
Invalid()1010 static RpoNumber Invalid() { return RpoNumber(kInvalidRpoNumber); }
1011
IsNext(const RpoNumber other)1012 bool IsNext(const RpoNumber other) const {
1013 DCHECK(IsValid());
1014 return other.index_ == this->index_ + 1;
1015 }
1016
1017 // Comparison operators.
1018 bool operator==(RpoNumber other) const { return index_ == other.index_; }
1019 bool operator!=(RpoNumber other) const { return index_ != other.index_; }
1020 bool operator>(RpoNumber other) const { return index_ > other.index_; }
1021 bool operator<(RpoNumber other) const { return index_ < other.index_; }
1022 bool operator<=(RpoNumber other) const { return index_ <= other.index_; }
1023 bool operator>=(RpoNumber other) const { return index_ >= other.index_; }
1024
1025 private:
RpoNumber(int32_t index)1026 explicit RpoNumber(int32_t index) : index_(index) {}
1027 int32_t index_;
1028 };
1029
1030
1031 std::ostream& operator<<(std::ostream&, const RpoNumber&);
1032
1033 class V8_EXPORT_PRIVATE Constant final {
1034 public:
1035 enum Type {
1036 kInt32,
1037 kInt64,
1038 kFloat32,
1039 kFloat64,
1040 kExternalReference,
1041 kHeapObject,
1042 kRpoNumber
1043 };
1044
1045 explicit Constant(int32_t v);
Constant(int64_t v)1046 explicit Constant(int64_t v) : type_(kInt64), value_(v) {}
Constant(float v)1047 explicit Constant(float v) : type_(kFloat32), value_(bit_cast<int32_t>(v)) {}
Constant(double v)1048 explicit Constant(double v) : type_(kFloat64), value_(bit_cast<int64_t>(v)) {}
Constant(ExternalReference ref)1049 explicit Constant(ExternalReference ref)
1050 : type_(kExternalReference), value_(bit_cast<intptr_t>(ref)) {}
Constant(Handle<HeapObject> obj)1051 explicit Constant(Handle<HeapObject> obj)
1052 : type_(kHeapObject), value_(bit_cast<intptr_t>(obj)) {}
Constant(RpoNumber rpo)1053 explicit Constant(RpoNumber rpo) : type_(kRpoNumber), value_(rpo.ToInt()) {}
1054 explicit Constant(RelocatablePtrConstantInfo info);
1055
type()1056 Type type() const { return type_; }
1057
rmode()1058 RelocInfo::Mode rmode() const { return rmode_; }
1059
ToInt32()1060 int32_t ToInt32() const {
1061 DCHECK(type() == kInt32 || type() == kInt64);
1062 const int32_t value = static_cast<int32_t>(value_);
1063 DCHECK_EQ(value_, static_cast<int64_t>(value));
1064 return value;
1065 }
1066
ToInt64()1067 int64_t ToInt64() const {
1068 if (type() == kInt32) return ToInt32();
1069 DCHECK_EQ(kInt64, type());
1070 return value_;
1071 }
1072
ToFloat32()1073 float ToFloat32() const {
1074 // TODO(ahaas): We should remove this function. If value_ has the bit
1075 // representation of a signalling NaN, then returning it as float can cause
1076 // the signalling bit to flip, and value_ is returned as a quiet NaN.
1077 DCHECK_EQ(kFloat32, type());
1078 return bit_cast<float>(static_cast<int32_t>(value_));
1079 }
1080
ToFloat32AsInt()1081 uint32_t ToFloat32AsInt() const {
1082 DCHECK_EQ(kFloat32, type());
1083 return bit_cast<uint32_t>(static_cast<int32_t>(value_));
1084 }
1085
ToFloat64()1086 Double ToFloat64() const {
1087 DCHECK_EQ(kFloat64, type());
1088 return Double(bit_cast<uint64_t>(value_));
1089 }
1090
ToExternalReference()1091 ExternalReference ToExternalReference() const {
1092 DCHECK_EQ(kExternalReference, type());
1093 return bit_cast<ExternalReference>(static_cast<intptr_t>(value_));
1094 }
1095
ToRpoNumber()1096 RpoNumber ToRpoNumber() const {
1097 DCHECK_EQ(kRpoNumber, type());
1098 return RpoNumber::FromInt(static_cast<int>(value_));
1099 }
1100
1101 Handle<HeapObject> ToHeapObject() const;
1102 Handle<Code> ToCode() const;
1103
1104 private:
1105 Type type_;
1106 RelocInfo::Mode rmode_ = RelocInfo::NONE;
1107 int64_t value_;
1108 };
1109
1110
1111 std::ostream& operator<<(std::ostream& os, const Constant& constant);
1112
1113
1114 // Forward declarations.
1115 class FrameStateDescriptor;
1116
1117 enum class StateValueKind : uint8_t {
1118 kArgumentsElements,
1119 kArgumentsLength,
1120 kPlain,
1121 kOptimizedOut,
1122 kNested,
1123 kDuplicate
1124 };
1125
1126 class StateValueDescriptor {
1127 public:
StateValueDescriptor()1128 StateValueDescriptor()
1129 : kind_(StateValueKind::kPlain), type_(MachineType::AnyTagged()) {}
1130
ArgumentsElements(ArgumentsStateType type)1131 static StateValueDescriptor ArgumentsElements(ArgumentsStateType type) {
1132 StateValueDescriptor descr(StateValueKind::kArgumentsElements,
1133 MachineType::AnyTagged());
1134 descr.args_type_ = type;
1135 return descr;
1136 }
ArgumentsLength(ArgumentsStateType type)1137 static StateValueDescriptor ArgumentsLength(ArgumentsStateType type) {
1138 StateValueDescriptor descr(StateValueKind::kArgumentsLength,
1139 MachineType::AnyTagged());
1140 descr.args_type_ = type;
1141 return descr;
1142 }
Plain(MachineType type)1143 static StateValueDescriptor Plain(MachineType type) {
1144 return StateValueDescriptor(StateValueKind::kPlain, type);
1145 }
OptimizedOut()1146 static StateValueDescriptor OptimizedOut() {
1147 return StateValueDescriptor(StateValueKind::kOptimizedOut,
1148 MachineType::AnyTagged());
1149 }
Recursive(size_t id)1150 static StateValueDescriptor Recursive(size_t id) {
1151 StateValueDescriptor descr(StateValueKind::kNested,
1152 MachineType::AnyTagged());
1153 descr.id_ = id;
1154 return descr;
1155 }
Duplicate(size_t id)1156 static StateValueDescriptor Duplicate(size_t id) {
1157 StateValueDescriptor descr(StateValueKind::kDuplicate,
1158 MachineType::AnyTagged());
1159 descr.id_ = id;
1160 return descr;
1161 }
1162
IsArgumentsElements()1163 bool IsArgumentsElements() const {
1164 return kind_ == StateValueKind::kArgumentsElements;
1165 }
IsArgumentsLength()1166 bool IsArgumentsLength() const {
1167 return kind_ == StateValueKind::kArgumentsLength;
1168 }
IsPlain()1169 bool IsPlain() const { return kind_ == StateValueKind::kPlain; }
IsOptimizedOut()1170 bool IsOptimizedOut() const { return kind_ == StateValueKind::kOptimizedOut; }
IsNested()1171 bool IsNested() const { return kind_ == StateValueKind::kNested; }
IsDuplicate()1172 bool IsDuplicate() const { return kind_ == StateValueKind::kDuplicate; }
type()1173 MachineType type() const { return type_; }
id()1174 size_t id() const {
1175 DCHECK(kind_ == StateValueKind::kDuplicate ||
1176 kind_ == StateValueKind::kNested);
1177 return id_;
1178 }
arguments_type()1179 ArgumentsStateType arguments_type() const {
1180 DCHECK(kind_ == StateValueKind::kArgumentsElements ||
1181 kind_ == StateValueKind::kArgumentsLength);
1182 return args_type_;
1183 }
1184
1185 private:
StateValueDescriptor(StateValueKind kind,MachineType type)1186 StateValueDescriptor(StateValueKind kind, MachineType type)
1187 : kind_(kind), type_(type) {}
1188
1189 StateValueKind kind_;
1190 MachineType type_;
1191 union {
1192 size_t id_;
1193 ArgumentsStateType args_type_;
1194 };
1195 };
1196
1197 class StateValueList {
1198 public:
StateValueList(Zone * zone)1199 explicit StateValueList(Zone* zone) : fields_(zone), nested_(zone) {}
1200
size()1201 size_t size() { return fields_.size(); }
1202
1203 struct Value {
1204 StateValueDescriptor* desc;
1205 StateValueList* nested;
1206
ValueValue1207 Value(StateValueDescriptor* desc, StateValueList* nested)
1208 : desc(desc), nested(nested) {}
1209 };
1210
1211 class iterator {
1212 public:
1213 // Bare minimum of operators needed for range iteration.
1214 bool operator!=(const iterator& other) const {
1215 return field_iterator != other.field_iterator;
1216 }
1217 bool operator==(const iterator& other) const {
1218 return field_iterator == other.field_iterator;
1219 }
1220 iterator& operator++() {
1221 if (field_iterator->IsNested()) {
1222 nested_iterator++;
1223 }
1224 ++field_iterator;
1225 return *this;
1226 }
1227 Value operator*() {
1228 StateValueDescriptor* desc = &(*field_iterator);
1229 StateValueList* nested = desc->IsNested() ? *nested_iterator : nullptr;
1230 return Value(desc, nested);
1231 }
1232
1233 private:
1234 friend class StateValueList;
1235
iterator(ZoneVector<StateValueDescriptor>::iterator it,ZoneVector<StateValueList * >::iterator nested)1236 iterator(ZoneVector<StateValueDescriptor>::iterator it,
1237 ZoneVector<StateValueList*>::iterator nested)
1238 : field_iterator(it), nested_iterator(nested) {}
1239
1240 ZoneVector<StateValueDescriptor>::iterator field_iterator;
1241 ZoneVector<StateValueList*>::iterator nested_iterator;
1242 };
1243
ReserveSize(size_t size)1244 void ReserveSize(size_t size) { fields_.reserve(size); }
1245
PushRecursiveField(Zone * zone,size_t id)1246 StateValueList* PushRecursiveField(Zone* zone, size_t id) {
1247 fields_.push_back(StateValueDescriptor::Recursive(id));
1248 StateValueList* nested =
1249 new (zone->New(sizeof(StateValueList))) StateValueList(zone);
1250 nested_.push_back(nested);
1251 return nested;
1252 }
PushArgumentsElements(ArgumentsStateType type)1253 void PushArgumentsElements(ArgumentsStateType type) {
1254 fields_.push_back(StateValueDescriptor::ArgumentsElements(type));
1255 }
PushArgumentsLength(ArgumentsStateType type)1256 void PushArgumentsLength(ArgumentsStateType type) {
1257 fields_.push_back(StateValueDescriptor::ArgumentsLength(type));
1258 }
PushDuplicate(size_t id)1259 void PushDuplicate(size_t id) {
1260 fields_.push_back(StateValueDescriptor::Duplicate(id));
1261 }
PushPlain(MachineType type)1262 void PushPlain(MachineType type) {
1263 fields_.push_back(StateValueDescriptor::Plain(type));
1264 }
PushOptimizedOut()1265 void PushOptimizedOut() {
1266 fields_.push_back(StateValueDescriptor::OptimizedOut());
1267 }
1268
begin()1269 iterator begin() { return iterator(fields_.begin(), nested_.begin()); }
end()1270 iterator end() { return iterator(fields_.end(), nested_.end()); }
1271
1272 private:
1273 ZoneVector<StateValueDescriptor> fields_;
1274 ZoneVector<StateValueList*> nested_;
1275 };
1276
1277 class FrameStateDescriptor : public ZoneObject {
1278 public:
1279 FrameStateDescriptor(Zone* zone, FrameStateType type, BailoutId bailout_id,
1280 OutputFrameStateCombine state_combine,
1281 size_t parameters_count, size_t locals_count,
1282 size_t stack_count,
1283 MaybeHandle<SharedFunctionInfo> shared_info,
1284 FrameStateDescriptor* outer_state = nullptr);
1285
type()1286 FrameStateType type() const { return type_; }
bailout_id()1287 BailoutId bailout_id() const { return bailout_id_; }
state_combine()1288 OutputFrameStateCombine state_combine() const { return frame_state_combine_; }
parameters_count()1289 size_t parameters_count() const { return parameters_count_; }
locals_count()1290 size_t locals_count() const { return locals_count_; }
stack_count()1291 size_t stack_count() const { return stack_count_; }
shared_info()1292 MaybeHandle<SharedFunctionInfo> shared_info() const { return shared_info_; }
outer_state()1293 FrameStateDescriptor* outer_state() const { return outer_state_; }
HasContext()1294 bool HasContext() const {
1295 return FrameStateFunctionInfo::IsJSFunctionType(type_) ||
1296 type_ == FrameStateType::kBuiltinContinuation;
1297 }
1298
1299 size_t GetSize() const;
1300 size_t GetTotalSize() const;
1301 size_t GetFrameCount() const;
1302 size_t GetJSFrameCount() const;
1303
GetStateValueDescriptors()1304 StateValueList* GetStateValueDescriptors() { return &values_; }
1305
1306 static const int kImpossibleValue = 0xdead;
1307
1308 private:
1309 FrameStateType type_;
1310 BailoutId bailout_id_;
1311 OutputFrameStateCombine frame_state_combine_;
1312 size_t parameters_count_;
1313 size_t locals_count_;
1314 size_t stack_count_;
1315 StateValueList values_;
1316 MaybeHandle<SharedFunctionInfo> const shared_info_;
1317 FrameStateDescriptor* outer_state_;
1318 };
1319
1320 // A deoptimization entry is a pair of the reason why we deoptimize and the
1321 // frame state descriptor that we have to go back to.
1322 class DeoptimizationEntry final {
1323 public:
DeoptimizationEntry()1324 DeoptimizationEntry() {}
DeoptimizationEntry(FrameStateDescriptor * descriptor,DeoptimizeKind kind,DeoptimizeReason reason,VectorSlotPair const & feedback)1325 DeoptimizationEntry(FrameStateDescriptor* descriptor, DeoptimizeKind kind,
1326 DeoptimizeReason reason, VectorSlotPair const& feedback)
1327 : descriptor_(descriptor),
1328 kind_(kind),
1329 reason_(reason),
1330 feedback_(feedback) {}
1331
descriptor()1332 FrameStateDescriptor* descriptor() const { return descriptor_; }
kind()1333 DeoptimizeKind kind() const { return kind_; }
reason()1334 DeoptimizeReason reason() const { return reason_; }
feedback()1335 VectorSlotPair const& feedback() const { return feedback_; }
1336
1337 private:
1338 FrameStateDescriptor* descriptor_ = nullptr;
1339 DeoptimizeKind kind_ = DeoptimizeKind::kEager;
1340 DeoptimizeReason reason_ = DeoptimizeReason::kUnknown;
1341 VectorSlotPair feedback_ = VectorSlotPair();
1342 };
1343
1344 typedef ZoneVector<DeoptimizationEntry> DeoptimizationVector;
1345
1346 class V8_EXPORT_PRIVATE PhiInstruction final
NON_EXPORTED_BASE(ZoneObject)1347 : public NON_EXPORTED_BASE(ZoneObject) {
1348 public:
1349 typedef ZoneVector<InstructionOperand> Inputs;
1350
1351 PhiInstruction(Zone* zone, int virtual_register, size_t input_count);
1352
1353 void SetInput(size_t offset, int virtual_register);
1354 void RenameInput(size_t offset, int virtual_register);
1355
1356 int virtual_register() const { return virtual_register_; }
1357 const IntVector& operands() const { return operands_; }
1358
1359 // TODO(dcarney): this has no real business being here, since it's internal to
1360 // the register allocator, but putting it here was convenient.
1361 const InstructionOperand& output() const { return output_; }
1362 InstructionOperand& output() { return output_; }
1363
1364 private:
1365 const int virtual_register_;
1366 InstructionOperand output_;
1367 IntVector operands_;
1368 };
1369
1370
1371 // Analogue of BasicBlock for Instructions instead of Nodes.
1372 class V8_EXPORT_PRIVATE InstructionBlock final
NON_EXPORTED_BASE(ZoneObject)1373 : public NON_EXPORTED_BASE(ZoneObject) {
1374 public:
1375 InstructionBlock(Zone* zone, RpoNumber rpo_number, RpoNumber loop_header,
1376 RpoNumber loop_end, bool deferred, bool handler);
1377
1378 // Instruction indexes (used by the register allocator).
1379 int first_instruction_index() const {
1380 DCHECK_LE(0, code_start_);
1381 DCHECK_LT(0, code_end_);
1382 DCHECK_GE(code_end_, code_start_);
1383 return code_start_;
1384 }
1385 int last_instruction_index() const {
1386 DCHECK_LE(0, code_start_);
1387 DCHECK_LT(0, code_end_);
1388 DCHECK_GE(code_end_, code_start_);
1389 return code_end_ - 1;
1390 }
1391
1392 int32_t code_start() const { return code_start_; }
1393 void set_code_start(int32_t start) { code_start_ = start; }
1394
1395 int32_t code_end() const { return code_end_; }
1396 void set_code_end(int32_t end) { code_end_ = end; }
1397
1398 bool IsDeferred() const { return deferred_; }
1399 bool IsHandler() const { return handler_; }
1400
1401 RpoNumber ao_number() const { return ao_number_; }
1402 RpoNumber rpo_number() const { return rpo_number_; }
1403 RpoNumber loop_header() const { return loop_header_; }
1404 RpoNumber loop_end() const {
1405 DCHECK(IsLoopHeader());
1406 return loop_end_;
1407 }
1408 inline bool IsLoopHeader() const { return loop_end_.IsValid(); }
1409
1410 typedef ZoneVector<RpoNumber> Predecessors;
1411 Predecessors& predecessors() { return predecessors_; }
1412 const Predecessors& predecessors() const { return predecessors_; }
1413 size_t PredecessorCount() const { return predecessors_.size(); }
1414 size_t PredecessorIndexOf(RpoNumber rpo_number) const;
1415
1416 typedef ZoneVector<RpoNumber> Successors;
1417 Successors& successors() { return successors_; }
1418 const Successors& successors() const { return successors_; }
1419 size_t SuccessorCount() const { return successors_.size(); }
1420
1421 typedef ZoneVector<PhiInstruction*> PhiInstructions;
1422 const PhiInstructions& phis() const { return phis_; }
1423 PhiInstruction* PhiAt(size_t i) const { return phis_[i]; }
1424 void AddPhi(PhiInstruction* phi) { phis_.push_back(phi); }
1425
1426 void set_ao_number(RpoNumber ao_number) { ao_number_ = ao_number; }
1427
1428 bool needs_frame() const { return needs_frame_; }
1429 void mark_needs_frame() { needs_frame_ = true; }
1430
1431 bool must_construct_frame() const { return must_construct_frame_; }
1432 void mark_must_construct_frame() { must_construct_frame_ = true; }
1433
1434 bool must_deconstruct_frame() const { return must_deconstruct_frame_; }
1435 void mark_must_deconstruct_frame() { must_deconstruct_frame_ = true; }
1436
1437 private:
1438 Successors successors_;
1439 Predecessors predecessors_;
1440 PhiInstructions phis_;
1441 RpoNumber ao_number_; // Assembly order number.
1442 const RpoNumber rpo_number_;
1443 const RpoNumber loop_header_;
1444 const RpoNumber loop_end_;
1445 int32_t code_start_; // start index of arch-specific code.
1446 int32_t code_end_; // end index of arch-specific code.
1447 const bool deferred_; // Block contains deferred code.
1448 const bool handler_; // Block is a handler entry point.
1449 bool needs_frame_;
1450 bool must_construct_frame_;
1451 bool must_deconstruct_frame_;
1452 };
1453
1454 class InstructionSequence;
1455
1456 struct PrintableInstructionBlock {
1457 const RegisterConfiguration* register_configuration_;
1458 const InstructionBlock* block_;
1459 const InstructionSequence* code_;
1460 };
1461
1462 std::ostream& operator<<(std::ostream& os,
1463 const PrintableInstructionBlock& printable_block);
1464
1465 typedef ZoneDeque<Constant> ConstantDeque;
1466 typedef std::map<int, Constant, std::less<int>,
1467 ZoneAllocator<std::pair<const int, Constant> > >
1468 ConstantMap;
1469
1470 typedef ZoneDeque<Instruction*> InstructionDeque;
1471 typedef ZoneDeque<ReferenceMap*> ReferenceMapDeque;
1472 typedef ZoneVector<InstructionBlock*> InstructionBlocks;
1473
1474
1475 // Forward declarations.
1476 struct PrintableInstructionSequence;
1477
1478
1479 // Represents architecture-specific generated code before, during, and after
1480 // register allocation.
1481 class V8_EXPORT_PRIVATE InstructionSequence final
NON_EXPORTED_BASE(ZoneObject)1482 : public NON_EXPORTED_BASE(ZoneObject) {
1483 public:
1484 static InstructionBlocks* InstructionBlocksFor(Zone* zone,
1485 const Schedule* schedule);
1486 // Puts the deferred blocks last.
1487 static void ComputeAssemblyOrder(InstructionBlocks* blocks);
1488
1489 InstructionSequence(Isolate* isolate, Zone* zone,
1490 InstructionBlocks* instruction_blocks);
1491
1492 int NextVirtualRegister();
1493 int VirtualRegisterCount() const { return next_virtual_register_; }
1494
1495 const InstructionBlocks& instruction_blocks() const {
1496 return *instruction_blocks_;
1497 }
1498
1499 int InstructionBlockCount() const {
1500 return static_cast<int>(instruction_blocks_->size());
1501 }
1502
1503 InstructionBlock* InstructionBlockAt(RpoNumber rpo_number) {
1504 return instruction_blocks_->at(rpo_number.ToSize());
1505 }
1506
1507 int LastLoopInstructionIndex(const InstructionBlock* block) {
1508 return instruction_blocks_->at(block->loop_end().ToSize() - 1)
1509 ->last_instruction_index();
1510 }
1511
1512 const InstructionBlock* InstructionBlockAt(RpoNumber rpo_number) const {
1513 return instruction_blocks_->at(rpo_number.ToSize());
1514 }
1515
1516 InstructionBlock* GetInstructionBlock(int instruction_index) const;
1517
1518 static MachineRepresentation DefaultRepresentation() {
1519 return MachineType::PointerRepresentation();
1520 }
1521 MachineRepresentation GetRepresentation(int virtual_register) const;
1522 void MarkAsRepresentation(MachineRepresentation rep, int virtual_register);
1523 int representation_mask() const { return representation_mask_; }
1524
1525 bool IsReference(int virtual_register) const {
1526 return CanBeTaggedPointer(GetRepresentation(virtual_register));
1527 }
1528 bool IsFP(int virtual_register) const {
1529 return IsFloatingPoint(GetRepresentation(virtual_register));
1530 }
1531
1532 Instruction* GetBlockStart(RpoNumber rpo) const;
1533
1534 typedef InstructionDeque::const_iterator const_iterator;
1535 const_iterator begin() const { return instructions_.begin(); }
1536 const_iterator end() const { return instructions_.end(); }
1537 const InstructionDeque& instructions() const { return instructions_; }
1538 int LastInstructionIndex() const {
1539 return static_cast<int>(instructions().size()) - 1;
1540 }
1541
1542 Instruction* InstructionAt(int index) const {
1543 DCHECK_LE(0, index);
1544 DCHECK_GT(instructions_.size(), index);
1545 return instructions_[index];
1546 }
1547
1548 Isolate* isolate() const { return isolate_; }
1549 const ReferenceMapDeque* reference_maps() const { return &reference_maps_; }
1550 Zone* zone() const { return zone_; }
1551
1552 // Used by the instruction selector while adding instructions.
1553 int AddInstruction(Instruction* instr);
1554 void StartBlock(RpoNumber rpo);
1555 void EndBlock(RpoNumber rpo);
1556
1557 int AddConstant(int virtual_register, Constant constant) {
1558 // TODO(titzer): allow RPO numbers as constants?
1559 DCHECK_NE(Constant::kRpoNumber, constant.type());
1560 DCHECK(virtual_register >= 0 && virtual_register < next_virtual_register_);
1561 DCHECK(constants_.find(virtual_register) == constants_.end());
1562 constants_.insert(std::make_pair(virtual_register, constant));
1563 return virtual_register;
1564 }
1565 Constant GetConstant(int virtual_register) const {
1566 ConstantMap::const_iterator it = constants_.find(virtual_register);
1567 DCHECK(it != constants_.end());
1568 DCHECK_EQ(virtual_register, it->first);
1569 return it->second;
1570 }
1571
1572 typedef ZoneVector<Constant> Immediates;
1573 Immediates& immediates() { return immediates_; }
1574
1575 ImmediateOperand AddImmediate(const Constant& constant) {
1576 if (constant.type() == Constant::kInt32 &&
1577 RelocInfo::IsNone(constant.rmode())) {
1578 return ImmediateOperand(ImmediateOperand::INLINE, constant.ToInt32());
1579 }
1580 int index = static_cast<int>(immediates_.size());
1581 immediates_.push_back(constant);
1582 return ImmediateOperand(ImmediateOperand::INDEXED, index);
1583 }
1584
1585 Constant GetImmediate(const ImmediateOperand* op) const {
1586 switch (op->type()) {
1587 case ImmediateOperand::INLINE:
1588 return Constant(op->inline_value());
1589 case ImmediateOperand::INDEXED: {
1590 int index = op->indexed_value();
1591 DCHECK_LE(0, index);
1592 DCHECK_GT(immediates_.size(), index);
1593 return immediates_[index];
1594 }
1595 }
1596 UNREACHABLE();
1597 }
1598
1599 int AddDeoptimizationEntry(FrameStateDescriptor* descriptor,
1600 DeoptimizeKind kind, DeoptimizeReason reason,
1601 VectorSlotPair const& feedback);
1602 DeoptimizationEntry const& GetDeoptimizationEntry(int deoptimization_id);
1603 int GetDeoptimizationEntryCount() const {
1604 return static_cast<int>(deoptimization_entries_.size());
1605 }
1606
1607 RpoNumber InputRpo(Instruction* instr, size_t index);
1608
1609 bool GetSourcePosition(const Instruction* instr,
1610 SourcePosition* result) const;
1611 void SetSourcePosition(const Instruction* instr, SourcePosition value);
1612
1613 bool ContainsCall() const {
1614 for (Instruction* instr : instructions_) {
1615 if (instr->IsCall()) return true;
1616 }
1617 return false;
1618 }
1619
1620 // APIs to aid debugging. For general-stream APIs, use operator<<
1621 void Print(const RegisterConfiguration* config) const;
1622 void Print() const;
1623
1624 void PrintBlock(const RegisterConfiguration* config, int block_id) const;
1625 void PrintBlock(int block_id) const;
1626
1627 void ValidateEdgeSplitForm() const;
1628 void ValidateDeferredBlockExitPaths() const;
1629 void ValidateDeferredBlockEntryPaths() const;
1630 void ValidateSSA() const;
1631
1632 static void SetRegisterConfigurationForTesting(
1633 const RegisterConfiguration* regConfig);
1634 static void ClearRegisterConfigurationForTesting();
1635
1636 private:
1637 friend V8_EXPORT_PRIVATE std::ostream& operator<<(
1638 std::ostream& os, const PrintableInstructionSequence& code);
1639
1640 typedef ZoneMap<const Instruction*, SourcePosition> SourcePositionMap;
1641
1642 static const RegisterConfiguration* RegisterConfigurationForTesting();
1643 static const RegisterConfiguration* registerConfigurationForTesting_;
1644
1645 Isolate* isolate_;
1646 Zone* const zone_;
1647 InstructionBlocks* const instruction_blocks_;
1648 SourcePositionMap source_positions_;
1649 ConstantMap constants_;
1650 Immediates immediates_;
1651 InstructionDeque instructions_;
1652 int next_virtual_register_;
1653 ReferenceMapDeque reference_maps_;
1654 ZoneVector<MachineRepresentation> representations_;
1655 int representation_mask_;
1656 DeoptimizationVector deoptimization_entries_;
1657
1658 // Used at construction time
1659 InstructionBlock* current_block_;
1660
1661 DISALLOW_COPY_AND_ASSIGN(InstructionSequence);
1662 };
1663
1664
1665 struct PrintableInstructionSequence {
1666 const RegisterConfiguration* register_configuration_;
1667 const InstructionSequence* sequence_;
1668 };
1669
1670 V8_EXPORT_PRIVATE std::ostream& operator<<(
1671 std::ostream& os, const PrintableInstructionSequence& code);
1672
1673 } // namespace compiler
1674 } // namespace internal
1675 } // namespace v8
1676
1677 #endif // V8_COMPILER_INSTRUCTION_H_
1678