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