1 // Copyright 2016 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_GRAPH_ASSEMBLER_H_
6 #define V8_COMPILER_GRAPH_ASSEMBLER_H_
7
8 #include "src/compiler/js-graph.h"
9 #include "src/compiler/node.h"
10 #include "src/compiler/simplified-operator.h"
11 #include "src/vector-slot-pair.h"
12
13 namespace v8 {
14 namespace internal {
15
16 class JSGraph;
17 class Graph;
18
19 namespace compiler {
20
21 #define PURE_ASSEMBLER_MACH_UNOP_LIST(V) \
22 V(ChangeInt32ToInt64) \
23 V(ChangeInt32ToFloat64) \
24 V(ChangeUint32ToFloat64) \
25 V(ChangeUint32ToUint64) \
26 V(ChangeFloat64ToInt32) \
27 V(ChangeFloat64ToUint32) \
28 V(TruncateInt64ToInt32) \
29 V(RoundFloat64ToInt32) \
30 V(TruncateFloat64ToWord32) \
31 V(Float64ExtractLowWord32) \
32 V(Float64ExtractHighWord32) \
33 V(BitcastInt32ToFloat32) \
34 V(BitcastInt64ToFloat64) \
35 V(BitcastFloat32ToInt32) \
36 V(BitcastFloat64ToInt64) \
37 V(Float64Abs) \
38 V(Word32ReverseBytes) \
39 V(Word64ReverseBytes)
40
41 #define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \
42 V(WordShl) \
43 V(WordSar) \
44 V(WordAnd) \
45 V(Word32Or) \
46 V(Word32And) \
47 V(Word32Xor) \
48 V(Word32Shr) \
49 V(Word32Shl) \
50 V(Word32Sar) \
51 V(IntAdd) \
52 V(IntSub) \
53 V(IntMul) \
54 V(IntLessThan) \
55 V(UintLessThan) \
56 V(Int32Add) \
57 V(Int32Sub) \
58 V(Int32Mul) \
59 V(Int32LessThanOrEqual) \
60 V(Uint32LessThanOrEqual) \
61 V(Uint32LessThan) \
62 V(Int32LessThan) \
63 V(Float64Add) \
64 V(Float64Sub) \
65 V(Float64Div) \
66 V(Float64Mod) \
67 V(Float64Equal) \
68 V(Float64LessThan) \
69 V(Float64LessThanOrEqual) \
70 V(Float64InsertLowWord32) \
71 V(Float64InsertHighWord32) \
72 V(Word32Equal) \
73 V(WordEqual)
74
75 #define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
76 V(Int32AddWithOverflow) \
77 V(Int32SubWithOverflow) \
78 V(Int32MulWithOverflow) \
79 V(Int32Mod) \
80 V(Int32Div) \
81 V(Uint32Mod) \
82 V(Uint32Div)
83
84 #define JSGRAPH_SINGLETON_CONSTANT_LIST(V) \
85 V(TrueConstant) \
86 V(FalseConstant) \
87 V(NullConstant) \
88 V(HeapNumberMapConstant) \
89 V(NoContextConstant) \
90 V(EmptyStringConstant) \
91 V(UndefinedConstant) \
92 V(TheHoleConstant) \
93 V(FixedArrayMapConstant) \
94 V(FixedDoubleArrayMapConstant) \
95 V(ToNumberBuiltinConstant) \
96 V(AllocateInNewSpaceStubConstant) \
97 V(AllocateInOldSpaceStubConstant)
98
99 class GraphAssembler;
100
101 enum class GraphAssemblerLabelType { kDeferred, kNonDeferred, kLoop };
102
103 // Label with statically known count of incoming branches and phis.
104 template <size_t VarCount>
105 class GraphAssemblerLabel {
106 public:
107 Node* PhiAt(size_t index);
108
109 template <typename... Reps>
GraphAssemblerLabel(GraphAssemblerLabelType type,Reps...reps)110 explicit GraphAssemblerLabel(GraphAssemblerLabelType type, Reps... reps)
111 : type_(type) {
112 STATIC_ASSERT(VarCount == sizeof...(reps));
113 MachineRepresentation reps_array[] = {MachineRepresentation::kNone,
114 reps...};
115 for (size_t i = 0; i < VarCount; i++) {
116 representations_[i] = reps_array[i + 1];
117 }
118 }
119
~GraphAssemblerLabel()120 ~GraphAssemblerLabel() { DCHECK(IsBound() || merged_count_ == 0); }
121
122 private:
123 friend class GraphAssembler;
124
SetBound()125 void SetBound() {
126 DCHECK(!IsBound());
127 is_bound_ = true;
128 }
IsBound()129 bool IsBound() const { return is_bound_; }
IsDeferred()130 bool IsDeferred() const {
131 return type_ == GraphAssemblerLabelType::kDeferred;
132 }
IsLoop()133 bool IsLoop() const { return type_ == GraphAssemblerLabelType::kLoop; }
134
135 bool is_bound_ = false;
136 GraphAssemblerLabelType const type_;
137 size_t merged_count_ = 0;
138 Node* effect_;
139 Node* control_;
140 Node* bindings_[VarCount + 1];
141 MachineRepresentation representations_[VarCount + 1];
142 };
143
144 class GraphAssembler {
145 public:
146 GraphAssembler(JSGraph* jsgraph, Node* effect, Node* control, Zone* zone);
147
148 void Reset(Node* effect, Node* control);
149
150 // Create label.
151 template <typename... Reps>
MakeLabelFor(GraphAssemblerLabelType type,Reps...reps)152 static GraphAssemblerLabel<sizeof...(Reps)> MakeLabelFor(
153 GraphAssemblerLabelType type, Reps... reps) {
154 return GraphAssemblerLabel<sizeof...(Reps)>(type, reps...);
155 }
156
157 // Convenience wrapper for creating non-deferred labels.
158 template <typename... Reps>
MakeLabel(Reps...reps)159 static GraphAssemblerLabel<sizeof...(Reps)> MakeLabel(Reps... reps) {
160 return MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, reps...);
161 }
162
163 // Convenience wrapper for creating loop labels.
164 template <typename... Reps>
MakeLoopLabel(Reps...reps)165 static GraphAssemblerLabel<sizeof...(Reps)> MakeLoopLabel(Reps... reps) {
166 return MakeLabelFor(GraphAssemblerLabelType::kLoop, reps...);
167 }
168
169 // Convenience wrapper for creating deferred labels.
170 template <typename... Reps>
MakeDeferredLabel(Reps...reps)171 static GraphAssemblerLabel<sizeof...(Reps)> MakeDeferredLabel(Reps... reps) {
172 return MakeLabelFor(GraphAssemblerLabelType::kDeferred, reps...);
173 }
174
175 // Value creation.
176 Node* IntPtrConstant(intptr_t value);
177 Node* Uint32Constant(int32_t value);
178 Node* Int32Constant(int32_t value);
179 Node* UniqueInt32Constant(int32_t value);
180 Node* SmiConstant(int32_t value);
181 Node* Float64Constant(double value);
182 Node* Projection(int index, Node* value);
183 Node* HeapConstant(Handle<HeapObject> object);
184 Node* CEntryStubConstant(int result_size);
185 Node* ExternalConstant(ExternalReference ref);
186
187 Node* LoadFramePointer();
188
189 #define SINGLETON_CONST_DECL(Name) Node* Name();
190 JSGRAPH_SINGLETON_CONSTANT_LIST(SINGLETON_CONST_DECL)
191 #undef SINGLETON_CONST_DECL
192
193 #define PURE_UNOP_DECL(Name) Node* Name(Node* input);
194 PURE_ASSEMBLER_MACH_UNOP_LIST(PURE_UNOP_DECL)
195 #undef PURE_UNOP_DECL
196
197 #define BINOP_DECL(Name) Node* Name(Node* left, Node* right);
198 PURE_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
199 CHECKED_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
200 #undef BINOP_DECL
201
202 // Debugging
203 Node* DebugBreak();
204
205 Node* Unreachable();
206
207 Node* Float64RoundDown(Node* value);
208 Node* Float64RoundTruncate(Node* value);
209
210 Node* ToNumber(Node* value);
211 Node* BitcastWordToTagged(Node* value);
212 Node* Allocate(PretenureFlag pretenure, Node* size);
213 Node* LoadField(FieldAccess const&, Node* object);
214 Node* LoadElement(ElementAccess const&, Node* object, Node* index);
215 Node* StoreField(FieldAccess const&, Node* object, Node* value);
216 Node* StoreElement(ElementAccess const&, Node* object, Node* index,
217 Node* value);
218
219 Node* Store(StoreRepresentation rep, Node* object, Node* offset, Node* value);
220 Node* Load(MachineType rep, Node* object, Node* offset);
221
222 Node* StoreUnaligned(MachineRepresentation rep, Node* object, Node* offset,
223 Node* value);
224 Node* LoadUnaligned(MachineType rep, Node* object, Node* offset);
225
226 Node* Retain(Node* buffer);
227 Node* UnsafePointerAdd(Node* base, Node* external);
228
229 Node* Word32PoisonOnSpeculation(Node* value);
230
231 Node* DeoptimizeIf(DeoptimizeReason reason, VectorSlotPair const& feedback,
232 Node* condition, Node* frame_state);
233 Node* DeoptimizeIfNot(
234 DeoptimizeReason reason, VectorSlotPair const& feedback, Node* condition,
235 Node* frame_state,
236 IsSafetyCheck is_safety_check = IsSafetyCheck::kSafetyCheck);
237 template <typename... Args>
238 Node* Call(const CallDescriptor* call_descriptor, Args... args);
239 template <typename... Args>
240 Node* Call(const Operator* op, Args... args);
241
242 // Basic control operations.
243 template <size_t VarCount>
244 void Bind(GraphAssemblerLabel<VarCount>* label);
245
246 template <typename... Vars>
247 void Goto(GraphAssemblerLabel<sizeof...(Vars)>* label, Vars...);
248
249 void Branch(Node* condition, GraphAssemblerLabel<0u>* if_true,
250 GraphAssemblerLabel<0u>* if_false);
251
252 // Control helpers.
253 // {GotoIf(c, l)} is equivalent to {Branch(c, l, templ);Bind(templ)}.
254 template <typename... Vars>
255 void GotoIf(Node* condition, GraphAssemblerLabel<sizeof...(Vars)>* label,
256 Vars...);
257
258 // {GotoIfNot(c, l)} is equivalent to {Branch(c, templ, l);Bind(templ)}.
259 template <typename... Vars>
260 void GotoIfNot(Node* condition, GraphAssemblerLabel<sizeof...(Vars)>* label,
261 Vars...);
262
263 // Extractors (should be only used when destructing/resetting the assembler).
264 Node* ExtractCurrentControl();
265 Node* ExtractCurrentEffect();
266
267 private:
268 template <typename... Vars>
269 void MergeState(GraphAssemblerLabel<sizeof...(Vars)>* label, Vars... vars);
270
271 Operator const* ToNumberOperator();
272
jsgraph()273 JSGraph* jsgraph() const { return jsgraph_; }
isolate()274 Isolate* isolate() const { return jsgraph_->isolate(); }
graph()275 Graph* graph() const { return jsgraph_->graph(); }
temp_zone()276 Zone* temp_zone() const { return temp_zone_; }
common()277 CommonOperatorBuilder* common() const { return jsgraph()->common(); }
machine()278 MachineOperatorBuilder* machine() const { return jsgraph()->machine(); }
simplified()279 SimplifiedOperatorBuilder* simplified() const {
280 return jsgraph()->simplified();
281 }
282
283 SetOncePointer<Operator const> to_number_operator_;
284 Zone* temp_zone_;
285 JSGraph* jsgraph_;
286 Node* current_effect_;
287 Node* current_control_;
288 };
289
290 template <size_t VarCount>
PhiAt(size_t index)291 Node* GraphAssemblerLabel<VarCount>::PhiAt(size_t index) {
292 DCHECK(IsBound());
293 DCHECK_LT(index, VarCount);
294 return bindings_[index];
295 }
296
297 template <typename... Vars>
MergeState(GraphAssemblerLabel<sizeof...(Vars)> * label,Vars...vars)298 void GraphAssembler::MergeState(GraphAssemblerLabel<sizeof...(Vars)>* label,
299 Vars... vars) {
300 int merged_count = static_cast<int>(label->merged_count_);
301 Node* var_array[] = {nullptr, vars...};
302 if (label->IsLoop()) {
303 if (merged_count == 0) {
304 DCHECK(!label->IsBound());
305 label->control_ = graph()->NewNode(common()->Loop(2), current_control_,
306 current_control_);
307 label->effect_ = graph()->NewNode(common()->EffectPhi(2), current_effect_,
308 current_effect_, label->control_);
309 Node* terminate = graph()->NewNode(common()->Terminate(), label->effect_,
310 label->control_);
311 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
312 for (size_t i = 0; i < sizeof...(vars); i++) {
313 label->bindings_[i] = graph()->NewNode(
314 common()->Phi(label->representations_[i], 2), var_array[i + 1],
315 var_array[i + 1], label->control_);
316 }
317 } else {
318 DCHECK(label->IsBound());
319 DCHECK_EQ(1, merged_count);
320 label->control_->ReplaceInput(1, current_control_);
321 label->effect_->ReplaceInput(1, current_effect_);
322 for (size_t i = 0; i < sizeof...(vars); i++) {
323 label->bindings_[i]->ReplaceInput(1, var_array[i + 1]);
324 }
325 }
326 } else {
327 DCHECK(!label->IsBound());
328 if (merged_count == 0) {
329 // Just set the control, effect and variables directly.
330 DCHECK(!label->IsBound());
331 label->control_ = current_control_;
332 label->effect_ = current_effect_;
333 for (size_t i = 0; i < sizeof...(vars); i++) {
334 label->bindings_[i] = var_array[i + 1];
335 }
336 } else if (merged_count == 1) {
337 // Create merge, effect phi and a phi for each variable.
338 label->control_ = graph()->NewNode(common()->Merge(2), label->control_,
339 current_control_);
340 label->effect_ = graph()->NewNode(common()->EffectPhi(2), label->effect_,
341 current_effect_, label->control_);
342 for (size_t i = 0; i < sizeof...(vars); i++) {
343 label->bindings_[i] = graph()->NewNode(
344 common()->Phi(label->representations_[i], 2), label->bindings_[i],
345 var_array[i + 1], label->control_);
346 }
347 } else {
348 // Append to the merge, effect phi and phis.
349 DCHECK_EQ(IrOpcode::kMerge, label->control_->opcode());
350 label->control_->AppendInput(graph()->zone(), current_control_);
351 NodeProperties::ChangeOp(label->control_,
352 common()->Merge(merged_count + 1));
353
354 DCHECK_EQ(IrOpcode::kEffectPhi, label->effect_->opcode());
355 label->effect_->ReplaceInput(merged_count, current_effect_);
356 label->effect_->AppendInput(graph()->zone(), label->control_);
357 NodeProperties::ChangeOp(label->effect_,
358 common()->EffectPhi(merged_count + 1));
359
360 for (size_t i = 0; i < sizeof...(vars); i++) {
361 DCHECK_EQ(IrOpcode::kPhi, label->bindings_[i]->opcode());
362 label->bindings_[i]->ReplaceInput(merged_count, var_array[i + 1]);
363 label->bindings_[i]->AppendInput(graph()->zone(), label->control_);
364 NodeProperties::ChangeOp(
365 label->bindings_[i],
366 common()->Phi(label->representations_[i], merged_count + 1));
367 }
368 }
369 }
370 label->merged_count_++;
371 }
372
373 template <size_t VarCount>
Bind(GraphAssemblerLabel<VarCount> * label)374 void GraphAssembler::Bind(GraphAssemblerLabel<VarCount>* label) {
375 DCHECK_NULL(current_control_);
376 DCHECK_NULL(current_effect_);
377 DCHECK_LT(0, label->merged_count_);
378
379 current_control_ = label->control_;
380 current_effect_ = label->effect_;
381
382 label->SetBound();
383 }
384
385 template <typename... Vars>
Goto(GraphAssemblerLabel<sizeof...(Vars)> * label,Vars...vars)386 void GraphAssembler::Goto(GraphAssemblerLabel<sizeof...(Vars)>* label,
387 Vars... vars) {
388 DCHECK_NOT_NULL(current_control_);
389 DCHECK_NOT_NULL(current_effect_);
390 MergeState(label, vars...);
391 current_control_ = nullptr;
392 current_effect_ = nullptr;
393 }
394
395 template <typename... Vars>
GotoIf(Node * condition,GraphAssemblerLabel<sizeof...(Vars)> * label,Vars...vars)396 void GraphAssembler::GotoIf(Node* condition,
397 GraphAssemblerLabel<sizeof...(Vars)>* label,
398 Vars... vars) {
399 BranchHint hint =
400 label->IsDeferred() ? BranchHint::kFalse : BranchHint::kNone;
401 Node* branch =
402 graph()->NewNode(common()->Branch(hint), condition, current_control_);
403
404 current_control_ = graph()->NewNode(common()->IfTrue(), branch);
405 MergeState(label, vars...);
406
407 current_control_ = graph()->NewNode(common()->IfFalse(), branch);
408 }
409
410 template <typename... Vars>
GotoIfNot(Node * condition,GraphAssemblerLabel<sizeof...(Vars)> * label,Vars...vars)411 void GraphAssembler::GotoIfNot(Node* condition,
412 GraphAssemblerLabel<sizeof...(Vars)>* label,
413 Vars... vars) {
414 BranchHint hint = label->IsDeferred() ? BranchHint::kTrue : BranchHint::kNone;
415 Node* branch =
416 graph()->NewNode(common()->Branch(hint), condition, current_control_);
417
418 current_control_ = graph()->NewNode(common()->IfFalse(), branch);
419 MergeState(label, vars...);
420
421 current_control_ = graph()->NewNode(common()->IfTrue(), branch);
422 }
423
424 template <typename... Args>
Call(const CallDescriptor * call_descriptor,Args...args)425 Node* GraphAssembler::Call(const CallDescriptor* call_descriptor,
426 Args... args) {
427 const Operator* op = common()->Call(call_descriptor);
428 return Call(op, args...);
429 }
430
431 template <typename... Args>
Call(const Operator * op,Args...args)432 Node* GraphAssembler::Call(const Operator* op, Args... args) {
433 DCHECK_EQ(IrOpcode::kCall, op->opcode());
434 Node* args_array[] = {args..., current_effect_, current_control_};
435 int size = static_cast<int>(sizeof...(args)) + op->EffectInputCount() +
436 op->ControlInputCount();
437 Node* call = graph()->NewNode(op, size, args_array);
438 DCHECK_EQ(0, op->ControlOutputCount());
439 current_effect_ = call;
440 return call;
441 }
442
443 } // namespace compiler
444 } // namespace internal
445 } // namespace v8
446
447 #endif // V8_COMPILER_GRAPH_ASSEMBLER_H_
448