• 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_SELECTOR_IMPL_H_
6 #define V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_
7 
8 #include "src/compiler/instruction-selector.h"
9 #include "src/compiler/instruction.h"
10 #include "src/compiler/linkage.h"
11 #include "src/compiler/schedule.h"
12 #include "src/macro-assembler.h"
13 
14 namespace v8 {
15 namespace internal {
16 namespace compiler {
17 
18 // Helper struct containing data about a table or lookup switch.
19 struct SwitchInfo {
20   int32_t min_value;           // minimum value of {case_values}
21   int32_t max_value;           // maximum value of {case_values}
22   size_t value_range;          // |max_value - min_value| + 1
23   size_t case_count;           // number of cases
24   int32_t* case_values;        // actual case values, unsorted
25   BasicBlock** case_branches;  // basic blocks corresponding to case values
26   BasicBlock* default_branch;  // default branch target
27 };
28 
29 // A helper class for the instruction selector that simplifies construction of
30 // Operands. This class implements a base for architecture-specific helpers.
31 class OperandGenerator {
32  public:
OperandGenerator(InstructionSelector * selector)33   explicit OperandGenerator(InstructionSelector* selector)
34       : selector_(selector) {}
35 
NoOutput()36   InstructionOperand NoOutput() {
37     return InstructionOperand();  // Generates an invalid operand.
38   }
39 
DefineAsRegister(Node * node)40   InstructionOperand DefineAsRegister(Node* node) {
41     return Define(node,
42                   UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
43                                      GetVReg(node)));
44   }
45 
DefineSameAsFirst(Node * node)46   InstructionOperand DefineSameAsFirst(Node* node) {
47     return Define(node,
48                   UnallocatedOperand(UnallocatedOperand::SAME_AS_FIRST_INPUT,
49                                      GetVReg(node)));
50   }
51 
DefineAsFixed(Node * node,Register reg)52   InstructionOperand DefineAsFixed(Node* node, Register reg) {
53     return Define(node, UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
54                                            reg.code(), GetVReg(node)));
55   }
56 
57   template <typename FPRegType>
DefineAsFixed(Node * node,FPRegType reg)58   InstructionOperand DefineAsFixed(Node* node, FPRegType reg) {
59     return Define(node,
60                   UnallocatedOperand(UnallocatedOperand::FIXED_FP_REGISTER,
61                                      reg.code(), GetVReg(node)));
62   }
63 
DefineAsConstant(Node * node)64   InstructionOperand DefineAsConstant(Node* node) {
65     return DefineAsConstant(node, ToConstant(node));
66   }
67 
DefineAsConstant(Node * node,Constant constant)68   InstructionOperand DefineAsConstant(Node* node, Constant constant) {
69     selector()->MarkAsDefined(node);
70     int virtual_register = GetVReg(node);
71     sequence()->AddConstant(virtual_register, constant);
72     return ConstantOperand(virtual_register);
73   }
74 
DefineAsLocation(Node * node,LinkageLocation location)75   InstructionOperand DefineAsLocation(Node* node, LinkageLocation location) {
76     return Define(node, ToUnallocatedOperand(location, GetVReg(node)));
77   }
78 
DefineAsDualLocation(Node * node,LinkageLocation primary_location,LinkageLocation secondary_location)79   InstructionOperand DefineAsDualLocation(Node* node,
80                                           LinkageLocation primary_location,
81                                           LinkageLocation secondary_location) {
82     return Define(node,
83                   ToDualLocationUnallocatedOperand(
84                       primary_location, secondary_location, GetVReg(node)));
85   }
86 
Use(Node * node)87   InstructionOperand Use(Node* node) {
88     return Use(node, UnallocatedOperand(UnallocatedOperand::NONE,
89                                         UnallocatedOperand::USED_AT_START,
90                                         GetVReg(node)));
91   }
92 
UseAnyAtEnd(Node * node)93   InstructionOperand UseAnyAtEnd(Node* node) {
94     return Use(node, UnallocatedOperand(UnallocatedOperand::ANY,
95                                         UnallocatedOperand::USED_AT_END,
96                                         GetVReg(node)));
97   }
98 
UseAny(Node * node)99   InstructionOperand UseAny(Node* node) {
100     return Use(node, UnallocatedOperand(UnallocatedOperand::ANY,
101                                         UnallocatedOperand::USED_AT_START,
102                                         GetVReg(node)));
103   }
104 
UseRegister(Node * node)105   InstructionOperand UseRegister(Node* node) {
106     return Use(node, UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
107                                         UnallocatedOperand::USED_AT_START,
108                                         GetVReg(node)));
109   }
110 
UseUniqueSlot(Node * node)111   InstructionOperand UseUniqueSlot(Node* node) {
112     return Use(node, UnallocatedOperand(UnallocatedOperand::MUST_HAVE_SLOT,
113                                         GetVReg(node)));
114   }
115 
116   // Use register or operand for the node. If a register is chosen, it won't
117   // alias any temporary or output registers.
UseUnique(Node * node)118   InstructionOperand UseUnique(Node* node) {
119     return Use(node,
120                UnallocatedOperand(UnallocatedOperand::NONE, GetVReg(node)));
121   }
122 
123   // Use a unique register for the node that does not alias any temporary or
124   // output registers.
UseUniqueRegister(Node * node)125   InstructionOperand UseUniqueRegister(Node* node) {
126     return Use(node, UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
127                                         GetVReg(node)));
128   }
129 
UseFixed(Node * node,Register reg)130   InstructionOperand UseFixed(Node* node, Register reg) {
131     return Use(node, UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
132                                         reg.code(), GetVReg(node)));
133   }
134 
135   template <typename FPRegType>
UseFixed(Node * node,FPRegType reg)136   InstructionOperand UseFixed(Node* node, FPRegType reg) {
137     return Use(node, UnallocatedOperand(UnallocatedOperand::FIXED_FP_REGISTER,
138                                         reg.code(), GetVReg(node)));
139   }
140 
UseExplicit(LinkageLocation location)141   InstructionOperand UseExplicit(LinkageLocation location) {
142     MachineRepresentation rep = InstructionSequence::DefaultRepresentation();
143     if (location.IsRegister()) {
144       return ExplicitOperand(LocationOperand::REGISTER, rep,
145                              location.AsRegister());
146     } else {
147       return ExplicitOperand(LocationOperand::STACK_SLOT, rep,
148                              location.GetLocation());
149     }
150   }
151 
UseImmediate(int immediate)152   InstructionOperand UseImmediate(int immediate) {
153     return sequence()->AddImmediate(Constant(immediate));
154   }
155 
UseImmediate(Node * node)156   InstructionOperand UseImmediate(Node* node) {
157     return sequence()->AddImmediate(ToConstant(node));
158   }
159 
UseNegatedImmediate(Node * node)160   InstructionOperand UseNegatedImmediate(Node* node) {
161     return sequence()->AddImmediate(ToNegatedConstant(node));
162   }
163 
UseLocation(Node * node,LinkageLocation location)164   InstructionOperand UseLocation(Node* node, LinkageLocation location) {
165     return Use(node, ToUnallocatedOperand(location, GetVReg(node)));
166   }
167 
168   // Used to force gap moves from the from_location to the to_location
169   // immediately before an instruction.
UsePointerLocation(LinkageLocation to_location,LinkageLocation from_location)170   InstructionOperand UsePointerLocation(LinkageLocation to_location,
171                                         LinkageLocation from_location) {
172     UnallocatedOperand casted_from_operand =
173         UnallocatedOperand::cast(TempLocation(from_location));
174     selector_->Emit(kArchNop, casted_from_operand);
175     return ToUnallocatedOperand(to_location,
176                                 casted_from_operand.virtual_register());
177   }
178 
TempRegister()179   InstructionOperand TempRegister() {
180     return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
181                               UnallocatedOperand::USED_AT_START,
182                               sequence()->NextVirtualRegister());
183   }
184 
AllocateVirtualRegister()185   int AllocateVirtualRegister() { return sequence()->NextVirtualRegister(); }
186 
DefineSameAsFirstForVreg(int vreg)187   InstructionOperand DefineSameAsFirstForVreg(int vreg) {
188     return UnallocatedOperand(UnallocatedOperand::SAME_AS_FIRST_INPUT, vreg);
189   }
190 
DefineAsRegistertForVreg(int vreg)191   InstructionOperand DefineAsRegistertForVreg(int vreg) {
192     return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg);
193   }
194 
UseRegisterForVreg(int vreg)195   InstructionOperand UseRegisterForVreg(int vreg) {
196     return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
197                               UnallocatedOperand::USED_AT_START, vreg);
198   }
199 
TempDoubleRegister()200   InstructionOperand TempDoubleRegister() {
201     UnallocatedOperand op = UnallocatedOperand(
202         UnallocatedOperand::MUST_HAVE_REGISTER,
203         UnallocatedOperand::USED_AT_START, sequence()->NextVirtualRegister());
204     sequence()->MarkAsRepresentation(MachineRepresentation::kFloat64,
205                                      op.virtual_register());
206     return op;
207   }
208 
TempRegister(Register reg)209   InstructionOperand TempRegister(Register reg) {
210     return UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER, reg.code(),
211                               InstructionOperand::kInvalidVirtualRegister);
212   }
213 
TempImmediate(int32_t imm)214   InstructionOperand TempImmediate(int32_t imm) {
215     return sequence()->AddImmediate(Constant(imm));
216   }
217 
TempLocation(LinkageLocation location)218   InstructionOperand TempLocation(LinkageLocation location) {
219     return ToUnallocatedOperand(location, sequence()->NextVirtualRegister());
220   }
221 
Label(BasicBlock * block)222   InstructionOperand Label(BasicBlock* block) {
223     return sequence()->AddImmediate(
224         Constant(RpoNumber::FromInt(block->rpo_number())));
225   }
226 
227  protected:
selector()228   InstructionSelector* selector() const { return selector_; }
sequence()229   InstructionSequence* sequence() const { return selector()->sequence(); }
zone()230   Zone* zone() const { return selector()->instruction_zone(); }
231 
232  private:
GetVReg(Node * node)233   int GetVReg(Node* node) const { return selector_->GetVirtualRegister(node); }
234 
ToConstant(const Node * node)235   static Constant ToConstant(const Node* node) {
236     switch (node->opcode()) {
237       case IrOpcode::kInt32Constant:
238         return Constant(OpParameter<int32_t>(node));
239       case IrOpcode::kInt64Constant:
240         return Constant(OpParameter<int64_t>(node));
241       case IrOpcode::kFloat32Constant:
242         return Constant(OpParameter<float>(node));
243       case IrOpcode::kRelocatableInt32Constant:
244       case IrOpcode::kRelocatableInt64Constant:
245         return Constant(OpParameter<RelocatablePtrConstantInfo>(node));
246       case IrOpcode::kFloat64Constant:
247       case IrOpcode::kNumberConstant:
248         return Constant(OpParameter<double>(node));
249       case IrOpcode::kExternalConstant:
250       case IrOpcode::kComment:
251         return Constant(OpParameter<ExternalReference>(node));
252       case IrOpcode::kHeapConstant:
253         return Constant(OpParameter<Handle<HeapObject>>(node));
254       default:
255         break;
256     }
257     UNREACHABLE();
258     return Constant(static_cast<int32_t>(0));
259   }
260 
ToNegatedConstant(const Node * node)261   static Constant ToNegatedConstant(const Node* node) {
262     switch (node->opcode()) {
263       case IrOpcode::kInt32Constant:
264         return Constant(-OpParameter<int32_t>(node));
265       case IrOpcode::kInt64Constant:
266         return Constant(-OpParameter<int64_t>(node));
267       default:
268         break;
269     }
270     UNREACHABLE();
271     return Constant(static_cast<int32_t>(0));
272   }
273 
Define(Node * node,UnallocatedOperand operand)274   UnallocatedOperand Define(Node* node, UnallocatedOperand operand) {
275     DCHECK_NOT_NULL(node);
276     DCHECK_EQ(operand.virtual_register(), GetVReg(node));
277     selector()->MarkAsDefined(node);
278     return operand;
279   }
280 
Use(Node * node,UnallocatedOperand operand)281   UnallocatedOperand Use(Node* node, UnallocatedOperand operand) {
282     DCHECK_NOT_NULL(node);
283     DCHECK_EQ(operand.virtual_register(), GetVReg(node));
284     selector()->MarkAsUsed(node);
285     return operand;
286   }
287 
ToDualLocationUnallocatedOperand(LinkageLocation primary_location,LinkageLocation secondary_location,int virtual_register)288   UnallocatedOperand ToDualLocationUnallocatedOperand(
289       LinkageLocation primary_location, LinkageLocation secondary_location,
290       int virtual_register) {
291     // We only support the primary location being a register and the secondary
292     // one a slot.
293     DCHECK(primary_location.IsRegister() &&
294            secondary_location.IsCalleeFrameSlot());
295     int reg_id = primary_location.AsRegister();
296     int slot_id = secondary_location.AsCalleeFrameSlot();
297     return UnallocatedOperand(reg_id, slot_id, virtual_register);
298   }
299 
ToUnallocatedOperand(LinkageLocation location,int virtual_register)300   UnallocatedOperand ToUnallocatedOperand(LinkageLocation location,
301                                           int virtual_register) {
302     if (location.IsAnyRegister()) {
303       // any machine register.
304       return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
305                                 virtual_register);
306     }
307     if (location.IsCallerFrameSlot()) {
308       // a location on the caller frame.
309       return UnallocatedOperand(UnallocatedOperand::FIXED_SLOT,
310                                 location.AsCallerFrameSlot(), virtual_register);
311     }
312     if (location.IsCalleeFrameSlot()) {
313       // a spill location on this (callee) frame.
314       return UnallocatedOperand(UnallocatedOperand::FIXED_SLOT,
315                                 location.AsCalleeFrameSlot(), virtual_register);
316     }
317     // a fixed register.
318     if (IsFloatingPoint(location.GetType().representation())) {
319       return UnallocatedOperand(UnallocatedOperand::FIXED_FP_REGISTER,
320                                 location.AsRegister(), virtual_register);
321     }
322     return UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
323                               location.AsRegister(), virtual_register);
324   }
325 
326   InstructionSelector* selector_;
327 };
328 
329 
330 // The flags continuation is a way to combine a branch or a materialization
331 // of a boolean value with an instruction that sets the flags register.
332 // The whole instruction is treated as a unit by the register allocator, and
333 // thus no spills or moves can be introduced between the flags-setting
334 // instruction and the branch or set it should be combined with.
335 class FlagsContinuation final {
336  public:
FlagsContinuation()337   FlagsContinuation() : mode_(kFlags_none) {}
338 
339   // Creates a new flags continuation from the given condition and true/false
340   // blocks.
FlagsContinuation(FlagsCondition condition,BasicBlock * true_block,BasicBlock * false_block)341   FlagsContinuation(FlagsCondition condition, BasicBlock* true_block,
342                     BasicBlock* false_block)
343       : mode_(kFlags_branch),
344         condition_(condition),
345         true_block_(true_block),
346         false_block_(false_block) {
347     DCHECK_NOT_NULL(true_block);
348     DCHECK_NOT_NULL(false_block);
349   }
350 
351   // Creates a new flags continuation for an eager deoptimization exit.
ForDeoptimize(FlagsCondition condition,DeoptimizeKind kind,DeoptimizeReason reason,Node * frame_state)352   static FlagsContinuation ForDeoptimize(FlagsCondition condition,
353                                          DeoptimizeKind kind,
354                                          DeoptimizeReason reason,
355                                          Node* frame_state) {
356     return FlagsContinuation(condition, kind, reason, frame_state);
357   }
358 
359   // Creates a new flags continuation for a boolean value.
ForSet(FlagsCondition condition,Node * result)360   static FlagsContinuation ForSet(FlagsCondition condition, Node* result) {
361     return FlagsContinuation(condition, result);
362   }
363 
364   // Creates a new flags continuation for a wasm trap.
ForTrap(FlagsCondition condition,Runtime::FunctionId trap_id,Node * result)365   static FlagsContinuation ForTrap(FlagsCondition condition,
366                                    Runtime::FunctionId trap_id, Node* result) {
367     return FlagsContinuation(condition, trap_id, result);
368   }
369 
IsNone()370   bool IsNone() const { return mode_ == kFlags_none; }
IsBranch()371   bool IsBranch() const { return mode_ == kFlags_branch; }
IsDeoptimize()372   bool IsDeoptimize() const { return mode_ == kFlags_deoptimize; }
IsSet()373   bool IsSet() const { return mode_ == kFlags_set; }
IsTrap()374   bool IsTrap() const { return mode_ == kFlags_trap; }
condition()375   FlagsCondition condition() const {
376     DCHECK(!IsNone());
377     return condition_;
378   }
kind()379   DeoptimizeKind kind() const {
380     DCHECK(IsDeoptimize());
381     return kind_;
382   }
reason()383   DeoptimizeReason reason() const {
384     DCHECK(IsDeoptimize());
385     return reason_;
386   }
frame_state()387   Node* frame_state() const {
388     DCHECK(IsDeoptimize());
389     return frame_state_or_result_;
390   }
result()391   Node* result() const {
392     DCHECK(IsSet());
393     return frame_state_or_result_;
394   }
trap_id()395   Runtime::FunctionId trap_id() const {
396     DCHECK(IsTrap());
397     return trap_id_;
398   }
true_block()399   BasicBlock* true_block() const {
400     DCHECK(IsBranch());
401     return true_block_;
402   }
false_block()403   BasicBlock* false_block() const {
404     DCHECK(IsBranch());
405     return false_block_;
406   }
407 
Negate()408   void Negate() {
409     DCHECK(!IsNone());
410     condition_ = NegateFlagsCondition(condition_);
411   }
412 
Commute()413   void Commute() {
414     DCHECK(!IsNone());
415     condition_ = CommuteFlagsCondition(condition_);
416   }
417 
Overwrite(FlagsCondition condition)418   void Overwrite(FlagsCondition condition) { condition_ = condition; }
419 
OverwriteAndNegateIfEqual(FlagsCondition condition)420   void OverwriteAndNegateIfEqual(FlagsCondition condition) {
421     DCHECK(condition_ == kEqual || condition_ == kNotEqual);
422     bool negate = condition_ == kEqual;
423     condition_ = condition;
424     if (negate) Negate();
425   }
426 
OverwriteUnsignedIfSigned()427   void OverwriteUnsignedIfSigned() {
428     switch (condition_) {
429       case kSignedLessThan:
430         condition_ = kUnsignedLessThan;
431         break;
432       case kSignedLessThanOrEqual:
433         condition_ = kUnsignedLessThanOrEqual;
434         break;
435       case kSignedGreaterThan:
436         condition_ = kUnsignedGreaterThan;
437         break;
438       case kSignedGreaterThanOrEqual:
439         condition_ = kUnsignedGreaterThanOrEqual;
440         break;
441       default:
442         break;
443     }
444   }
445 
446   // Encodes this flags continuation into the given opcode.
Encode(InstructionCode opcode)447   InstructionCode Encode(InstructionCode opcode) {
448     opcode |= FlagsModeField::encode(mode_);
449     if (mode_ != kFlags_none) {
450       opcode |= FlagsConditionField::encode(condition_);
451     }
452     return opcode;
453   }
454 
455  private:
FlagsContinuation(FlagsCondition condition,DeoptimizeKind kind,DeoptimizeReason reason,Node * frame_state)456   FlagsContinuation(FlagsCondition condition, DeoptimizeKind kind,
457                     DeoptimizeReason reason, Node* frame_state)
458       : mode_(kFlags_deoptimize),
459         condition_(condition),
460         kind_(kind),
461         reason_(reason),
462         frame_state_or_result_(frame_state) {
463     DCHECK_NOT_NULL(frame_state);
464   }
FlagsContinuation(FlagsCondition condition,Node * result)465   FlagsContinuation(FlagsCondition condition, Node* result)
466       : mode_(kFlags_set),
467         condition_(condition),
468         frame_state_or_result_(result) {
469     DCHECK_NOT_NULL(result);
470   }
471 
FlagsContinuation(FlagsCondition condition,Runtime::FunctionId trap_id,Node * result)472   FlagsContinuation(FlagsCondition condition, Runtime::FunctionId trap_id,
473                     Node* result)
474       : mode_(kFlags_trap),
475         condition_(condition),
476         frame_state_or_result_(result),
477         trap_id_(trap_id) {
478     DCHECK_NOT_NULL(result);
479   }
480 
481   FlagsMode const mode_;
482   FlagsCondition condition_;
483   DeoptimizeKind kind_;          // Only valid if mode_ == kFlags_deoptimize
484   DeoptimizeReason reason_;      // Only valid if mode_ == kFlags_deoptimize
485   Node* frame_state_or_result_;  // Only valid if mode_ == kFlags_deoptimize
486                                  // or mode_ == kFlags_set.
487   BasicBlock* true_block_;       // Only valid if mode_ == kFlags_branch.
488   BasicBlock* false_block_;      // Only valid if mode_ == kFlags_branch.
489   Runtime::FunctionId trap_id_;  // Only valid if mode_ == kFlags_trap.
490 };
491 
492 }  // namespace compiler
493 }  // namespace internal
494 }  // namespace v8
495 
496 #endif  // V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_
497