• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <map>
10 #include <set>
11 
12 #include "src/compiler/common-operator.h"
13 #include "src/compiler/frame.h"
14 #include "src/compiler/graph.h"
15 #include "src/compiler/instruction-codes.h"
16 #include "src/compiler/opcodes.h"
17 #include "src/compiler/schedule.h"
18 // TODO(titzer): don't include the macro-assembler?
19 #include "src/macro-assembler.h"
20 #include "src/zone-allocator.h"
21 
22 namespace v8 {
23 namespace internal {
24 
25 // Forward declarations.
26 class OStream;
27 
28 namespace compiler {
29 
30 // Forward declarations.
31 class Linkage;
32 
33 // A couple of reserved opcodes are used for internal use.
34 const InstructionCode kGapInstruction = -1;
35 const InstructionCode kBlockStartInstruction = -2;
36 const InstructionCode kSourcePositionInstruction = -3;
37 
38 
39 #define INSTRUCTION_OPERAND_LIST(V)              \
40   V(Constant, CONSTANT, 128)                     \
41   V(Immediate, IMMEDIATE, 128)                   \
42   V(StackSlot, STACK_SLOT, 128)                  \
43   V(DoubleStackSlot, DOUBLE_STACK_SLOT, 128)     \
44   V(Register, REGISTER, Register::kNumRegisters) \
45   V(DoubleRegister, DOUBLE_REGISTER, DoubleRegister::kMaxNumRegisters)
46 
47 class InstructionOperand : public ZoneObject {
48  public:
49   enum Kind {
50     INVALID,
51     UNALLOCATED,
52     CONSTANT,
53     IMMEDIATE,
54     STACK_SLOT,
55     DOUBLE_STACK_SLOT,
56     REGISTER,
57     DOUBLE_REGISTER
58   };
59 
InstructionOperand()60   InstructionOperand() : value_(KindField::encode(INVALID)) {}
InstructionOperand(Kind kind,int index)61   InstructionOperand(Kind kind, int index) { ConvertTo(kind, index); }
62 
kind()63   Kind kind() const { return KindField::decode(value_); }
index()64   int index() const { return static_cast<int>(value_) >> KindField::kSize; }
65 #define INSTRUCTION_OPERAND_PREDICATE(name, type, number) \
66   bool Is##name() const { return kind() == type; }
67   INSTRUCTION_OPERAND_LIST(INSTRUCTION_OPERAND_PREDICATE)
68   INSTRUCTION_OPERAND_PREDICATE(Unallocated, UNALLOCATED, 0)
69   INSTRUCTION_OPERAND_PREDICATE(Ignored, INVALID, 0)
70 #undef INSTRUCTION_OPERAND_PREDICATE
Equals(InstructionOperand * other)71   bool Equals(InstructionOperand* other) const {
72     return value_ == other->value_;
73   }
74 
ConvertTo(Kind kind,int index)75   void ConvertTo(Kind kind, int index) {
76     if (kind == REGISTER || kind == DOUBLE_REGISTER) DCHECK(index >= 0);
77     value_ = KindField::encode(kind);
78     value_ |= index << KindField::kSize;
79     DCHECK(this->index() == index);
80   }
81 
82   // Calls SetUpCache()/TearDownCache() for each subclass.
83   static void SetUpCaches();
84   static void TearDownCaches();
85 
86  protected:
87   typedef BitField<Kind, 0, 3> KindField;
88 
89   unsigned value_;
90 };
91 
92 typedef ZoneVector<InstructionOperand*> InstructionOperandVector;
93 
94 OStream& operator<<(OStream& os, const InstructionOperand& op);
95 
96 class UnallocatedOperand : public InstructionOperand {
97  public:
98   enum BasicPolicy { FIXED_SLOT, EXTENDED_POLICY };
99 
100   enum ExtendedPolicy {
101     NONE,
102     ANY,
103     FIXED_REGISTER,
104     FIXED_DOUBLE_REGISTER,
105     MUST_HAVE_REGISTER,
106     SAME_AS_FIRST_INPUT
107   };
108 
109   // Lifetime of operand inside the instruction.
110   enum Lifetime {
111     // USED_AT_START operand is guaranteed to be live only at
112     // instruction start. Register allocator is free to assign the same register
113     // to some other operand used inside instruction (i.e. temporary or
114     // output).
115     USED_AT_START,
116 
117     // USED_AT_END operand is treated as live until the end of
118     // instruction. This means that register allocator will not reuse it's
119     // register for any other operand inside instruction.
120     USED_AT_END
121   };
122 
UnallocatedOperand(ExtendedPolicy policy)123   explicit UnallocatedOperand(ExtendedPolicy policy)
124       : InstructionOperand(UNALLOCATED, 0) {
125     value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
126     value_ |= ExtendedPolicyField::encode(policy);
127     value_ |= LifetimeField::encode(USED_AT_END);
128   }
129 
UnallocatedOperand(BasicPolicy policy,int index)130   UnallocatedOperand(BasicPolicy policy, int index)
131       : InstructionOperand(UNALLOCATED, 0) {
132     DCHECK(policy == FIXED_SLOT);
133     value_ |= BasicPolicyField::encode(policy);
134     value_ |= index << FixedSlotIndexField::kShift;
135     DCHECK(this->fixed_slot_index() == index);
136   }
137 
UnallocatedOperand(ExtendedPolicy policy,int index)138   UnallocatedOperand(ExtendedPolicy policy, int index)
139       : InstructionOperand(UNALLOCATED, 0) {
140     DCHECK(policy == FIXED_REGISTER || policy == FIXED_DOUBLE_REGISTER);
141     value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
142     value_ |= ExtendedPolicyField::encode(policy);
143     value_ |= LifetimeField::encode(USED_AT_END);
144     value_ |= FixedRegisterField::encode(index);
145   }
146 
UnallocatedOperand(ExtendedPolicy policy,Lifetime lifetime)147   UnallocatedOperand(ExtendedPolicy policy, Lifetime lifetime)
148       : InstructionOperand(UNALLOCATED, 0) {
149     value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
150     value_ |= ExtendedPolicyField::encode(policy);
151     value_ |= LifetimeField::encode(lifetime);
152   }
153 
CopyUnconstrained(Zone * zone)154   UnallocatedOperand* CopyUnconstrained(Zone* zone) {
155     UnallocatedOperand* result = new (zone) UnallocatedOperand(ANY);
156     result->set_virtual_register(virtual_register());
157     return result;
158   }
159 
cast(const InstructionOperand * op)160   static const UnallocatedOperand* cast(const InstructionOperand* op) {
161     DCHECK(op->IsUnallocated());
162     return static_cast<const UnallocatedOperand*>(op);
163   }
164 
cast(InstructionOperand * op)165   static UnallocatedOperand* cast(InstructionOperand* op) {
166     DCHECK(op->IsUnallocated());
167     return static_cast<UnallocatedOperand*>(op);
168   }
169 
170   // The encoding used for UnallocatedOperand operands depends on the policy
171   // that is
172   // stored within the operand. The FIXED_SLOT policy uses a compact encoding
173   // because it accommodates a larger pay-load.
174   //
175   // For FIXED_SLOT policy:
176   //     +------------------------------------------+
177   //     |       slot_index      |  vreg  | 0 | 001 |
178   //     +------------------------------------------+
179   //
180   // For all other (extended) policies:
181   //     +------------------------------------------+
182   //     |  reg_index  | L | PPP |  vreg  | 1 | 001 |    L ... Lifetime
183   //     +------------------------------------------+    P ... Policy
184   //
185   // The slot index is a signed value which requires us to decode it manually
186   // instead of using the BitField utility class.
187 
188   // The superclass has a KindField.
189   STATIC_ASSERT(KindField::kSize == 3);
190 
191   // BitFields for all unallocated operands.
192   class BasicPolicyField : public BitField<BasicPolicy, 3, 1> {};
193   class VirtualRegisterField : public BitField<unsigned, 4, 18> {};
194 
195   // BitFields specific to BasicPolicy::FIXED_SLOT.
196   class FixedSlotIndexField : public BitField<int, 22, 10> {};
197 
198   // BitFields specific to BasicPolicy::EXTENDED_POLICY.
199   class ExtendedPolicyField : public BitField<ExtendedPolicy, 22, 3> {};
200   class LifetimeField : public BitField<Lifetime, 25, 1> {};
201   class FixedRegisterField : public BitField<int, 26, 6> {};
202 
203   static const int kMaxVirtualRegisters = VirtualRegisterField::kMax + 1;
204   static const int kFixedSlotIndexWidth = FixedSlotIndexField::kSize;
205   static const int kMaxFixedSlotIndex = (1 << (kFixedSlotIndexWidth - 1)) - 1;
206   static const int kMinFixedSlotIndex = -(1 << (kFixedSlotIndexWidth - 1));
207 
208   // Predicates for the operand policy.
HasAnyPolicy()209   bool HasAnyPolicy() const {
210     return basic_policy() == EXTENDED_POLICY && extended_policy() == ANY;
211   }
HasFixedPolicy()212   bool HasFixedPolicy() const {
213     return basic_policy() == FIXED_SLOT ||
214            extended_policy() == FIXED_REGISTER ||
215            extended_policy() == FIXED_DOUBLE_REGISTER;
216   }
HasRegisterPolicy()217   bool HasRegisterPolicy() const {
218     return basic_policy() == EXTENDED_POLICY &&
219            extended_policy() == MUST_HAVE_REGISTER;
220   }
HasSameAsInputPolicy()221   bool HasSameAsInputPolicy() const {
222     return basic_policy() == EXTENDED_POLICY &&
223            extended_policy() == SAME_AS_FIRST_INPUT;
224   }
HasFixedSlotPolicy()225   bool HasFixedSlotPolicy() const { return basic_policy() == FIXED_SLOT; }
HasFixedRegisterPolicy()226   bool HasFixedRegisterPolicy() const {
227     return basic_policy() == EXTENDED_POLICY &&
228            extended_policy() == FIXED_REGISTER;
229   }
HasFixedDoubleRegisterPolicy()230   bool HasFixedDoubleRegisterPolicy() const {
231     return basic_policy() == EXTENDED_POLICY &&
232            extended_policy() == FIXED_DOUBLE_REGISTER;
233   }
234 
235   // [basic_policy]: Distinguish between FIXED_SLOT and all other policies.
basic_policy()236   BasicPolicy basic_policy() const { return BasicPolicyField::decode(value_); }
237 
238   // [extended_policy]: Only for non-FIXED_SLOT. The finer-grained policy.
extended_policy()239   ExtendedPolicy extended_policy() const {
240     DCHECK(basic_policy() == EXTENDED_POLICY);
241     return ExtendedPolicyField::decode(value_);
242   }
243 
244   // [fixed_slot_index]: Only for FIXED_SLOT.
fixed_slot_index()245   int fixed_slot_index() const {
246     DCHECK(HasFixedSlotPolicy());
247     return static_cast<int>(value_) >> FixedSlotIndexField::kShift;
248   }
249 
250   // [fixed_register_index]: Only for FIXED_REGISTER or FIXED_DOUBLE_REGISTER.
fixed_register_index()251   int fixed_register_index() const {
252     DCHECK(HasFixedRegisterPolicy() || HasFixedDoubleRegisterPolicy());
253     return FixedRegisterField::decode(value_);
254   }
255 
256   // [virtual_register]: The virtual register ID for this operand.
virtual_register()257   int virtual_register() const { return VirtualRegisterField::decode(value_); }
set_virtual_register(unsigned id)258   void set_virtual_register(unsigned id) {
259     value_ = VirtualRegisterField::update(value_, id);
260   }
261 
262   // [lifetime]: Only for non-FIXED_SLOT.
IsUsedAtStart()263   bool IsUsedAtStart() {
264     DCHECK(basic_policy() == EXTENDED_POLICY);
265     return LifetimeField::decode(value_) == USED_AT_START;
266   }
267 };
268 
269 
270 class MoveOperands FINAL {
271  public:
MoveOperands(InstructionOperand * source,InstructionOperand * destination)272   MoveOperands(InstructionOperand* source, InstructionOperand* destination)
273       : source_(source), destination_(destination) {}
274 
source()275   InstructionOperand* source() const { return source_; }
set_source(InstructionOperand * operand)276   void set_source(InstructionOperand* operand) { source_ = operand; }
277 
destination()278   InstructionOperand* destination() const { return destination_; }
set_destination(InstructionOperand * operand)279   void set_destination(InstructionOperand* operand) { destination_ = operand; }
280 
281   // The gap resolver marks moves as "in-progress" by clearing the
282   // destination (but not the source).
IsPending()283   bool IsPending() const { return destination_ == NULL && source_ != NULL; }
284 
285   // True if this move a move into the given destination operand.
Blocks(InstructionOperand * operand)286   bool Blocks(InstructionOperand* operand) const {
287     return !IsEliminated() && source()->Equals(operand);
288   }
289 
290   // A move is redundant if it's been eliminated, if its source and
291   // destination are the same, or if its destination is unneeded or constant.
IsRedundant()292   bool IsRedundant() const {
293     return IsEliminated() || source_->Equals(destination_) || IsIgnored() ||
294            (destination_ != NULL && destination_->IsConstant());
295   }
296 
IsIgnored()297   bool IsIgnored() const {
298     return destination_ != NULL && destination_->IsIgnored();
299   }
300 
301   // We clear both operands to indicate move that's been eliminated.
Eliminate()302   void Eliminate() { source_ = destination_ = NULL; }
IsEliminated()303   bool IsEliminated() const {
304     DCHECK(source_ != NULL || destination_ == NULL);
305     return source_ == NULL;
306   }
307 
308  private:
309   InstructionOperand* source_;
310   InstructionOperand* destination_;
311 };
312 
313 OStream& operator<<(OStream& os, const MoveOperands& mo);
314 
315 template <InstructionOperand::Kind kOperandKind, int kNumCachedOperands>
316 class SubKindOperand FINAL : public InstructionOperand {
317  public:
Create(int index,Zone * zone)318   static SubKindOperand* Create(int index, Zone* zone) {
319     DCHECK(index >= 0);
320     if (index < kNumCachedOperands) return &cache[index];
321     return new (zone) SubKindOperand(index);
322   }
323 
cast(InstructionOperand * op)324   static SubKindOperand* cast(InstructionOperand* op) {
325     DCHECK(op->kind() == kOperandKind);
326     return reinterpret_cast<SubKindOperand*>(op);
327   }
328 
329   static void SetUpCache();
330   static void TearDownCache();
331 
332  private:
333   static SubKindOperand* cache;
334 
SubKindOperand()335   SubKindOperand() : InstructionOperand() {}
SubKindOperand(int index)336   explicit SubKindOperand(int index)
337       : InstructionOperand(kOperandKind, index) {}
338 };
339 
340 
341 #define INSTRUCTION_TYPEDEF_SUBKIND_OPERAND_CLASS(name, type, number) \
342   typedef SubKindOperand<InstructionOperand::type, number> name##Operand;
INSTRUCTION_OPERAND_LIST(INSTRUCTION_TYPEDEF_SUBKIND_OPERAND_CLASS)343 INSTRUCTION_OPERAND_LIST(INSTRUCTION_TYPEDEF_SUBKIND_OPERAND_CLASS)
344 #undef INSTRUCTION_TYPEDEF_SUBKIND_OPERAND_CLASS
345 
346 
347 class ParallelMove FINAL : public ZoneObject {
348  public:
349   explicit ParallelMove(Zone* zone) : move_operands_(4, zone) {}
350 
351   void AddMove(InstructionOperand* from, InstructionOperand* to, Zone* zone) {
352     move_operands_.Add(MoveOperands(from, to), zone);
353   }
354 
355   bool IsRedundant() const;
356 
357   ZoneList<MoveOperands>* move_operands() { return &move_operands_; }
358   const ZoneList<MoveOperands>* move_operands() const {
359     return &move_operands_;
360   }
361 
362  private:
363   ZoneList<MoveOperands> move_operands_;
364 };
365 
366 OStream& operator<<(OStream& os, const ParallelMove& pm);
367 
368 class PointerMap FINAL : public ZoneObject {
369  public:
PointerMap(Zone * zone)370   explicit PointerMap(Zone* zone)
371       : pointer_operands_(8, zone),
372         untagged_operands_(0, zone),
373         instruction_position_(-1) {}
374 
GetNormalizedOperands()375   const ZoneList<InstructionOperand*>* GetNormalizedOperands() {
376     for (int i = 0; i < untagged_operands_.length(); ++i) {
377       RemovePointer(untagged_operands_[i]);
378     }
379     untagged_operands_.Clear();
380     return &pointer_operands_;
381   }
instruction_position()382   int instruction_position() const { return instruction_position_; }
383 
set_instruction_position(int pos)384   void set_instruction_position(int pos) {
385     DCHECK(instruction_position_ == -1);
386     instruction_position_ = pos;
387   }
388 
389   void RecordPointer(InstructionOperand* op, Zone* zone);
390   void RemovePointer(InstructionOperand* op);
391   void RecordUntagged(InstructionOperand* op, Zone* zone);
392 
393  private:
394   friend OStream& operator<<(OStream& os, const PointerMap& pm);
395 
396   ZoneList<InstructionOperand*> pointer_operands_;
397   ZoneList<InstructionOperand*> untagged_operands_;
398   int instruction_position_;
399 };
400 
401 OStream& operator<<(OStream& os, const PointerMap& pm);
402 
403 // TODO(titzer): s/PointerMap/ReferenceMap/
404 class Instruction : public ZoneObject {
405  public:
OutputCount()406   size_t OutputCount() const { return OutputCountField::decode(bit_field_); }
OutputAt(size_t i)407   InstructionOperand* OutputAt(size_t i) const {
408     DCHECK(i < OutputCount());
409     return operands_[i];
410   }
411 
HasOutput()412   bool HasOutput() const { return OutputCount() == 1; }
Output()413   InstructionOperand* Output() const { return OutputAt(0); }
414 
InputCount()415   size_t InputCount() const { return InputCountField::decode(bit_field_); }
InputAt(size_t i)416   InstructionOperand* InputAt(size_t i) const {
417     DCHECK(i < InputCount());
418     return operands_[OutputCount() + i];
419   }
420 
TempCount()421   size_t TempCount() const { return TempCountField::decode(bit_field_); }
TempAt(size_t i)422   InstructionOperand* TempAt(size_t i) const {
423     DCHECK(i < TempCount());
424     return operands_[OutputCount() + InputCount() + i];
425   }
426 
opcode()427   InstructionCode opcode() const { return opcode_; }
arch_opcode()428   ArchOpcode arch_opcode() const { return ArchOpcodeField::decode(opcode()); }
addressing_mode()429   AddressingMode addressing_mode() const {
430     return AddressingModeField::decode(opcode());
431   }
flags_mode()432   FlagsMode flags_mode() const { return FlagsModeField::decode(opcode()); }
flags_condition()433   FlagsCondition flags_condition() const {
434     return FlagsConditionField::decode(opcode());
435   }
436 
437   // TODO(titzer): make control and call into flags.
New(Zone * zone,InstructionCode opcode)438   static Instruction* New(Zone* zone, InstructionCode opcode) {
439     return New(zone, opcode, 0, NULL, 0, NULL, 0, NULL);
440   }
441 
New(Zone * zone,InstructionCode opcode,size_t output_count,InstructionOperand ** outputs,size_t input_count,InstructionOperand ** inputs,size_t temp_count,InstructionOperand ** temps)442   static Instruction* New(Zone* zone, InstructionCode opcode,
443                           size_t output_count, InstructionOperand** outputs,
444                           size_t input_count, InstructionOperand** inputs,
445                           size_t temp_count, InstructionOperand** temps) {
446     DCHECK(opcode >= 0);
447     DCHECK(output_count == 0 || outputs != NULL);
448     DCHECK(input_count == 0 || inputs != NULL);
449     DCHECK(temp_count == 0 || temps != NULL);
450     InstructionOperand* none = NULL;
451     USE(none);
452     int size = static_cast<int>(RoundUp(sizeof(Instruction), kPointerSize) +
453                                 (output_count + input_count + temp_count - 1) *
454                                     sizeof(none));
455     return new (zone->New(size)) Instruction(
456         opcode, output_count, outputs, input_count, inputs, temp_count, temps);
457   }
458 
459   // TODO(titzer): another holdover from lithium days; register allocator
460   // should not need to know about control instructions.
MarkAsControl()461   Instruction* MarkAsControl() {
462     bit_field_ = IsControlField::update(bit_field_, true);
463     return this;
464   }
MarkAsCall()465   Instruction* MarkAsCall() {
466     bit_field_ = IsCallField::update(bit_field_, true);
467     return this;
468   }
IsControl()469   bool IsControl() const { return IsControlField::decode(bit_field_); }
IsCall()470   bool IsCall() const { return IsCallField::decode(bit_field_); }
NeedsPointerMap()471   bool NeedsPointerMap() const { return IsCall(); }
HasPointerMap()472   bool HasPointerMap() const { return pointer_map_ != NULL; }
473 
IsGapMoves()474   bool IsGapMoves() const {
475     return opcode() == kGapInstruction || opcode() == kBlockStartInstruction;
476   }
IsBlockStart()477   bool IsBlockStart() const { return opcode() == kBlockStartInstruction; }
IsSourcePosition()478   bool IsSourcePosition() const {
479     return opcode() == kSourcePositionInstruction;
480   }
481 
ClobbersRegisters()482   bool ClobbersRegisters() const { return IsCall(); }
ClobbersTemps()483   bool ClobbersTemps() const { return IsCall(); }
ClobbersDoubleRegisters()484   bool ClobbersDoubleRegisters() const { return IsCall(); }
pointer_map()485   PointerMap* pointer_map() const { return pointer_map_; }
486 
set_pointer_map(PointerMap * map)487   void set_pointer_map(PointerMap* map) {
488     DCHECK(NeedsPointerMap());
489     DCHECK_EQ(NULL, pointer_map_);
490     pointer_map_ = map;
491   }
492 
493   // Placement new operator so that we can smash instructions into
494   // zone-allocated memory.
new(size_t,void * location)495   void* operator new(size_t, void* location) { return location; }
496 
delete(void * pointer,void * location)497   void operator delete(void* pointer, void* location) { UNREACHABLE(); }
498 
499  protected:
Instruction(InstructionCode opcode)500   explicit Instruction(InstructionCode opcode)
501       : opcode_(opcode),
502         bit_field_(OutputCountField::encode(0) | InputCountField::encode(0) |
503                    TempCountField::encode(0) | IsCallField::encode(false) |
504                    IsControlField::encode(false)),
505         pointer_map_(NULL) {}
506 
Instruction(InstructionCode opcode,size_t output_count,InstructionOperand ** outputs,size_t input_count,InstructionOperand ** inputs,size_t temp_count,InstructionOperand ** temps)507   Instruction(InstructionCode opcode, size_t output_count,
508               InstructionOperand** outputs, size_t input_count,
509               InstructionOperand** inputs, size_t temp_count,
510               InstructionOperand** temps)
511       : opcode_(opcode),
512         bit_field_(OutputCountField::encode(output_count) |
513                    InputCountField::encode(input_count) |
514                    TempCountField::encode(temp_count) |
515                    IsCallField::encode(false) | IsControlField::encode(false)),
516         pointer_map_(NULL) {
517     for (size_t i = 0; i < output_count; ++i) {
518       operands_[i] = outputs[i];
519     }
520     for (size_t i = 0; i < input_count; ++i) {
521       operands_[output_count + i] = inputs[i];
522     }
523     for (size_t i = 0; i < temp_count; ++i) {
524       operands_[output_count + input_count + i] = temps[i];
525     }
526   }
527 
528  protected:
529   typedef BitField<size_t, 0, 8> OutputCountField;
530   typedef BitField<size_t, 8, 16> InputCountField;
531   typedef BitField<size_t, 24, 6> TempCountField;
532   typedef BitField<bool, 30, 1> IsCallField;
533   typedef BitField<bool, 31, 1> IsControlField;
534 
535   InstructionCode opcode_;
536   uint32_t bit_field_;
537   PointerMap* pointer_map_;
538   InstructionOperand* operands_[1];
539 };
540 
541 OStream& operator<<(OStream& os, const Instruction& instr);
542 
543 // Represents moves inserted before an instruction due to register allocation.
544 // TODO(titzer): squash GapInstruction back into Instruction, since essentially
545 // every instruction can possibly have moves inserted before it.
546 class GapInstruction : public Instruction {
547  public:
548   enum InnerPosition {
549     BEFORE,
550     START,
551     END,
552     AFTER,
553     FIRST_INNER_POSITION = BEFORE,
554     LAST_INNER_POSITION = AFTER
555   };
556 
GetOrCreateParallelMove(InnerPosition pos,Zone * zone)557   ParallelMove* GetOrCreateParallelMove(InnerPosition pos, Zone* zone) {
558     if (parallel_moves_[pos] == NULL) {
559       parallel_moves_[pos] = new (zone) ParallelMove(zone);
560     }
561     return parallel_moves_[pos];
562   }
563 
GetParallelMove(InnerPosition pos)564   ParallelMove* GetParallelMove(InnerPosition pos) {
565     return parallel_moves_[pos];
566   }
567 
New(Zone * zone)568   static GapInstruction* New(Zone* zone) {
569     void* buffer = zone->New(sizeof(GapInstruction));
570     return new (buffer) GapInstruction(kGapInstruction);
571   }
572 
cast(Instruction * instr)573   static GapInstruction* cast(Instruction* instr) {
574     DCHECK(instr->IsGapMoves());
575     return static_cast<GapInstruction*>(instr);
576   }
577 
cast(const Instruction * instr)578   static const GapInstruction* cast(const Instruction* instr) {
579     DCHECK(instr->IsGapMoves());
580     return static_cast<const GapInstruction*>(instr);
581   }
582 
583  protected:
GapInstruction(InstructionCode opcode)584   explicit GapInstruction(InstructionCode opcode) : Instruction(opcode) {
585     parallel_moves_[BEFORE] = NULL;
586     parallel_moves_[START] = NULL;
587     parallel_moves_[END] = NULL;
588     parallel_moves_[AFTER] = NULL;
589   }
590 
591  private:
592   friend OStream& operator<<(OStream& os, const Instruction& instr);
593   ParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
594 };
595 
596 
597 // This special kind of gap move instruction represents the beginning of a
598 // block of code.
599 // TODO(titzer): move code_start and code_end from BasicBlock to here.
600 class BlockStartInstruction FINAL : public GapInstruction {
601  public:
block()602   BasicBlock* block() const { return block_; }
label()603   Label* label() { return &label_; }
604 
New(Zone * zone,BasicBlock * block)605   static BlockStartInstruction* New(Zone* zone, BasicBlock* block) {
606     void* buffer = zone->New(sizeof(BlockStartInstruction));
607     return new (buffer) BlockStartInstruction(block);
608   }
609 
cast(Instruction * instr)610   static BlockStartInstruction* cast(Instruction* instr) {
611     DCHECK(instr->IsBlockStart());
612     return static_cast<BlockStartInstruction*>(instr);
613   }
614 
615  private:
BlockStartInstruction(BasicBlock * block)616   explicit BlockStartInstruction(BasicBlock* block)
617       : GapInstruction(kBlockStartInstruction), block_(block) {}
618 
619   BasicBlock* block_;
620   Label label_;
621 };
622 
623 
624 class SourcePositionInstruction FINAL : public Instruction {
625  public:
New(Zone * zone,SourcePosition position)626   static SourcePositionInstruction* New(Zone* zone, SourcePosition position) {
627     void* buffer = zone->New(sizeof(SourcePositionInstruction));
628     return new (buffer) SourcePositionInstruction(position);
629   }
630 
source_position()631   SourcePosition source_position() const { return source_position_; }
632 
cast(Instruction * instr)633   static SourcePositionInstruction* cast(Instruction* instr) {
634     DCHECK(instr->IsSourcePosition());
635     return static_cast<SourcePositionInstruction*>(instr);
636   }
637 
cast(const Instruction * instr)638   static const SourcePositionInstruction* cast(const Instruction* instr) {
639     DCHECK(instr->IsSourcePosition());
640     return static_cast<const SourcePositionInstruction*>(instr);
641   }
642 
643  private:
SourcePositionInstruction(SourcePosition source_position)644   explicit SourcePositionInstruction(SourcePosition source_position)
645       : Instruction(kSourcePositionInstruction),
646         source_position_(source_position) {
647     DCHECK(!source_position_.IsInvalid());
648     DCHECK(!source_position_.IsUnknown());
649   }
650 
651   SourcePosition source_position_;
652 };
653 
654 
655 class Constant FINAL {
656  public:
657   enum Type { kInt32, kInt64, kFloat64, kExternalReference, kHeapObject };
658 
Constant(int32_t v)659   explicit Constant(int32_t v) : type_(kInt32), value_(v) {}
Constant(int64_t v)660   explicit Constant(int64_t v) : type_(kInt64), value_(v) {}
Constant(double v)661   explicit Constant(double v) : type_(kFloat64), value_(bit_cast<int64_t>(v)) {}
Constant(ExternalReference ref)662   explicit Constant(ExternalReference ref)
663       : type_(kExternalReference), value_(bit_cast<intptr_t>(ref)) {}
Constant(Handle<HeapObject> obj)664   explicit Constant(Handle<HeapObject> obj)
665       : type_(kHeapObject), value_(bit_cast<intptr_t>(obj)) {}
666 
type()667   Type type() const { return type_; }
668 
ToInt32()669   int32_t ToInt32() const {
670     DCHECK_EQ(kInt32, type());
671     return static_cast<int32_t>(value_);
672   }
673 
ToInt64()674   int64_t ToInt64() const {
675     if (type() == kInt32) return ToInt32();
676     DCHECK_EQ(kInt64, type());
677     return value_;
678   }
679 
ToFloat64()680   double ToFloat64() const {
681     if (type() == kInt32) return ToInt32();
682     DCHECK_EQ(kFloat64, type());
683     return bit_cast<double>(value_);
684   }
685 
ToExternalReference()686   ExternalReference ToExternalReference() const {
687     DCHECK_EQ(kExternalReference, type());
688     return bit_cast<ExternalReference>(static_cast<intptr_t>(value_));
689   }
690 
ToHeapObject()691   Handle<HeapObject> ToHeapObject() const {
692     DCHECK_EQ(kHeapObject, type());
693     return bit_cast<Handle<HeapObject> >(static_cast<intptr_t>(value_));
694   }
695 
696  private:
697   Type type_;
698   int64_t value_;
699 };
700 
701 
702 class FrameStateDescriptor : public ZoneObject {
703  public:
704   FrameStateDescriptor(const FrameStateCallInfo& state_info,
705                        size_t parameters_count, size_t locals_count,
706                        size_t stack_count,
707                        FrameStateDescriptor* outer_state = NULL)
708       : type_(state_info.type()),
709         bailout_id_(state_info.bailout_id()),
710         frame_state_combine_(state_info.state_combine()),
711         parameters_count_(parameters_count),
712         locals_count_(locals_count),
713         stack_count_(stack_count),
714         outer_state_(outer_state),
715         jsfunction_(state_info.jsfunction()) {}
716 
type()717   FrameStateType type() const { return type_; }
bailout_id()718   BailoutId bailout_id() const { return bailout_id_; }
state_combine()719   OutputFrameStateCombine state_combine() const { return frame_state_combine_; }
parameters_count()720   size_t parameters_count() const { return parameters_count_; }
locals_count()721   size_t locals_count() const { return locals_count_; }
stack_count()722   size_t stack_count() const { return stack_count_; }
outer_state()723   FrameStateDescriptor* outer_state() const { return outer_state_; }
jsfunction()724   MaybeHandle<JSFunction> jsfunction() const { return jsfunction_; }
725 
size()726   size_t size() const {
727     return parameters_count_ + locals_count_ + stack_count_ +
728            (HasContext() ? 1 : 0);
729   }
730 
GetTotalSize()731   size_t GetTotalSize() const {
732     size_t total_size = 0;
733     for (const FrameStateDescriptor* iter = this; iter != NULL;
734          iter = iter->outer_state_) {
735       total_size += iter->size();
736     }
737     return total_size;
738   }
739 
GetHeight(OutputFrameStateCombine override)740   size_t GetHeight(OutputFrameStateCombine override) const {
741     size_t height = size() - parameters_count();
742     switch (override) {
743       case kPushOutput:
744         ++height;
745         break;
746       case kIgnoreOutput:
747         break;
748     }
749     return height;
750   }
751 
GetFrameCount()752   size_t GetFrameCount() const {
753     size_t count = 0;
754     for (const FrameStateDescriptor* iter = this; iter != NULL;
755          iter = iter->outer_state_) {
756       ++count;
757     }
758     return count;
759   }
760 
GetJSFrameCount()761   size_t GetJSFrameCount() const {
762     size_t count = 0;
763     for (const FrameStateDescriptor* iter = this; iter != NULL;
764          iter = iter->outer_state_) {
765       if (iter->type_ == JS_FRAME) {
766         ++count;
767       }
768     }
769     return count;
770   }
771 
HasContext()772   bool HasContext() const { return type_ == JS_FRAME; }
773 
774  private:
775   FrameStateType type_;
776   BailoutId bailout_id_;
777   OutputFrameStateCombine frame_state_combine_;
778   size_t parameters_count_;
779   size_t locals_count_;
780   size_t stack_count_;
781   FrameStateDescriptor* outer_state_;
782   MaybeHandle<JSFunction> jsfunction_;
783 };
784 
785 OStream& operator<<(OStream& os, const Constant& constant);
786 
787 typedef ZoneDeque<Constant> ConstantDeque;
788 typedef std::map<int, Constant, std::less<int>,
789                  zone_allocator<std::pair<int, Constant> > > ConstantMap;
790 
791 typedef ZoneDeque<Instruction*> InstructionDeque;
792 typedef ZoneDeque<PointerMap*> PointerMapDeque;
793 typedef ZoneVector<FrameStateDescriptor*> DeoptimizationVector;
794 
795 // Represents architecture-specific generated code before, during, and after
796 // register allocation.
797 // TODO(titzer): s/IsDouble/IsFloat64/
798 class InstructionSequence FINAL {
799  public:
InstructionSequence(Linkage * linkage,Graph * graph,Schedule * schedule)800   InstructionSequence(Linkage* linkage, Graph* graph, Schedule* schedule)
801       : graph_(graph),
802         linkage_(linkage),
803         schedule_(schedule),
804         constants_(ConstantMap::key_compare(),
805                    ConstantMap::allocator_type(zone())),
806         immediates_(zone()),
807         instructions_(zone()),
808         next_virtual_register_(graph->NodeCount()),
809         pointer_maps_(zone()),
810         doubles_(std::less<int>(), VirtualRegisterSet::allocator_type(zone())),
811         references_(std::less<int>(),
812                     VirtualRegisterSet::allocator_type(zone())),
813         deoptimization_entries_(zone()) {}
814 
NextVirtualRegister()815   int NextVirtualRegister() { return next_virtual_register_++; }
VirtualRegisterCount()816   int VirtualRegisterCount() const { return next_virtual_register_; }
817 
ValueCount()818   int ValueCount() const { return graph_->NodeCount(); }
819 
BasicBlockCount()820   int BasicBlockCount() const {
821     return static_cast<int>(schedule_->rpo_order()->size());
822   }
823 
BlockAt(int rpo_number)824   BasicBlock* BlockAt(int rpo_number) const {
825     return (*schedule_->rpo_order())[rpo_number];
826   }
827 
GetContainingLoop(BasicBlock * block)828   BasicBlock* GetContainingLoop(BasicBlock* block) {
829     return block->loop_header_;
830   }
831 
GetLoopEnd(BasicBlock * block)832   int GetLoopEnd(BasicBlock* block) const { return block->loop_end_; }
833 
834   BasicBlock* GetBasicBlock(int instruction_index);
835 
GetVirtualRegister(Node * node)836   int GetVirtualRegister(Node* node) const { return node->id(); }
837 
838   bool IsReference(int virtual_register) const;
839   bool IsDouble(int virtual_register) const;
840 
841   void MarkAsReference(int virtual_register);
842   void MarkAsDouble(int virtual_register);
843 
844   void AddGapMove(int index, InstructionOperand* from, InstructionOperand* to);
845 
846   Label* GetLabel(BasicBlock* block);
847   BlockStartInstruction* GetBlockStart(BasicBlock* block);
848 
849   typedef InstructionDeque::const_iterator const_iterator;
begin()850   const_iterator begin() const { return instructions_.begin(); }
end()851   const_iterator end() const { return instructions_.end(); }
852 
GapAt(int index)853   GapInstruction* GapAt(int index) const {
854     return GapInstruction::cast(InstructionAt(index));
855   }
IsGapAt(int index)856   bool IsGapAt(int index) const { return InstructionAt(index)->IsGapMoves(); }
InstructionAt(int index)857   Instruction* InstructionAt(int index) const {
858     DCHECK(index >= 0);
859     DCHECK(index < static_cast<int>(instructions_.size()));
860     return instructions_[index];
861   }
862 
frame()863   Frame* frame() { return &frame_; }
graph()864   Graph* graph() const { return graph_; }
isolate()865   Isolate* isolate() const { return zone()->isolate(); }
linkage()866   Linkage* linkage() const { return linkage_; }
schedule()867   Schedule* schedule() const { return schedule_; }
pointer_maps()868   const PointerMapDeque* pointer_maps() const { return &pointer_maps_; }
zone()869   Zone* zone() const { return graph_->zone(); }
870 
871   // Used by the code generator while adding instructions.
872   int AddInstruction(Instruction* instr, BasicBlock* block);
873   void StartBlock(BasicBlock* block);
874   void EndBlock(BasicBlock* block);
875 
AddConstant(int virtual_register,Constant constant)876   void AddConstant(int virtual_register, Constant constant) {
877     DCHECK(constants_.find(virtual_register) == constants_.end());
878     constants_.insert(std::make_pair(virtual_register, constant));
879   }
GetConstant(int virtual_register)880   Constant GetConstant(int virtual_register) const {
881     ConstantMap::const_iterator it = constants_.find(virtual_register);
882     DCHECK(it != constants_.end());
883     DCHECK_EQ(virtual_register, it->first);
884     return it->second;
885   }
886 
887   typedef ConstantDeque Immediates;
immediates()888   const Immediates& immediates() const { return immediates_; }
889 
AddImmediate(Constant constant)890   int AddImmediate(Constant constant) {
891     int index = static_cast<int>(immediates_.size());
892     immediates_.push_back(constant);
893     return index;
894   }
GetImmediate(int index)895   Constant GetImmediate(int index) const {
896     DCHECK(index >= 0);
897     DCHECK(index < static_cast<int>(immediates_.size()));
898     return immediates_[index];
899   }
900 
901   class StateId {
902    public:
FromInt(int id)903     static StateId FromInt(int id) { return StateId(id); }
ToInt()904     int ToInt() const { return id_; }
905 
906    private:
StateId(int id)907     explicit StateId(int id) : id_(id) {}
908     int id_;
909   };
910 
911   StateId AddFrameStateDescriptor(FrameStateDescriptor* descriptor);
912   FrameStateDescriptor* GetFrameStateDescriptor(StateId deoptimization_id);
913   int GetFrameStateDescriptorCount();
914 
915  private:
916   friend OStream& operator<<(OStream& os, const InstructionSequence& code);
917 
918   typedef std::set<int, std::less<int>, ZoneIntAllocator> VirtualRegisterSet;
919 
920   Graph* graph_;
921   Linkage* linkage_;
922   Schedule* schedule_;
923   ConstantMap constants_;
924   ConstantDeque immediates_;
925   InstructionDeque instructions_;
926   int next_virtual_register_;
927   PointerMapDeque pointer_maps_;
928   VirtualRegisterSet doubles_;
929   VirtualRegisterSet references_;
930   Frame frame_;
931   DeoptimizationVector deoptimization_entries_;
932 };
933 
934 OStream& operator<<(OStream& os, const InstructionSequence& code);
935 
936 }  // namespace compiler
937 }  // namespace internal
938 }  // namespace v8
939 
940 #endif  // V8_COMPILER_INSTRUCTION_H_
941