1 // Copyright 2015 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 #include "src/compiler/code-assembler.h"
6
7 #include <ostream>
8
9 #include "src/base/bits.h"
10 #include "src/codegen/code-factory.h"
11 #include "src/codegen/interface-descriptors-inl.h"
12 #include "src/codegen/machine-type.h"
13 #include "src/codegen/macro-assembler.h"
14 #include "src/compiler/backend/instruction-selector.h"
15 #include "src/compiler/graph.h"
16 #include "src/compiler/js-graph.h"
17 #include "src/compiler/linkage.h"
18 #include "src/compiler/node-matchers.h"
19 #include "src/compiler/pipeline.h"
20 #include "src/compiler/raw-machine-assembler.h"
21 #include "src/compiler/schedule.h"
22 #include "src/execution/frames.h"
23 #include "src/handles/handles-inl.h"
24 #include "src/heap/factory-inl.h"
25 #include "src/interpreter/bytecodes.h"
26 #include "src/numbers/conversions-inl.h"
27 #include "src/objects/smi.h"
28 #include "src/utils/memcopy.h"
29 #include "src/zone/zone.h"
30
31 namespace v8 {
32 namespace internal {
33
34 constexpr MachineType MachineTypeOf<Smi>::value;
35 constexpr MachineType MachineTypeOf<Object>::value;
36 constexpr MachineType MachineTypeOf<MaybeObject>::value;
37
38 namespace compiler {
39
40 static_assert(std::is_convertible<TNode<Number>, TNode<Object>>::value,
41 "test subtyping");
42 static_assert(
43 std::is_convertible<TNode<Number>, TNode<UnionT<Smi, HeapObject>>>::value,
44 "test subtyping");
45 static_assert(
46 !std::is_convertible<TNode<UnionT<Smi, HeapObject>>, TNode<Number>>::value,
47 "test subtyping");
48
CodeAssemblerState(Isolate * isolate,Zone * zone,const CallInterfaceDescriptor & descriptor,CodeKind kind,const char * name,Builtin builtin)49 CodeAssemblerState::CodeAssemblerState(
50 Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
51 CodeKind kind, const char* name, Builtin builtin)
52 // TODO(rmcilroy): Should we use Linkage::GetBytecodeDispatchDescriptor for
53 // bytecode handlers?
54 : CodeAssemblerState(
55 isolate, zone,
56 Linkage::GetStubCallDescriptor(
57 zone, descriptor, descriptor.GetStackParameterCount(),
58 CallDescriptor::kNoFlags, Operator::kNoProperties),
59 kind, name, builtin) {}
60
CodeAssemblerState(Isolate * isolate,Zone * zone,int parameter_count,CodeKind kind,const char * name,Builtin builtin)61 CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone,
62 int parameter_count, CodeKind kind,
63 const char* name, Builtin builtin)
64 : CodeAssemblerState(
65 isolate, zone,
66 Linkage::GetJSCallDescriptor(zone, false, parameter_count,
67 CallDescriptor::kCanUseRoots),
68 kind, name, builtin) {}
69
CodeAssemblerState(Isolate * isolate,Zone * zone,CallDescriptor * call_descriptor,CodeKind kind,const char * name,Builtin builtin)70 CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone,
71 CallDescriptor* call_descriptor,
72 CodeKind kind, const char* name,
73 Builtin builtin)
74 : raw_assembler_(new RawMachineAssembler(
75 isolate, zone->New<Graph>(zone), call_descriptor,
76 MachineType::PointerRepresentation(),
77 InstructionSelector::SupportedMachineOperatorFlags(),
78 InstructionSelector::AlignmentRequirements())),
79 kind_(kind),
80 name_(name),
81 builtin_(builtin),
82 code_generated_(false),
83 variables_(zone),
84 jsgraph_(zone->New<JSGraph>(
85 isolate, raw_assembler_->graph(), raw_assembler_->common(),
86 zone->New<JSOperatorBuilder>(zone), raw_assembler_->simplified(),
87 raw_assembler_->machine())) {}
88
89 CodeAssemblerState::~CodeAssemblerState() = default;
90
parameter_count() const91 int CodeAssemblerState::parameter_count() const {
92 return static_cast<int>(raw_assembler_->call_descriptor()->ParameterCount());
93 }
94
95 CodeAssembler::~CodeAssembler() = default;
96
97 #if DEBUG
PrintCurrentBlock(std::ostream & os)98 void CodeAssemblerState::PrintCurrentBlock(std::ostream& os) {
99 raw_assembler_->PrintCurrentBlock(os);
100 }
101 #endif
102
InsideBlock()103 bool CodeAssemblerState::InsideBlock() { return raw_assembler_->InsideBlock(); }
104
SetInitialDebugInformation(const char * msg,const char * file,int line)105 void CodeAssemblerState::SetInitialDebugInformation(const char* msg,
106 const char* file,
107 int line) {
108 #if DEBUG
109 AssemblerDebugInfo debug_info = {msg, file, line};
110 raw_assembler_->SetCurrentExternalSourcePosition({file, line});
111 raw_assembler_->SetInitialDebugInformation(debug_info);
112 #endif // DEBUG
113 }
114
115 class BreakOnNodeDecorator final : public GraphDecorator {
116 public:
BreakOnNodeDecorator(NodeId node_id)117 explicit BreakOnNodeDecorator(NodeId node_id) : node_id_(node_id) {}
118
Decorate(Node * node)119 void Decorate(Node* node) final {
120 if (node->id() == node_id_) {
121 base::OS::DebugBreak();
122 }
123 }
124
125 private:
126 NodeId node_id_;
127 };
128
BreakOnNode(int node_id)129 void CodeAssembler::BreakOnNode(int node_id) {
130 Graph* graph = raw_assembler()->graph();
131 Zone* zone = graph->zone();
132 GraphDecorator* decorator =
133 zone->New<BreakOnNodeDecorator>(static_cast<NodeId>(node_id));
134 graph->AddDecorator(decorator);
135 }
136
RegisterCallGenerationCallbacks(const CodeAssemblerCallback & call_prologue,const CodeAssemblerCallback & call_epilogue)137 void CodeAssembler::RegisterCallGenerationCallbacks(
138 const CodeAssemblerCallback& call_prologue,
139 const CodeAssemblerCallback& call_epilogue) {
140 // The callback can be registered only once.
141 DCHECK(!state_->call_prologue_);
142 DCHECK(!state_->call_epilogue_);
143 state_->call_prologue_ = call_prologue;
144 state_->call_epilogue_ = call_epilogue;
145 }
146
UnregisterCallGenerationCallbacks()147 void CodeAssembler::UnregisterCallGenerationCallbacks() {
148 state_->call_prologue_ = nullptr;
149 state_->call_epilogue_ = nullptr;
150 }
151
CallPrologue()152 void CodeAssembler::CallPrologue() {
153 if (state_->call_prologue_) {
154 state_->call_prologue_();
155 }
156 }
157
CallEpilogue()158 void CodeAssembler::CallEpilogue() {
159 if (state_->call_epilogue_) {
160 state_->call_epilogue_();
161 }
162 }
163
Word32ShiftIsSafe() const164 bool CodeAssembler::Word32ShiftIsSafe() const {
165 return raw_assembler()->machine()->Word32ShiftIsSafe();
166 }
167
168 // static
GenerateCode(CodeAssemblerState * state,const AssemblerOptions & options,const ProfileDataFromFile * profile_data)169 Handle<Code> CodeAssembler::GenerateCode(
170 CodeAssemblerState* state, const AssemblerOptions& options,
171 const ProfileDataFromFile* profile_data) {
172 DCHECK(!state->code_generated_);
173
174 RawMachineAssembler* rasm = state->raw_assembler_.get();
175
176 Handle<Code> code;
177 Graph* graph = rasm->ExportForOptimization();
178
179 code = Pipeline::GenerateCodeForCodeStub(
180 rasm->isolate(), rasm->call_descriptor(), graph, state->jsgraph_,
181 rasm->source_positions(), state->kind_, state->name_,
182 state->builtin_, options, profile_data)
183 .ToHandleChecked();
184
185 state->code_generated_ = true;
186 return code;
187 }
188
Is64() const189 bool CodeAssembler::Is64() const { return raw_assembler()->machine()->Is64(); }
Is32() const190 bool CodeAssembler::Is32() const { return raw_assembler()->machine()->Is32(); }
191
IsFloat64RoundUpSupported() const192 bool CodeAssembler::IsFloat64RoundUpSupported() const {
193 return raw_assembler()->machine()->Float64RoundUp().IsSupported();
194 }
195
IsFloat64RoundDownSupported() const196 bool CodeAssembler::IsFloat64RoundDownSupported() const {
197 return raw_assembler()->machine()->Float64RoundDown().IsSupported();
198 }
199
IsFloat64RoundTiesEvenSupported() const200 bool CodeAssembler::IsFloat64RoundTiesEvenSupported() const {
201 return raw_assembler()->machine()->Float64RoundTiesEven().IsSupported();
202 }
203
IsFloat64RoundTruncateSupported() const204 bool CodeAssembler::IsFloat64RoundTruncateSupported() const {
205 return raw_assembler()->machine()->Float64RoundTruncate().IsSupported();
206 }
207
IsInt32AbsWithOverflowSupported() const208 bool CodeAssembler::IsInt32AbsWithOverflowSupported() const {
209 return raw_assembler()->machine()->Int32AbsWithOverflow().IsSupported();
210 }
211
IsInt64AbsWithOverflowSupported() const212 bool CodeAssembler::IsInt64AbsWithOverflowSupported() const {
213 return raw_assembler()->machine()->Int64AbsWithOverflow().IsSupported();
214 }
215
IsIntPtrAbsWithOverflowSupported() const216 bool CodeAssembler::IsIntPtrAbsWithOverflowSupported() const {
217 return Is64() ? IsInt64AbsWithOverflowSupported()
218 : IsInt32AbsWithOverflowSupported();
219 }
220
IsWord32PopcntSupported() const221 bool CodeAssembler::IsWord32PopcntSupported() const {
222 return raw_assembler()->machine()->Word32Popcnt().IsSupported();
223 }
224
IsWord64PopcntSupported() const225 bool CodeAssembler::IsWord64PopcntSupported() const {
226 return raw_assembler()->machine()->Word64Popcnt().IsSupported();
227 }
228
IsWord32CtzSupported() const229 bool CodeAssembler::IsWord32CtzSupported() const {
230 return raw_assembler()->machine()->Word32Ctz().IsSupported();
231 }
232
IsWord64CtzSupported() const233 bool CodeAssembler::IsWord64CtzSupported() const {
234 return raw_assembler()->machine()->Word64Ctz().IsSupported();
235 }
236
237 #ifdef DEBUG
GenerateCheckMaybeObjectIsObject(TNode<MaybeObject> node,const char * location)238 void CodeAssembler::GenerateCheckMaybeObjectIsObject(TNode<MaybeObject> node,
239 const char* location) {
240 Label ok(this);
241 GotoIf(WordNotEqual(WordAnd(BitcastMaybeObjectToWord(node),
242 IntPtrConstant(kHeapObjectTagMask)),
243 IntPtrConstant(kWeakHeapObjectTag)),
244 &ok);
245 base::EmbeddedVector<char, 1024> message;
246 SNPrintF(message, "no Object: %s", location);
247 TNode<String> message_node = StringConstant(message.begin());
248 // This somewhat misuses the AbortCSADcheck runtime function. This will print
249 // "abort: CSA_DCHECK failed: <message>", which is good enough.
250 AbortCSADcheck(message_node);
251 Unreachable();
252 Bind(&ok);
253 }
254 #endif
255
Int32Constant(int32_t value)256 TNode<Int32T> CodeAssembler::Int32Constant(int32_t value) {
257 return UncheckedCast<Int32T>(jsgraph()->Int32Constant(value));
258 }
259
Int64Constant(int64_t value)260 TNode<Int64T> CodeAssembler::Int64Constant(int64_t value) {
261 return UncheckedCast<Int64T>(jsgraph()->Int64Constant(value));
262 }
263
IntPtrConstant(intptr_t value)264 TNode<IntPtrT> CodeAssembler::IntPtrConstant(intptr_t value) {
265 return UncheckedCast<IntPtrT>(jsgraph()->IntPtrConstant(value));
266 }
267
TaggedIndexConstant(intptr_t value)268 TNode<TaggedIndex> CodeAssembler::TaggedIndexConstant(intptr_t value) {
269 DCHECK(TaggedIndex::IsValid(value));
270 return UncheckedCast<TaggedIndex>(raw_assembler()->IntPtrConstant(value));
271 }
272
NumberConstant(double value)273 TNode<Number> CodeAssembler::NumberConstant(double value) {
274 int smi_value;
275 if (DoubleToSmiInteger(value, &smi_value)) {
276 return UncheckedCast<Number>(SmiConstant(smi_value));
277 } else {
278 // We allocate the heap number constant eagerly at this point instead of
279 // deferring allocation to code generation
280 // (see AllocateAndInstallRequestedHeapObjects) since that makes it easier
281 // to generate constant lookups for embedded builtins.
282 return UncheckedCast<Number>(HeapConstant(
283 isolate()->factory()->NewHeapNumberForCodeAssembler(value)));
284 }
285 }
286
SmiConstant(Smi value)287 TNode<Smi> CodeAssembler::SmiConstant(Smi value) {
288 return UncheckedCast<Smi>(BitcastWordToTaggedSigned(
289 IntPtrConstant(static_cast<intptr_t>(value.ptr()))));
290 }
291
SmiConstant(int value)292 TNode<Smi> CodeAssembler::SmiConstant(int value) {
293 return SmiConstant(Smi::FromInt(value));
294 }
295
UntypedHeapConstant(Handle<HeapObject> object)296 TNode<HeapObject> CodeAssembler::UntypedHeapConstant(
297 Handle<HeapObject> object) {
298 return UncheckedCast<HeapObject>(jsgraph()->HeapConstant(object));
299 }
300
StringConstant(const char * str)301 TNode<String> CodeAssembler::StringConstant(const char* str) {
302 Handle<String> internalized_string =
303 factory()->InternalizeString(base::OneByteVector(str));
304 return UncheckedCast<String>(HeapConstant(internalized_string));
305 }
306
BooleanConstant(bool value)307 TNode<Oddball> CodeAssembler::BooleanConstant(bool value) {
308 Handle<Object> object = isolate()->factory()->ToBoolean(value);
309 return UncheckedCast<Oddball>(
310 jsgraph()->HeapConstant(Handle<HeapObject>::cast(object)));
311 }
312
ExternalConstant(ExternalReference address)313 TNode<ExternalReference> CodeAssembler::ExternalConstant(
314 ExternalReference address) {
315 return UncheckedCast<ExternalReference>(
316 raw_assembler()->ExternalConstant(address));
317 }
318
Float32Constant(double value)319 TNode<Float32T> CodeAssembler::Float32Constant(double value) {
320 return UncheckedCast<Float32T>(jsgraph()->Float32Constant(value));
321 }
322
Float64Constant(double value)323 TNode<Float64T> CodeAssembler::Float64Constant(double value) {
324 return UncheckedCast<Float64T>(jsgraph()->Float64Constant(value));
325 }
326
IsMapOffsetConstant(Node * node)327 bool CodeAssembler::IsMapOffsetConstant(Node* node) {
328 return raw_assembler()->IsMapOffsetConstant(node);
329 }
330
TryToInt32Constant(TNode<IntegralT> node,int32_t * out_value)331 bool CodeAssembler::TryToInt32Constant(TNode<IntegralT> node,
332 int32_t* out_value) {
333 {
334 Int64Matcher m(node);
335 if (m.HasResolvedValue() &&
336 m.IsInRange(std::numeric_limits<int32_t>::min(),
337 std::numeric_limits<int32_t>::max())) {
338 *out_value = static_cast<int32_t>(m.ResolvedValue());
339 return true;
340 }
341 }
342
343 {
344 Int32Matcher m(node);
345 if (m.HasResolvedValue()) {
346 *out_value = m.ResolvedValue();
347 return true;
348 }
349 }
350
351 return false;
352 }
353
TryToInt64Constant(TNode<IntegralT> node,int64_t * out_value)354 bool CodeAssembler::TryToInt64Constant(TNode<IntegralT> node,
355 int64_t* out_value) {
356 Int64Matcher m(node);
357 if (m.HasResolvedValue()) *out_value = m.ResolvedValue();
358 return m.HasResolvedValue();
359 }
360
TryToSmiConstant(TNode<Smi> tnode,Smi * out_value)361 bool CodeAssembler::TryToSmiConstant(TNode<Smi> tnode, Smi* out_value) {
362 Node* node = tnode;
363 if (node->opcode() == IrOpcode::kBitcastWordToTaggedSigned) {
364 node = node->InputAt(0);
365 }
366 return TryToSmiConstant(ReinterpretCast<IntPtrT>(tnode), out_value);
367 }
368
TryToSmiConstant(TNode<IntegralT> node,Smi * out_value)369 bool CodeAssembler::TryToSmiConstant(TNode<IntegralT> node, Smi* out_value) {
370 IntPtrMatcher m(node);
371 if (m.HasResolvedValue()) {
372 intptr_t value = m.ResolvedValue();
373 // Make sure that the value is actually a smi
374 CHECK_EQ(0, value & ((static_cast<intptr_t>(1) << kSmiShiftSize) - 1));
375 *out_value = Smi(static_cast<Address>(value));
376 return true;
377 }
378 return false;
379 }
380
TryToIntPtrConstant(TNode<Smi> tnode,intptr_t * out_value)381 bool CodeAssembler::TryToIntPtrConstant(TNode<Smi> tnode, intptr_t* out_value) {
382 Node* node = tnode;
383 if (node->opcode() == IrOpcode::kBitcastWordToTaggedSigned ||
384 node->opcode() == IrOpcode::kBitcastWordToTagged) {
385 node = node->InputAt(0);
386 }
387 return TryToIntPtrConstant(ReinterpretCast<IntPtrT>(tnode), out_value);
388 }
389
TryToIntPtrConstant(TNode<IntegralT> node,intptr_t * out_value)390 bool CodeAssembler::TryToIntPtrConstant(TNode<IntegralT> node,
391 intptr_t* out_value) {
392 IntPtrMatcher m(node);
393 if (m.HasResolvedValue()) *out_value = m.ResolvedValue();
394 return m.HasResolvedValue();
395 }
396
IsUndefinedConstant(TNode<Object> node)397 bool CodeAssembler::IsUndefinedConstant(TNode<Object> node) {
398 compiler::HeapObjectMatcher m(node);
399 return m.Is(isolate()->factory()->undefined_value());
400 }
401
IsNullConstant(TNode<Object> node)402 bool CodeAssembler::IsNullConstant(TNode<Object> node) {
403 compiler::HeapObjectMatcher m(node);
404 return m.Is(isolate()->factory()->null_value());
405 }
406
UntypedParameter(int index)407 Node* CodeAssembler::UntypedParameter(int index) {
408 if (index == kTargetParameterIndex) return raw_assembler()->TargetParameter();
409 return raw_assembler()->Parameter(index);
410 }
411
IsJSFunctionCall() const412 bool CodeAssembler::IsJSFunctionCall() const {
413 auto call_descriptor = raw_assembler()->call_descriptor();
414 return call_descriptor->IsJSFunctionCall();
415 }
416
GetJSContextParameter()417 TNode<Context> CodeAssembler::GetJSContextParameter() {
418 auto call_descriptor = raw_assembler()->call_descriptor();
419 DCHECK(call_descriptor->IsJSFunctionCall());
420 return Parameter<Context>(Linkage::GetJSCallContextParamIndex(
421 static_cast<int>(call_descriptor->JSParameterCount())));
422 }
423
Return(TNode<Object> value)424 void CodeAssembler::Return(TNode<Object> value) {
425 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
426 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(0).IsTagged());
427 return raw_assembler()->Return(value);
428 }
429
Return(TNode<Object> value1,TNode<Object> value2)430 void CodeAssembler::Return(TNode<Object> value1, TNode<Object> value2) {
431 DCHECK_EQ(2, raw_assembler()->call_descriptor()->ReturnCount());
432 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(0).IsTagged());
433 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(1).IsTagged());
434 return raw_assembler()->Return(value1, value2);
435 }
436
Return(TNode<Object> value1,TNode<Object> value2,TNode<Object> value3)437 void CodeAssembler::Return(TNode<Object> value1, TNode<Object> value2,
438 TNode<Object> value3) {
439 DCHECK_EQ(3, raw_assembler()->call_descriptor()->ReturnCount());
440 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(0).IsTagged());
441 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(1).IsTagged());
442 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(2).IsTagged());
443 return raw_assembler()->Return(value1, value2, value3);
444 }
445
Return(TNode<Int32T> value)446 void CodeAssembler::Return(TNode<Int32T> value) {
447 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
448 DCHECK_EQ(MachineType::Int32(),
449 raw_assembler()->call_descriptor()->GetReturnType(0));
450 return raw_assembler()->Return(value);
451 }
452
Return(TNode<Uint32T> value)453 void CodeAssembler::Return(TNode<Uint32T> value) {
454 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
455 DCHECK_EQ(MachineType::Uint32(),
456 raw_assembler()->call_descriptor()->GetReturnType(0));
457 return raw_assembler()->Return(value);
458 }
459
Return(TNode<WordT> value)460 void CodeAssembler::Return(TNode<WordT> value) {
461 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
462 DCHECK_EQ(
463 MachineType::PointerRepresentation(),
464 raw_assembler()->call_descriptor()->GetReturnType(0).representation());
465 return raw_assembler()->Return(value);
466 }
467
Return(TNode<Float32T> value)468 void CodeAssembler::Return(TNode<Float32T> value) {
469 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
470 DCHECK_EQ(MachineType::Float32(),
471 raw_assembler()->call_descriptor()->GetReturnType(0));
472 return raw_assembler()->Return(value);
473 }
474
Return(TNode<Float64T> value)475 void CodeAssembler::Return(TNode<Float64T> value) {
476 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
477 DCHECK_EQ(MachineType::Float64(),
478 raw_assembler()->call_descriptor()->GetReturnType(0));
479 return raw_assembler()->Return(value);
480 }
481
Return(TNode<WordT> value1,TNode<WordT> value2)482 void CodeAssembler::Return(TNode<WordT> value1, TNode<WordT> value2) {
483 DCHECK_EQ(2, raw_assembler()->call_descriptor()->ReturnCount());
484 DCHECK_EQ(
485 MachineType::PointerRepresentation(),
486 raw_assembler()->call_descriptor()->GetReturnType(0).representation());
487 DCHECK_EQ(
488 MachineType::PointerRepresentation(),
489 raw_assembler()->call_descriptor()->GetReturnType(1).representation());
490 return raw_assembler()->Return(value1, value2);
491 }
492
Return(TNode<WordT> value1,TNode<Object> value2)493 void CodeAssembler::Return(TNode<WordT> value1, TNode<Object> value2) {
494 DCHECK_EQ(2, raw_assembler()->call_descriptor()->ReturnCount());
495 DCHECK_EQ(
496 MachineType::PointerRepresentation(),
497 raw_assembler()->call_descriptor()->GetReturnType(0).representation());
498 DCHECK(raw_assembler()->call_descriptor()->GetReturnType(1).IsTagged());
499 return raw_assembler()->Return(value1, value2);
500 }
501
PopAndReturn(Node * pop,Node * value)502 void CodeAssembler::PopAndReturn(Node* pop, Node* value) {
503 DCHECK_EQ(1, raw_assembler()->call_descriptor()->ReturnCount());
504 return raw_assembler()->PopAndReturn(pop, value);
505 }
506
ReturnIf(TNode<BoolT> condition,TNode<Object> value)507 void CodeAssembler::ReturnIf(TNode<BoolT> condition, TNode<Object> value) {
508 Label if_return(this), if_continue(this);
509 Branch(condition, &if_return, &if_continue);
510 Bind(&if_return);
511 Return(value);
512 Bind(&if_continue);
513 }
514
AbortCSADcheck(Node * message)515 void CodeAssembler::AbortCSADcheck(Node* message) {
516 raw_assembler()->AbortCSADcheck(message);
517 }
518
DebugBreak()519 void CodeAssembler::DebugBreak() { raw_assembler()->DebugBreak(); }
520
Unreachable()521 void CodeAssembler::Unreachable() {
522 DebugBreak();
523 raw_assembler()->Unreachable();
524 }
525
Comment(std::string str)526 void CodeAssembler::Comment(std::string str) {
527 if (!FLAG_code_comments) return;
528 raw_assembler()->Comment(str);
529 }
530
StaticAssert(TNode<BoolT> value,const char * source)531 void CodeAssembler::StaticAssert(TNode<BoolT> value, const char* source) {
532 raw_assembler()->StaticAssert(value, source);
533 }
534
SetSourcePosition(const char * file,int line)535 void CodeAssembler::SetSourcePosition(const char* file, int line) {
536 raw_assembler()->SetCurrentExternalSourcePosition({file, line});
537 }
538
PushSourcePosition()539 void CodeAssembler::PushSourcePosition() {
540 auto position = raw_assembler()->GetCurrentExternalSourcePosition();
541 state_->macro_call_stack_.push_back(position);
542 }
543
PopSourcePosition()544 void CodeAssembler::PopSourcePosition() {
545 state_->macro_call_stack_.pop_back();
546 }
547
GetMacroSourcePositionStack() const548 const std::vector<FileAndLine>& CodeAssembler::GetMacroSourcePositionStack()
549 const {
550 return state_->macro_call_stack_;
551 }
552
Bind(Label * label)553 void CodeAssembler::Bind(Label* label) { return label->Bind(); }
554
555 #if DEBUG
Bind(Label * label,AssemblerDebugInfo debug_info)556 void CodeAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) {
557 return label->Bind(debug_info);
558 }
559 #endif // DEBUG
560
LoadFramePointer()561 TNode<RawPtrT> CodeAssembler::LoadFramePointer() {
562 return UncheckedCast<RawPtrT>(raw_assembler()->LoadFramePointer());
563 }
564
LoadParentFramePointer()565 TNode<RawPtrT> CodeAssembler::LoadParentFramePointer() {
566 return UncheckedCast<RawPtrT>(raw_assembler()->LoadParentFramePointer());
567 }
568
569 #define DEFINE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \
570 TNode<ResType> CodeAssembler::name(TNode<Arg1Type> a, TNode<Arg2Type> b) { \
571 return UncheckedCast<ResType>(raw_assembler()->name(a, b)); \
572 }
CODE_ASSEMBLER_BINARY_OP_LIST(DEFINE_CODE_ASSEMBLER_BINARY_OP)573 CODE_ASSEMBLER_BINARY_OP_LIST(DEFINE_CODE_ASSEMBLER_BINARY_OP)
574 #undef DEFINE_CODE_ASSEMBLER_BINARY_OP
575
576 TNode<WordT> CodeAssembler::WordShl(TNode<WordT> value, int shift) {
577 return (shift != 0) ? WordShl(value, IntPtrConstant(shift)) : value;
578 }
579
WordShr(TNode<WordT> value,int shift)580 TNode<WordT> CodeAssembler::WordShr(TNode<WordT> value, int shift) {
581 return (shift != 0) ? WordShr(value, IntPtrConstant(shift)) : value;
582 }
583
WordSar(TNode<WordT> value,int shift)584 TNode<WordT> CodeAssembler::WordSar(TNode<WordT> value, int shift) {
585 return (shift != 0) ? WordSar(value, IntPtrConstant(shift)) : value;
586 }
587
Word32Shr(TNode<Word32T> value,int shift)588 TNode<Word32T> CodeAssembler::Word32Shr(TNode<Word32T> value, int shift) {
589 return (shift != 0) ? Word32Shr(value, Int32Constant(shift)) : value;
590 }
591
Word32Sar(TNode<Word32T> value,int shift)592 TNode<Word32T> CodeAssembler::Word32Sar(TNode<Word32T> value, int shift) {
593 return (shift != 0) ? Word32Sar(value, Int32Constant(shift)) : value;
594 }
595
596 #define CODE_ASSEMBLER_COMPARE(Name, ArgT, VarT, ToConstant, op) \
597 TNode<BoolT> CodeAssembler::Name(TNode<ArgT> left, TNode<ArgT> right) { \
598 VarT lhs, rhs; \
599 if (ToConstant(left, &lhs) && ToConstant(right, &rhs)) { \
600 return BoolConstant(lhs op rhs); \
601 } \
602 return UncheckedCast<BoolT>(raw_assembler()->Name(left, right)); \
603 }
604
605 CODE_ASSEMBLER_COMPARE(IntPtrEqual, WordT, intptr_t, TryToIntPtrConstant, ==)
606 CODE_ASSEMBLER_COMPARE(WordEqual, WordT, intptr_t, TryToIntPtrConstant, ==)
607 CODE_ASSEMBLER_COMPARE(WordNotEqual, WordT, intptr_t, TryToIntPtrConstant, !=)
608 CODE_ASSEMBLER_COMPARE(Word32Equal, Word32T, int32_t, TryToInt32Constant, ==)
609 CODE_ASSEMBLER_COMPARE(Word32NotEqual, Word32T, int32_t, TryToInt32Constant, !=)
610 CODE_ASSEMBLER_COMPARE(Word64Equal, Word64T, int64_t, TryToInt64Constant, ==)
611 CODE_ASSEMBLER_COMPARE(Word64NotEqual, Word64T, int64_t, TryToInt64Constant, !=)
612 #undef CODE_ASSEMBLER_COMPARE
613
ChangeUint32ToWord(TNode<Word32T> value)614 TNode<UintPtrT> CodeAssembler::ChangeUint32ToWord(TNode<Word32T> value) {
615 if (raw_assembler()->machine()->Is64()) {
616 return UncheckedCast<UintPtrT>(
617 raw_assembler()->ChangeUint32ToUint64(value));
618 }
619 return ReinterpretCast<UintPtrT>(value);
620 }
621
ChangeInt32ToIntPtr(TNode<Word32T> value)622 TNode<IntPtrT> CodeAssembler::ChangeInt32ToIntPtr(TNode<Word32T> value) {
623 if (raw_assembler()->machine()->Is64()) {
624 return UncheckedCast<IntPtrT>(raw_assembler()->ChangeInt32ToInt64(value));
625 }
626 return ReinterpretCast<IntPtrT>(value);
627 }
628
ChangeFloat64ToIntPtr(TNode<Float64T> value)629 TNode<IntPtrT> CodeAssembler::ChangeFloat64ToIntPtr(TNode<Float64T> value) {
630 if (raw_assembler()->machine()->Is64()) {
631 return UncheckedCast<IntPtrT>(raw_assembler()->ChangeFloat64ToInt64(value));
632 }
633 return UncheckedCast<IntPtrT>(raw_assembler()->ChangeFloat64ToInt32(value));
634 }
635
ChangeFloat64ToUintPtr(TNode<Float64T> value)636 TNode<UintPtrT> CodeAssembler::ChangeFloat64ToUintPtr(TNode<Float64T> value) {
637 if (raw_assembler()->machine()->Is64()) {
638 return UncheckedCast<UintPtrT>(
639 raw_assembler()->ChangeFloat64ToUint64(value));
640 }
641 return UncheckedCast<UintPtrT>(raw_assembler()->ChangeFloat64ToUint32(value));
642 }
643
ChangeUintPtrToFloat64(TNode<UintPtrT> value)644 TNode<Float64T> CodeAssembler::ChangeUintPtrToFloat64(TNode<UintPtrT> value) {
645 if (raw_assembler()->machine()->Is64()) {
646 // TODO(turbofan): Maybe we should introduce a ChangeUint64ToFloat64
647 // machine operator to TurboFan here?
648 return UncheckedCast<Float64T>(
649 raw_assembler()->RoundUint64ToFloat64(value));
650 }
651 return UncheckedCast<Float64T>(raw_assembler()->ChangeUint32ToFloat64(value));
652 }
653
RoundIntPtrToFloat64(Node * value)654 TNode<Float64T> CodeAssembler::RoundIntPtrToFloat64(Node* value) {
655 if (raw_assembler()->machine()->Is64()) {
656 return UncheckedCast<Float64T>(raw_assembler()->RoundInt64ToFloat64(value));
657 }
658 return UncheckedCast<Float64T>(raw_assembler()->ChangeInt32ToFloat64(value));
659 }
660
TruncateFloat32ToInt32(TNode<Float32T> value)661 TNode<Int32T> CodeAssembler::TruncateFloat32ToInt32(TNode<Float32T> value) {
662 return UncheckedCast<Int32T>(raw_assembler()->TruncateFloat32ToInt32(
663 value, TruncateKind::kSetOverflowToMin));
664 }
665 #define DEFINE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \
666 TNode<ResType> CodeAssembler::name(TNode<ArgType> a) { \
667 return UncheckedCast<ResType>(raw_assembler()->name(a)); \
668 }
CODE_ASSEMBLER_UNARY_OP_LIST(DEFINE_CODE_ASSEMBLER_UNARY_OP)669 CODE_ASSEMBLER_UNARY_OP_LIST(DEFINE_CODE_ASSEMBLER_UNARY_OP)
670 #undef DEFINE_CODE_ASSEMBLER_UNARY_OP
671
672 Node* CodeAssembler::Load(MachineType type, Node* base) {
673 return raw_assembler()->Load(type, base);
674 }
675
Load(MachineType type,Node * base,Node * offset)676 Node* CodeAssembler::Load(MachineType type, Node* base, Node* offset) {
677 return raw_assembler()->Load(type, base, offset);
678 }
679
LoadFullTagged(Node * base)680 TNode<Object> CodeAssembler::LoadFullTagged(Node* base) {
681 return BitcastWordToTagged(Load<RawPtrT>(base));
682 }
683
LoadFullTagged(Node * base,TNode<IntPtrT> offset)684 TNode<Object> CodeAssembler::LoadFullTagged(Node* base, TNode<IntPtrT> offset) {
685 // Please use LoadFromObject(MachineType::MapInHeader(), object,
686 // IntPtrConstant(-kHeapObjectTag)) instead.
687 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
688 return BitcastWordToTagged(Load<RawPtrT>(base, offset));
689 }
690
AtomicLoad(MachineType type,AtomicMemoryOrder order,TNode<RawPtrT> base,TNode<WordT> offset)691 Node* CodeAssembler::AtomicLoad(MachineType type, AtomicMemoryOrder order,
692 TNode<RawPtrT> base, TNode<WordT> offset) {
693 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
694 return raw_assembler()->AtomicLoad(AtomicLoadParameters(type, order), base,
695 offset);
696 }
697
698 template <class Type>
AtomicLoad64(AtomicMemoryOrder order,TNode<RawPtrT> base,TNode<WordT> offset)699 TNode<Type> CodeAssembler::AtomicLoad64(AtomicMemoryOrder order,
700 TNode<RawPtrT> base,
701 TNode<WordT> offset) {
702 return UncheckedCast<Type>(raw_assembler()->AtomicLoad64(
703 AtomicLoadParameters(MachineType::Uint64(), order), base, offset));
704 }
705
706 template TNode<AtomicInt64> CodeAssembler::AtomicLoad64<AtomicInt64>(
707 AtomicMemoryOrder order, TNode<RawPtrT> base, TNode<WordT> offset);
708 template TNode<AtomicUint64> CodeAssembler::AtomicLoad64<AtomicUint64>(
709 AtomicMemoryOrder order, TNode<RawPtrT> base, TNode<WordT> offset);
710
LoadFromObject(MachineType type,TNode<Object> object,TNode<IntPtrT> offset)711 Node* CodeAssembler::LoadFromObject(MachineType type, TNode<Object> object,
712 TNode<IntPtrT> offset) {
713 return raw_assembler()->LoadFromObject(type, object, offset);
714 }
715
716 #ifdef V8_MAP_PACKING
PackMapWord(Node * value)717 Node* CodeAssembler::PackMapWord(Node* value) {
718 TNode<IntPtrT> map_word =
719 BitcastTaggedToWordForTagAndSmiBits(UncheckedCast<AnyTaggedT>(value));
720 TNode<WordT> packed = WordXor(UncheckedCast<WordT>(map_word),
721 IntPtrConstant(Internals::kMapWordXorMask));
722 return BitcastWordToTaggedSigned(packed);
723 }
724 #endif
725
LoadRootMapWord(RootIndex root_index)726 TNode<AnyTaggedT> CodeAssembler::LoadRootMapWord(RootIndex root_index) {
727 #ifdef V8_MAP_PACKING
728 Handle<Object> root = isolate()->root_handle(root_index);
729 Node* map = HeapConstant(Handle<Map>::cast(root));
730 map = PackMapWord(map);
731 return ReinterpretCast<AnyTaggedT>(map);
732 #else
733 return LoadRoot(root_index);
734 #endif
735 }
736
LoadRoot(RootIndex root_index)737 TNode<Object> CodeAssembler::LoadRoot(RootIndex root_index) {
738 if (RootsTable::IsImmortalImmovable(root_index)) {
739 Handle<Object> root = isolate()->root_handle(root_index);
740 if (root->IsSmi()) {
741 return SmiConstant(Smi::cast(*root));
742 } else {
743 return HeapConstant(Handle<HeapObject>::cast(root));
744 }
745 }
746
747 // TODO(jgruber): In theory we could generate better code for this by
748 // letting the macro assembler decide how to load from the roots list. In most
749 // cases, it would boil down to loading from a fixed kRootRegister offset.
750 TNode<ExternalReference> isolate_root =
751 ExternalConstant(ExternalReference::isolate_root(isolate()));
752 int offset = IsolateData::root_slot_offset(root_index);
753 return UncheckedCast<Object>(
754 LoadFullTagged(isolate_root, IntPtrConstant(offset)));
755 }
756
UnalignedLoad(MachineType type,TNode<RawPtrT> base,TNode<WordT> offset)757 Node* CodeAssembler::UnalignedLoad(MachineType type, TNode<RawPtrT> base,
758 TNode<WordT> offset) {
759 return raw_assembler()->UnalignedLoad(type, static_cast<Node*>(base), offset);
760 }
761
Store(Node * base,Node * value)762 void CodeAssembler::Store(Node* base, Node* value) {
763 raw_assembler()->Store(MachineRepresentation::kTagged, base, value,
764 kFullWriteBarrier);
765 }
766
StoreToObject(MachineRepresentation rep,TNode<Object> object,TNode<IntPtrT> offset,Node * value,StoreToObjectWriteBarrier write_barrier)767 void CodeAssembler::StoreToObject(MachineRepresentation rep,
768 TNode<Object> object, TNode<IntPtrT> offset,
769 Node* value,
770 StoreToObjectWriteBarrier write_barrier) {
771 WriteBarrierKind write_barrier_kind;
772 switch (write_barrier) {
773 case StoreToObjectWriteBarrier::kFull:
774 write_barrier_kind = WriteBarrierKind::kFullWriteBarrier;
775 break;
776 case StoreToObjectWriteBarrier::kMap:
777 write_barrier_kind = WriteBarrierKind::kMapWriteBarrier;
778 break;
779 case StoreToObjectWriteBarrier::kNone:
780 if (CanBeTaggedPointer(rep)) {
781 write_barrier_kind = WriteBarrierKind::kAssertNoWriteBarrier;
782 } else {
783 write_barrier_kind = WriteBarrierKind::kNoWriteBarrier;
784 }
785 break;
786 }
787 raw_assembler()->StoreToObject(rep, object, offset, value,
788 write_barrier_kind);
789 }
790
OptimizedStoreField(MachineRepresentation rep,TNode<HeapObject> object,int offset,Node * value)791 void CodeAssembler::OptimizedStoreField(MachineRepresentation rep,
792 TNode<HeapObject> object, int offset,
793 Node* value) {
794 raw_assembler()->OptimizedStoreField(rep, object, offset, value,
795 WriteBarrierKind::kFullWriteBarrier);
796 }
797
OptimizedStoreFieldAssertNoWriteBarrier(MachineRepresentation rep,TNode<HeapObject> object,int offset,Node * value)798 void CodeAssembler::OptimizedStoreFieldAssertNoWriteBarrier(
799 MachineRepresentation rep, TNode<HeapObject> object, int offset,
800 Node* value) {
801 raw_assembler()->OptimizedStoreField(rep, object, offset, value,
802 WriteBarrierKind::kAssertNoWriteBarrier);
803 }
804
OptimizedStoreFieldUnsafeNoWriteBarrier(MachineRepresentation rep,TNode<HeapObject> object,int offset,Node * value)805 void CodeAssembler::OptimizedStoreFieldUnsafeNoWriteBarrier(
806 MachineRepresentation rep, TNode<HeapObject> object, int offset,
807 Node* value) {
808 raw_assembler()->OptimizedStoreField(rep, object, offset, value,
809 WriteBarrierKind::kNoWriteBarrier);
810 }
811
OptimizedStoreMap(TNode<HeapObject> object,TNode<Map> map)812 void CodeAssembler::OptimizedStoreMap(TNode<HeapObject> object,
813 TNode<Map> map) {
814 raw_assembler()->OptimizedStoreMap(object, map);
815 }
816
Store(Node * base,Node * offset,Node * value)817 void CodeAssembler::Store(Node* base, Node* offset, Node* value) {
818 // Please use OptimizedStoreMap(base, value) instead.
819 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
820 raw_assembler()->Store(MachineRepresentation::kTagged, base, offset, value,
821 kFullWriteBarrier);
822 }
823
StoreEphemeronKey(Node * base,Node * offset,Node * value)824 void CodeAssembler::StoreEphemeronKey(Node* base, Node* offset, Node* value) {
825 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
826 raw_assembler()->Store(MachineRepresentation::kTagged, base, offset, value,
827 kEphemeronKeyWriteBarrier);
828 }
829
StoreNoWriteBarrier(MachineRepresentation rep,Node * base,Node * value)830 void CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base,
831 Node* value) {
832 raw_assembler()->Store(
833 rep, base, value,
834 CanBeTaggedPointer(rep) ? kAssertNoWriteBarrier : kNoWriteBarrier);
835 }
836
StoreNoWriteBarrier(MachineRepresentation rep,Node * base,Node * offset,Node * value)837 void CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base,
838 Node* offset, Node* value) {
839 // Please use OptimizedStoreMap(base, value) instead.
840 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
841 raw_assembler()->Store(
842 rep, base, offset, value,
843 CanBeTaggedPointer(rep) ? kAssertNoWriteBarrier : kNoWriteBarrier);
844 }
845
UnsafeStoreNoWriteBarrier(MachineRepresentation rep,Node * base,Node * value)846 void CodeAssembler::UnsafeStoreNoWriteBarrier(MachineRepresentation rep,
847 Node* base, Node* value) {
848 raw_assembler()->Store(rep, base, value, kNoWriteBarrier);
849 }
850
UnsafeStoreNoWriteBarrier(MachineRepresentation rep,Node * base,Node * offset,Node * value)851 void CodeAssembler::UnsafeStoreNoWriteBarrier(MachineRepresentation rep,
852 Node* base, Node* offset,
853 Node* value) {
854 // Please use OptimizedStoreMap(base, value) instead.
855 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
856 raw_assembler()->Store(rep, base, offset, value, kNoWriteBarrier);
857 }
858
StoreFullTaggedNoWriteBarrier(TNode<RawPtrT> base,TNode<Object> tagged_value)859 void CodeAssembler::StoreFullTaggedNoWriteBarrier(TNode<RawPtrT> base,
860 TNode<Object> tagged_value) {
861 StoreNoWriteBarrier(MachineType::PointerRepresentation(), base,
862 BitcastTaggedToWord(tagged_value));
863 }
864
StoreFullTaggedNoWriteBarrier(TNode<RawPtrT> base,TNode<IntPtrT> offset,TNode<Object> tagged_value)865 void CodeAssembler::StoreFullTaggedNoWriteBarrier(TNode<RawPtrT> base,
866 TNode<IntPtrT> offset,
867 TNode<Object> tagged_value) {
868 // Please use OptimizedStoreMap(base, tagged_value) instead.
869 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
870 StoreNoWriteBarrier(MachineType::PointerRepresentation(), base, offset,
871 BitcastTaggedToWord(tagged_value));
872 }
873
AtomicStore(MachineRepresentation rep,AtomicMemoryOrder order,TNode<RawPtrT> base,TNode<WordT> offset,TNode<Word32T> value)874 void CodeAssembler::AtomicStore(MachineRepresentation rep,
875 AtomicMemoryOrder order, TNode<RawPtrT> base,
876 TNode<WordT> offset, TNode<Word32T> value) {
877 DCHECK(!raw_assembler()->IsMapOffsetConstantMinusTag(offset));
878 raw_assembler()->AtomicStore(
879 AtomicStoreParameters(rep, WriteBarrierKind::kNoWriteBarrier, order),
880 base, offset, value);
881 }
882
AtomicStore64(AtomicMemoryOrder order,TNode<RawPtrT> base,TNode<WordT> offset,TNode<UintPtrT> value,TNode<UintPtrT> value_high)883 void CodeAssembler::AtomicStore64(AtomicMemoryOrder order, TNode<RawPtrT> base,
884 TNode<WordT> offset, TNode<UintPtrT> value,
885 TNode<UintPtrT> value_high) {
886 raw_assembler()->AtomicStore64(
887 AtomicStoreParameters(MachineRepresentation::kWord64,
888 WriteBarrierKind::kNoWriteBarrier, order),
889 base, offset, value, value_high);
890 }
891
892 #define ATOMIC_FUNCTION(name) \
893 TNode<Word32T> CodeAssembler::Atomic##name( \
894 MachineType type, TNode<RawPtrT> base, TNode<UintPtrT> offset, \
895 TNode<Word32T> value) { \
896 return UncheckedCast<Word32T>( \
897 raw_assembler()->Atomic##name(type, base, offset, value)); \
898 } \
899 template <class Type> \
900 TNode<Type> CodeAssembler::Atomic##name##64( \
901 TNode<RawPtrT> base, TNode<UintPtrT> offset, TNode<UintPtrT> value, \
902 TNode<UintPtrT> value_high) { \
903 return UncheckedCast<Type>( \
904 raw_assembler()->Atomic##name##64(base, offset, value, value_high)); \
905 } \
906 template TNode<AtomicInt64> CodeAssembler::Atomic##name##64 < AtomicInt64 > \
907 (TNode<RawPtrT> base, TNode<UintPtrT> offset, TNode<UintPtrT> value, \
908 TNode<UintPtrT> value_high); \
909 template TNode<AtomicUint64> CodeAssembler::Atomic##name##64 < \
910 AtomicUint64 > (TNode<RawPtrT> base, TNode<UintPtrT> offset, \
911 TNode<UintPtrT> value, TNode<UintPtrT> value_high);
912 ATOMIC_FUNCTION(Add)
ATOMIC_FUNCTION(Sub)913 ATOMIC_FUNCTION(Sub)
914 ATOMIC_FUNCTION(And)
915 ATOMIC_FUNCTION(Or)
916 ATOMIC_FUNCTION(Xor)
917 ATOMIC_FUNCTION(Exchange)
918 #undef ATOMIC_FUNCTION
919
920 TNode<Word32T> CodeAssembler::AtomicCompareExchange(MachineType type,
921 TNode<RawPtrT> base,
922 TNode<WordT> offset,
923 TNode<Word32T> old_value,
924 TNode<Word32T> new_value) {
925 return UncheckedCast<Word32T>(raw_assembler()->AtomicCompareExchange(
926 type, base, offset, old_value, new_value));
927 }
928
929 template <class Type>
AtomicCompareExchange64(TNode<RawPtrT> base,TNode<WordT> offset,TNode<UintPtrT> old_value,TNode<UintPtrT> new_value,TNode<UintPtrT> old_value_high,TNode<UintPtrT> new_value_high)930 TNode<Type> CodeAssembler::AtomicCompareExchange64(
931 TNode<RawPtrT> base, TNode<WordT> offset, TNode<UintPtrT> old_value,
932 TNode<UintPtrT> new_value, TNode<UintPtrT> old_value_high,
933 TNode<UintPtrT> new_value_high) {
934 // This uses Uint64() intentionally: AtomicCompareExchange is not implemented
935 // for Int64(), which is fine because the machine instruction only cares
936 // about words.
937 return UncheckedCast<Type>(raw_assembler()->AtomicCompareExchange64(
938 base, offset, old_value, old_value_high, new_value, new_value_high));
939 }
940
941 template TNode<AtomicInt64> CodeAssembler::AtomicCompareExchange64<AtomicInt64>(
942 TNode<RawPtrT> base, TNode<WordT> offset, TNode<UintPtrT> old_value,
943 TNode<UintPtrT> new_value, TNode<UintPtrT> old_value_high,
944 TNode<UintPtrT> new_value_high);
945 template TNode<AtomicUint64>
946 CodeAssembler::AtomicCompareExchange64<AtomicUint64>(
947 TNode<RawPtrT> base, TNode<WordT> offset, TNode<UintPtrT> old_value,
948 TNode<UintPtrT> new_value, TNode<UintPtrT> old_value_high,
949 TNode<UintPtrT> new_value_high);
950
StoreRoot(RootIndex root_index,TNode<Object> value)951 void CodeAssembler::StoreRoot(RootIndex root_index, TNode<Object> value) {
952 DCHECK(!RootsTable::IsImmortalImmovable(root_index));
953 TNode<ExternalReference> isolate_root =
954 ExternalConstant(ExternalReference::isolate_root(isolate()));
955 int offset = IsolateData::root_slot_offset(root_index);
956 StoreFullTaggedNoWriteBarrier(isolate_root, IntPtrConstant(offset), value);
957 }
958
Projection(int index,Node * value)959 Node* CodeAssembler::Projection(int index, Node* value) {
960 DCHECK_LT(index, value->op()->ValueOutputCount());
961 return raw_assembler()->Projection(index, value);
962 }
963
OptimizedAllocate(TNode<IntPtrT> size,AllocationType allocation,AllowLargeObjects allow_large_objects)964 TNode<HeapObject> CodeAssembler::OptimizedAllocate(
965 TNode<IntPtrT> size, AllocationType allocation,
966 AllowLargeObjects allow_large_objects) {
967 return UncheckedCast<HeapObject>(raw_assembler()->OptimizedAllocate(
968 size, allocation, allow_large_objects));
969 }
970
HandleException(Node * node)971 void CodeAssembler::HandleException(Node* node) {
972 if (state_->exception_handler_labels_.size() == 0) return;
973 CodeAssemblerExceptionHandlerLabel* label =
974 state_->exception_handler_labels_.back();
975
976 if (node->op()->HasProperty(Operator::kNoThrow)) {
977 return;
978 }
979
980 Label success(this), exception(this, Label::kDeferred);
981 success.MergeVariables();
982 exception.MergeVariables();
983
984 raw_assembler()->Continuations(node, success.label_, exception.label_);
985
986 Bind(&exception);
987 const Operator* op = raw_assembler()->common()->IfException();
988 Node* exception_value = raw_assembler()->AddNode(op, node, node);
989 label->AddInputs({UncheckedCast<Object>(exception_value)});
990 Goto(label->plain_label());
991
992 Bind(&success);
993 raw_assembler()->AddNode(raw_assembler()->common()->IfSuccess(), node);
994 }
995
996 namespace {
997 template <size_t kMaxSize>
998 class NodeArray {
999 public:
Add(Node * node)1000 void Add(Node* node) {
1001 DCHECK_GT(kMaxSize, size());
1002 *ptr_++ = node;
1003 }
1004
data() const1005 Node* const* data() const { return arr_; }
size() const1006 int size() const { return static_cast<int>(ptr_ - arr_); }
1007
1008 private:
1009 Node* arr_[kMaxSize];
1010 Node** ptr_ = arr_;
1011 };
1012 } // namespace
1013
CallRuntimeImpl(Runtime::FunctionId function,TNode<Object> context,std::initializer_list<TNode<Object>> args)1014 Node* CodeAssembler::CallRuntimeImpl(
1015 Runtime::FunctionId function, TNode<Object> context,
1016 std::initializer_list<TNode<Object>> args) {
1017 int result_size = Runtime::FunctionForId(function)->result_size;
1018 TNode<CodeT> centry =
1019 HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size));
1020 constexpr size_t kMaxNumArgs = 6;
1021 DCHECK_GE(kMaxNumArgs, args.size());
1022 int argc = static_cast<int>(args.size());
1023 auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
1024 zone(), function, argc, Operator::kNoProperties,
1025 Runtime::MayAllocate(function) ? CallDescriptor::kNoFlags
1026 : CallDescriptor::kNoAllocate);
1027
1028 TNode<ExternalReference> ref =
1029 ExternalConstant(ExternalReference::Create(function));
1030 TNode<Int32T> arity = Int32Constant(argc);
1031
1032 NodeArray<kMaxNumArgs + 4> inputs;
1033 inputs.Add(centry);
1034 for (auto arg : args) inputs.Add(arg);
1035 inputs.Add(ref);
1036 inputs.Add(arity);
1037 inputs.Add(context);
1038
1039 CallPrologue();
1040 Node* return_value =
1041 raw_assembler()->CallN(call_descriptor, inputs.size(), inputs.data());
1042 HandleException(return_value);
1043 CallEpilogue();
1044 return return_value;
1045 }
1046
TailCallRuntimeImpl(Runtime::FunctionId function,TNode<Int32T> arity,TNode<Object> context,std::initializer_list<TNode<Object>> args)1047 void CodeAssembler::TailCallRuntimeImpl(
1048 Runtime::FunctionId function, TNode<Int32T> arity, TNode<Object> context,
1049 std::initializer_list<TNode<Object>> args) {
1050 int result_size = Runtime::FunctionForId(function)->result_size;
1051 TNode<CodeT> centry =
1052 HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size));
1053 constexpr size_t kMaxNumArgs = 6;
1054 DCHECK_GE(kMaxNumArgs, args.size());
1055 int argc = static_cast<int>(args.size());
1056 auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
1057 zone(), function, argc, Operator::kNoProperties,
1058 CallDescriptor::kNoFlags);
1059
1060 TNode<ExternalReference> ref =
1061 ExternalConstant(ExternalReference::Create(function));
1062
1063 NodeArray<kMaxNumArgs + 4> inputs;
1064 inputs.Add(centry);
1065 for (auto arg : args) inputs.Add(arg);
1066 inputs.Add(ref);
1067 inputs.Add(arity);
1068 inputs.Add(context);
1069
1070 raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data());
1071 }
1072
CallStubN(StubCallMode call_mode,const CallInterfaceDescriptor & descriptor,int input_count,Node * const * inputs)1073 Node* CodeAssembler::CallStubN(StubCallMode call_mode,
1074 const CallInterfaceDescriptor& descriptor,
1075 int input_count, Node* const* inputs) {
1076 DCHECK(call_mode == StubCallMode::kCallCodeObject ||
1077 call_mode == StubCallMode::kCallBuiltinPointer);
1078
1079 // implicit nodes are target and optionally context.
1080 int implicit_nodes = descriptor.HasContextParameter() ? 2 : 1;
1081 DCHECK_LE(implicit_nodes, input_count);
1082 int argc = input_count - implicit_nodes;
1083 #ifdef DEBUG
1084 if (descriptor.AllowVarArgs()) {
1085 DCHECK_LE(descriptor.GetParameterCount(), argc);
1086 } else {
1087 DCHECK_EQ(descriptor.GetParameterCount(), argc);
1088 }
1089 #endif
1090 // Extra arguments not mentioned in the descriptor are passed on the stack.
1091 int stack_parameter_count = argc - descriptor.GetRegisterParameterCount();
1092 DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count);
1093
1094 auto call_descriptor = Linkage::GetStubCallDescriptor(
1095 zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags,
1096 Operator::kNoProperties, call_mode);
1097
1098 CallPrologue();
1099 Node* return_value =
1100 raw_assembler()->CallN(call_descriptor, input_count, inputs);
1101 HandleException(return_value);
1102 CallEpilogue();
1103 return return_value;
1104 }
1105
TailCallStubImpl(const CallInterfaceDescriptor & descriptor,TNode<CodeT> target,TNode<Object> context,std::initializer_list<Node * > args)1106 void CodeAssembler::TailCallStubImpl(const CallInterfaceDescriptor& descriptor,
1107 TNode<CodeT> target, TNode<Object> context,
1108 std::initializer_list<Node*> args) {
1109 constexpr size_t kMaxNumArgs = 11;
1110 DCHECK_GE(kMaxNumArgs, args.size());
1111 DCHECK_EQ(descriptor.GetParameterCount(), args.size());
1112 auto call_descriptor = Linkage::GetStubCallDescriptor(
1113 zone(), descriptor, descriptor.GetStackParameterCount(),
1114 CallDescriptor::kNoFlags, Operator::kNoProperties);
1115
1116 NodeArray<kMaxNumArgs + 2> inputs;
1117 inputs.Add(target);
1118 for (auto arg : args) inputs.Add(arg);
1119 if (descriptor.HasContextParameter()) {
1120 inputs.Add(context);
1121 }
1122
1123 raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data());
1124 }
1125
CallStubRImpl(StubCallMode call_mode,const CallInterfaceDescriptor & descriptor,TNode<Object> target,TNode<Object> context,std::initializer_list<Node * > args)1126 Node* CodeAssembler::CallStubRImpl(StubCallMode call_mode,
1127 const CallInterfaceDescriptor& descriptor,
1128 TNode<Object> target, TNode<Object> context,
1129 std::initializer_list<Node*> args) {
1130 DCHECK(call_mode == StubCallMode::kCallCodeObject ||
1131 call_mode == StubCallMode::kCallBuiltinPointer);
1132
1133 constexpr size_t kMaxNumArgs = 10;
1134 DCHECK_GE(kMaxNumArgs, args.size());
1135
1136 NodeArray<kMaxNumArgs + 2> inputs;
1137 inputs.Add(target);
1138 for (auto arg : args) inputs.Add(arg);
1139 if (descriptor.HasContextParameter()) {
1140 inputs.Add(context);
1141 }
1142
1143 return CallStubN(call_mode, descriptor, inputs.size(), inputs.data());
1144 }
1145
CallJSStubImpl(const CallInterfaceDescriptor & descriptor,TNode<Object> target,TNode<Object> context,TNode<Object> function,base::Optional<TNode<Object>> new_target,TNode<Int32T> arity,std::initializer_list<Node * > args)1146 Node* CodeAssembler::CallJSStubImpl(const CallInterfaceDescriptor& descriptor,
1147 TNode<Object> target, TNode<Object> context,
1148 TNode<Object> function,
1149 base::Optional<TNode<Object>> new_target,
1150 TNode<Int32T> arity,
1151 std::initializer_list<Node*> args) {
1152 constexpr size_t kMaxNumArgs = 10;
1153 DCHECK_GE(kMaxNumArgs, args.size());
1154 NodeArray<kMaxNumArgs + 5> inputs;
1155 inputs.Add(target);
1156 inputs.Add(function);
1157 if (new_target) {
1158 inputs.Add(*new_target);
1159 }
1160 inputs.Add(arity);
1161 for (auto arg : args) inputs.Add(arg);
1162 if (descriptor.HasContextParameter()) {
1163 inputs.Add(context);
1164 }
1165 return CallStubN(StubCallMode::kCallCodeObject, descriptor, inputs.size(),
1166 inputs.data());
1167 }
1168
TailCallStubThenBytecodeDispatchImpl(const CallInterfaceDescriptor & descriptor,Node * target,Node * context,std::initializer_list<Node * > args)1169 void CodeAssembler::TailCallStubThenBytecodeDispatchImpl(
1170 const CallInterfaceDescriptor& descriptor, Node* target, Node* context,
1171 std::initializer_list<Node*> args) {
1172 constexpr size_t kMaxNumArgs = 6;
1173 DCHECK_GE(kMaxNumArgs, args.size());
1174
1175 DCHECK_LE(descriptor.GetParameterCount(), args.size());
1176 int argc = static_cast<int>(args.size());
1177 // Extra arguments not mentioned in the descriptor are passed on the stack.
1178 int stack_parameter_count = argc - descriptor.GetRegisterParameterCount();
1179 DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count);
1180 auto call_descriptor = Linkage::GetStubCallDescriptor(
1181 zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags,
1182 Operator::kNoProperties);
1183
1184 NodeArray<kMaxNumArgs + 2> inputs;
1185 inputs.Add(target);
1186 for (auto arg : args) inputs.Add(arg);
1187 inputs.Add(context);
1188
1189 raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data());
1190 }
1191
1192 template <class... TArgs>
TailCallBytecodeDispatch(const CallInterfaceDescriptor & descriptor,TNode<RawPtrT> target,TArgs...args)1193 void CodeAssembler::TailCallBytecodeDispatch(
1194 const CallInterfaceDescriptor& descriptor, TNode<RawPtrT> target,
1195 TArgs... args) {
1196 DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args));
1197 auto call_descriptor = Linkage::GetBytecodeDispatchCallDescriptor(
1198 zone(), descriptor, descriptor.GetStackParameterCount());
1199
1200 Node* nodes[] = {target, args...};
1201 CHECK_EQ(descriptor.GetParameterCount() + 1, arraysize(nodes));
1202 raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes);
1203 }
1204
1205 // Instantiate TailCallBytecodeDispatch() for argument counts used by
1206 // CSA-generated code
1207 template V8_EXPORT_PRIVATE void CodeAssembler::TailCallBytecodeDispatch(
1208 const CallInterfaceDescriptor& descriptor, TNode<RawPtrT> target,
1209 TNode<Object>, TNode<IntPtrT>, TNode<BytecodeArray>,
1210 TNode<ExternalReference>);
1211
TailCallJSCode(TNode<CodeT> code,TNode<Context> context,TNode<JSFunction> function,TNode<Object> new_target,TNode<Int32T> arg_count)1212 void CodeAssembler::TailCallJSCode(TNode<CodeT> code, TNode<Context> context,
1213 TNode<JSFunction> function,
1214 TNode<Object> new_target,
1215 TNode<Int32T> arg_count) {
1216 JSTrampolineDescriptor descriptor;
1217 auto call_descriptor = Linkage::GetStubCallDescriptor(
1218 zone(), descriptor, descriptor.GetStackParameterCount(),
1219 CallDescriptor::kFixedTargetRegister, Operator::kNoProperties);
1220
1221 Node* nodes[] = {code, function, new_target, arg_count, context};
1222 CHECK_EQ(descriptor.GetParameterCount() + 2, arraysize(nodes));
1223 raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes);
1224 }
1225
CallCFunctionN(Signature<MachineType> * signature,int input_count,Node * const * inputs)1226 Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature,
1227 int input_count, Node* const* inputs) {
1228 auto call_descriptor = Linkage::GetSimplifiedCDescriptor(zone(), signature);
1229 return raw_assembler()->CallN(call_descriptor, input_count, inputs);
1230 }
1231
CallCFunction(Node * function,base::Optional<MachineType> return_type,std::initializer_list<CodeAssembler::CFunctionArg> args)1232 Node* CodeAssembler::CallCFunction(
1233 Node* function, base::Optional<MachineType> return_type,
1234 std::initializer_list<CodeAssembler::CFunctionArg> args) {
1235 return raw_assembler()->CallCFunction(function, return_type, args);
1236 }
1237
CallCFunctionWithoutFunctionDescriptor(Node * function,MachineType return_type,std::initializer_list<CodeAssembler::CFunctionArg> args)1238 Node* CodeAssembler::CallCFunctionWithoutFunctionDescriptor(
1239 Node* function, MachineType return_type,
1240 std::initializer_list<CodeAssembler::CFunctionArg> args) {
1241 return raw_assembler()->CallCFunctionWithoutFunctionDescriptor(
1242 function, return_type, args);
1243 }
1244
CallCFunctionWithCallerSavedRegisters(Node * function,MachineType return_type,SaveFPRegsMode mode,std::initializer_list<CodeAssembler::CFunctionArg> args)1245 Node* CodeAssembler::CallCFunctionWithCallerSavedRegisters(
1246 Node* function, MachineType return_type, SaveFPRegsMode mode,
1247 std::initializer_list<CodeAssembler::CFunctionArg> args) {
1248 DCHECK(return_type.LessThanOrEqualPointerSize());
1249 return raw_assembler()->CallCFunctionWithCallerSavedRegisters(
1250 function, return_type, mode, args);
1251 }
1252
Goto(Label * label)1253 void CodeAssembler::Goto(Label* label) {
1254 label->MergeVariables();
1255 raw_assembler()->Goto(label->label_);
1256 }
1257
GotoIf(TNode<IntegralT> condition,Label * true_label)1258 void CodeAssembler::GotoIf(TNode<IntegralT> condition, Label* true_label) {
1259 Label false_label(this);
1260 Branch(condition, true_label, &false_label);
1261 Bind(&false_label);
1262 }
1263
GotoIfNot(TNode<IntegralT> condition,Label * false_label)1264 void CodeAssembler::GotoIfNot(TNode<IntegralT> condition, Label* false_label) {
1265 Label true_label(this);
1266 Branch(condition, &true_label, false_label);
1267 Bind(&true_label);
1268 }
1269
Branch(TNode<IntegralT> condition,Label * true_label,Label * false_label)1270 void CodeAssembler::Branch(TNode<IntegralT> condition, Label* true_label,
1271 Label* false_label) {
1272 int32_t constant;
1273 if (TryToInt32Constant(condition, &constant)) {
1274 if ((true_label->is_used() || true_label->is_bound()) &&
1275 (false_label->is_used() || false_label->is_bound())) {
1276 return Goto(constant ? true_label : false_label);
1277 }
1278 }
1279 true_label->MergeVariables();
1280 false_label->MergeVariables();
1281 return raw_assembler()->Branch(condition, true_label->label_,
1282 false_label->label_);
1283 }
1284
Branch(TNode<BoolT> condition,const std::function<void ()> & true_body,const std::function<void ()> & false_body)1285 void CodeAssembler::Branch(TNode<BoolT> condition,
1286 const std::function<void()>& true_body,
1287 const std::function<void()>& false_body) {
1288 int32_t constant;
1289 if (TryToInt32Constant(condition, &constant)) {
1290 return constant ? true_body() : false_body();
1291 }
1292
1293 Label vtrue(this), vfalse(this);
1294 Branch(condition, &vtrue, &vfalse);
1295
1296 Bind(&vtrue);
1297 true_body();
1298
1299 Bind(&vfalse);
1300 false_body();
1301 }
1302
Branch(TNode<BoolT> condition,Label * true_label,const std::function<void ()> & false_body)1303 void CodeAssembler::Branch(TNode<BoolT> condition, Label* true_label,
1304 const std::function<void()>& false_body) {
1305 int32_t constant;
1306 if (TryToInt32Constant(condition, &constant)) {
1307 return constant ? Goto(true_label) : false_body();
1308 }
1309
1310 Label vfalse(this);
1311 Branch(condition, true_label, &vfalse);
1312 Bind(&vfalse);
1313 false_body();
1314 }
1315
Branch(TNode<BoolT> condition,const std::function<void ()> & true_body,Label * false_label)1316 void CodeAssembler::Branch(TNode<BoolT> condition,
1317 const std::function<void()>& true_body,
1318 Label* false_label) {
1319 int32_t constant;
1320 if (TryToInt32Constant(condition, &constant)) {
1321 return constant ? true_body() : Goto(false_label);
1322 }
1323
1324 Label vtrue(this);
1325 Branch(condition, &vtrue, false_label);
1326 Bind(&vtrue);
1327 true_body();
1328 }
1329
Switch(Node * index,Label * default_label,const int32_t * case_values,Label ** case_labels,size_t case_count)1330 void CodeAssembler::Switch(Node* index, Label* default_label,
1331 const int32_t* case_values, Label** case_labels,
1332 size_t case_count) {
1333 RawMachineLabel** labels = zone()->NewArray<RawMachineLabel*>(case_count);
1334 for (size_t i = 0; i < case_count; ++i) {
1335 labels[i] = case_labels[i]->label_;
1336 case_labels[i]->MergeVariables();
1337 }
1338 default_label->MergeVariables();
1339 return raw_assembler()->Switch(index, default_label->label_, case_values,
1340 labels, case_count);
1341 }
1342
UnalignedLoadSupported(MachineRepresentation rep) const1343 bool CodeAssembler::UnalignedLoadSupported(MachineRepresentation rep) const {
1344 return raw_assembler()->machine()->UnalignedLoadSupported(rep);
1345 }
UnalignedStoreSupported(MachineRepresentation rep) const1346 bool CodeAssembler::UnalignedStoreSupported(MachineRepresentation rep) const {
1347 return raw_assembler()->machine()->UnalignedStoreSupported(rep);
1348 }
1349
1350 // RawMachineAssembler delegate helpers:
isolate() const1351 Isolate* CodeAssembler::isolate() const { return raw_assembler()->isolate(); }
1352
factory() const1353 Factory* CodeAssembler::factory() const { return isolate()->factory(); }
1354
zone() const1355 Zone* CodeAssembler::zone() const { return raw_assembler()->zone(); }
1356
IsExceptionHandlerActive() const1357 bool CodeAssembler::IsExceptionHandlerActive() const {
1358 return state_->exception_handler_labels_.size() != 0;
1359 }
1360
raw_assembler() const1361 RawMachineAssembler* CodeAssembler::raw_assembler() const {
1362 return state_->raw_assembler_.get();
1363 }
1364
jsgraph() const1365 JSGraph* CodeAssembler::jsgraph() const { return state_->jsgraph_; }
1366
1367 // The core implementation of Variable is stored through an indirection so
1368 // that it can outlive the often block-scoped Variable declarations. This is
1369 // needed to ensure that variable binding and merging through phis can
1370 // properly be verified.
1371 class CodeAssemblerVariable::Impl : public ZoneObject {
1372 public:
Impl(MachineRepresentation rep,CodeAssemblerState::VariableId id)1373 explicit Impl(MachineRepresentation rep, CodeAssemblerState::VariableId id)
1374 :
1375 #if DEBUG
1376 debug_info_(AssemblerDebugInfo(nullptr, nullptr, -1)),
1377 #endif
1378 value_(nullptr),
1379 rep_(rep),
1380 var_id_(id) {
1381 }
1382
1383 #if DEBUG
debug_info() const1384 AssemblerDebugInfo debug_info() const { return debug_info_; }
set_debug_info(AssemblerDebugInfo debug_info)1385 void set_debug_info(AssemblerDebugInfo debug_info) {
1386 debug_info_ = debug_info;
1387 }
1388
1389 AssemblerDebugInfo debug_info_;
1390 #endif // DEBUG
operator <(const CodeAssemblerVariable::Impl & other) const1391 bool operator<(const CodeAssemblerVariable::Impl& other) const {
1392 return var_id_ < other.var_id_;
1393 }
1394 Node* value_;
1395 MachineRepresentation rep_;
1396 CodeAssemblerState::VariableId var_id_;
1397 };
1398
operator ()(const CodeAssemblerVariable::Impl * a,const CodeAssemblerVariable::Impl * b) const1399 bool CodeAssemblerVariable::ImplComparator::operator()(
1400 const CodeAssemblerVariable::Impl* a,
1401 const CodeAssemblerVariable::Impl* b) const {
1402 return *a < *b;
1403 }
1404
CodeAssemblerVariable(CodeAssembler * assembler,MachineRepresentation rep)1405 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler,
1406 MachineRepresentation rep)
1407 : impl_(assembler->zone()->New<Impl>(rep,
1408 assembler->state()->NextVariableId())),
1409 state_(assembler->state()) {
1410 state_->variables_.insert(impl_);
1411 }
1412
CodeAssemblerVariable(CodeAssembler * assembler,MachineRepresentation rep,Node * initial_value)1413 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler,
1414 MachineRepresentation rep,
1415 Node* initial_value)
1416 : CodeAssemblerVariable(assembler, rep) {
1417 Bind(initial_value);
1418 }
1419
1420 #if DEBUG
CodeAssemblerVariable(CodeAssembler * assembler,AssemblerDebugInfo debug_info,MachineRepresentation rep)1421 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler,
1422 AssemblerDebugInfo debug_info,
1423 MachineRepresentation rep)
1424 : impl_(assembler->zone()->New<Impl>(rep,
1425 assembler->state()->NextVariableId())),
1426 state_(assembler->state()) {
1427 impl_->set_debug_info(debug_info);
1428 state_->variables_.insert(impl_);
1429 }
1430
CodeAssemblerVariable(CodeAssembler * assembler,AssemblerDebugInfo debug_info,MachineRepresentation rep,Node * initial_value)1431 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler,
1432 AssemblerDebugInfo debug_info,
1433 MachineRepresentation rep,
1434 Node* initial_value)
1435 : CodeAssemblerVariable(assembler, debug_info, rep) {
1436 impl_->set_debug_info(debug_info);
1437 Bind(initial_value);
1438 }
1439 #endif // DEBUG
1440
~CodeAssemblerVariable()1441 CodeAssemblerVariable::~CodeAssemblerVariable() {
1442 state_->variables_.erase(impl_);
1443 }
1444
Bind(Node * value)1445 void CodeAssemblerVariable::Bind(Node* value) { impl_->value_ = value; }
1446
value() const1447 Node* CodeAssemblerVariable::value() const {
1448 #if DEBUG
1449 if (!IsBound()) {
1450 std::stringstream str;
1451 str << "#Use of unbound variable:"
1452 << "#\n Variable: " << *this << "#\n Current Block: ";
1453 state_->PrintCurrentBlock(str);
1454 FATAL("%s", str.str().c_str());
1455 }
1456 if (!state_->InsideBlock()) {
1457 std::stringstream str;
1458 str << "#Accessing variable value outside a block:"
1459 << "#\n Variable: " << *this;
1460 FATAL("%s", str.str().c_str());
1461 }
1462 #endif // DEBUG
1463 return impl_->value_;
1464 }
1465
rep() const1466 MachineRepresentation CodeAssemblerVariable::rep() const { return impl_->rep_; }
1467
IsBound() const1468 bool CodeAssemblerVariable::IsBound() const { return impl_->value_ != nullptr; }
1469
operator <<(std::ostream & os,const CodeAssemblerVariable::Impl & impl)1470 std::ostream& operator<<(std::ostream& os,
1471 const CodeAssemblerVariable::Impl& impl) {
1472 #if DEBUG
1473 AssemblerDebugInfo info = impl.debug_info();
1474 if (info.name) os << "V" << info;
1475 #endif // DEBUG
1476 return os;
1477 }
1478
operator <<(std::ostream & os,const CodeAssemblerVariable & variable)1479 std::ostream& operator<<(std::ostream& os,
1480 const CodeAssemblerVariable& variable) {
1481 os << *variable.impl_;
1482 return os;
1483 }
1484
CodeAssemblerLabel(CodeAssembler * assembler,size_t vars_count,CodeAssemblerVariable * const * vars,CodeAssemblerLabel::Type type)1485 CodeAssemblerLabel::CodeAssemblerLabel(CodeAssembler* assembler,
1486 size_t vars_count,
1487 CodeAssemblerVariable* const* vars,
1488 CodeAssemblerLabel::Type type)
1489 : bound_(false),
1490 merge_count_(0),
1491 state_(assembler->state()),
1492 label_(nullptr) {
1493 label_ = assembler->zone()->New<RawMachineLabel>(
1494 type == kDeferred ? RawMachineLabel::kDeferred
1495 : RawMachineLabel::kNonDeferred);
1496 for (size_t i = 0; i < vars_count; ++i) {
1497 variable_phis_[vars[i]->impl_] = nullptr;
1498 }
1499 }
1500
~CodeAssemblerLabel()1501 CodeAssemblerLabel::~CodeAssemblerLabel() { label_->~RawMachineLabel(); }
1502
MergeVariables()1503 void CodeAssemblerLabel::MergeVariables() {
1504 ++merge_count_;
1505 for (CodeAssemblerVariable::Impl* var : state_->variables_) {
1506 size_t count = 0;
1507 Node* node = var->value_;
1508 if (node != nullptr) {
1509 auto i = variable_merges_.find(var);
1510 if (i != variable_merges_.end()) {
1511 i->second.push_back(node);
1512 count = i->second.size();
1513 } else {
1514 count = 1;
1515 variable_merges_[var] = std::vector<Node*>(1, node);
1516 }
1517 }
1518 // If the following asserts, then you've jumped to a label without a bound
1519 // variable along that path that expects to merge its value into a phi.
1520 // This can also occur if a label is bound that is never jumped to.
1521 DCHECK(variable_phis_.find(var) == variable_phis_.end() ||
1522 count == merge_count_);
1523 USE(count);
1524
1525 // If the label is already bound, we already know the set of variables to
1526 // merge and phi nodes have already been created.
1527 if (bound_) {
1528 auto phi = variable_phis_.find(var);
1529 if (phi != variable_phis_.end()) {
1530 DCHECK_NOT_NULL(phi->second);
1531 state_->raw_assembler_->AppendPhiInput(phi->second, node);
1532 } else {
1533 auto i = variable_merges_.find(var);
1534 if (i != variable_merges_.end()) {
1535 // If the following assert fires, then you've declared a variable that
1536 // has the same bound value along all paths up until the point you
1537 // bound this label, but then later merged a path with a new value for
1538 // the variable after the label bind (it's not possible to add phis to
1539 // the bound label after the fact, just make sure to list the variable
1540 // in the label's constructor's list of merged variables).
1541 #if DEBUG
1542 if (find_if(i->second.begin(), i->second.end(),
1543 [node](Node* e) -> bool { return node != e; }) !=
1544 i->second.end()) {
1545 std::stringstream str;
1546 str << "Unmerged variable found when jumping to block. \n"
1547 << "# Variable: " << *var;
1548 if (bound_) {
1549 str << "\n# Target block: " << *label_->block();
1550 }
1551 str << "\n# Current Block: ";
1552 state_->PrintCurrentBlock(str);
1553 FATAL("%s", str.str().c_str());
1554 }
1555 #endif // DEBUG
1556 }
1557 }
1558 }
1559 }
1560 }
1561
1562 #if DEBUG
Bind(AssemblerDebugInfo debug_info)1563 void CodeAssemblerLabel::Bind(AssemblerDebugInfo debug_info) {
1564 if (bound_) {
1565 std::stringstream str;
1566 str << "Cannot bind the same label twice:"
1567 << "\n# current: " << debug_info
1568 << "\n# previous: " << *label_->block();
1569 FATAL("%s", str.str().c_str());
1570 }
1571 if (FLAG_enable_source_at_csa_bind) {
1572 state_->raw_assembler_->SetCurrentExternalSourcePosition(
1573 {debug_info.file, debug_info.line});
1574 }
1575 state_->raw_assembler_->Bind(label_, debug_info);
1576 UpdateVariablesAfterBind();
1577 }
1578 #endif // DEBUG
1579
Bind()1580 void CodeAssemblerLabel::Bind() {
1581 DCHECK(!bound_);
1582 state_->raw_assembler_->Bind(label_);
1583 UpdateVariablesAfterBind();
1584 }
1585
UpdateVariablesAfterBind()1586 void CodeAssemblerLabel::UpdateVariablesAfterBind() {
1587 // Make sure that all variables that have changed along any path up to this
1588 // point are marked as merge variables.
1589 for (auto var : state_->variables_) {
1590 Node* shared_value = nullptr;
1591 auto i = variable_merges_.find(var);
1592 if (i != variable_merges_.end()) {
1593 for (auto value : i->second) {
1594 DCHECK_NOT_NULL(value);
1595 if (value != shared_value) {
1596 if (shared_value == nullptr) {
1597 shared_value = value;
1598 } else {
1599 variable_phis_[var] = nullptr;
1600 }
1601 }
1602 }
1603 }
1604 }
1605
1606 for (auto var : variable_phis_) {
1607 CodeAssemblerVariable::Impl* var_impl = var.first;
1608 auto i = variable_merges_.find(var_impl);
1609 #if DEBUG
1610 bool not_found = i == variable_merges_.end();
1611 if (not_found || i->second.size() != merge_count_) {
1612 std::stringstream str;
1613 str << "A variable that has been marked as beeing merged at the label"
1614 << "\n# doesn't have a bound value along all of the paths that "
1615 << "\n# have been merged into the label up to this point."
1616 << "\n#"
1617 << "\n# This can happen in the following cases:"
1618 << "\n# - By explicitly marking it so in the label constructor"
1619 << "\n# - By having seen different bound values at branches"
1620 << "\n#"
1621 << "\n# Merge count: expected=" << merge_count_
1622 << " vs. found=" << (not_found ? 0 : i->second.size())
1623 << "\n# Variable: " << *var_impl
1624 << "\n# Current Block: " << *label_->block();
1625 FATAL("%s", str.str().c_str());
1626 }
1627 #endif // DEBUG
1628 Node* phi = state_->raw_assembler_->Phi(
1629 var.first->rep_, static_cast<int>(merge_count_), &(i->second[0]));
1630 variable_phis_[var_impl] = phi;
1631 }
1632
1633 // Bind all variables to a merge phi, the common value along all paths or
1634 // null.
1635 for (auto var : state_->variables_) {
1636 auto i = variable_phis_.find(var);
1637 if (i != variable_phis_.end()) {
1638 var->value_ = i->second;
1639 } else {
1640 auto j = variable_merges_.find(var);
1641 if (j != variable_merges_.end() && j->second.size() == merge_count_) {
1642 var->value_ = j->second.back();
1643 } else {
1644 var->value_ = nullptr;
1645 }
1646 }
1647 }
1648
1649 bound_ = true;
1650 }
1651
AddInputs(std::vector<Node * > inputs)1652 void CodeAssemblerParameterizedLabelBase::AddInputs(std::vector<Node*> inputs) {
1653 if (!phi_nodes_.empty()) {
1654 DCHECK_EQ(inputs.size(), phi_nodes_.size());
1655 for (size_t i = 0; i < inputs.size(); ++i) {
1656 // We use {nullptr} as a sentinel for an uninitialized value.
1657 if (phi_nodes_[i] == nullptr) continue;
1658 state_->raw_assembler_->AppendPhiInput(phi_nodes_[i], inputs[i]);
1659 }
1660 } else {
1661 DCHECK_EQ(inputs.size(), phi_inputs_.size());
1662 for (size_t i = 0; i < inputs.size(); ++i) {
1663 phi_inputs_[i].push_back(inputs[i]);
1664 }
1665 }
1666 }
1667
CreatePhi(MachineRepresentation rep,const std::vector<Node * > & inputs)1668 Node* CodeAssemblerParameterizedLabelBase::CreatePhi(
1669 MachineRepresentation rep, const std::vector<Node*>& inputs) {
1670 for (Node* input : inputs) {
1671 // We use {nullptr} as a sentinel for an uninitialized value. We must not
1672 // create phi nodes for these.
1673 if (input == nullptr) return nullptr;
1674 }
1675 return state_->raw_assembler_->Phi(rep, static_cast<int>(inputs.size()),
1676 &inputs.front());
1677 }
1678
CreatePhis(std::vector<MachineRepresentation> representations)1679 const std::vector<Node*>& CodeAssemblerParameterizedLabelBase::CreatePhis(
1680 std::vector<MachineRepresentation> representations) {
1681 DCHECK(is_used());
1682 DCHECK(phi_nodes_.empty());
1683 phi_nodes_.reserve(phi_inputs_.size());
1684 DCHECK_EQ(representations.size(), phi_inputs_.size());
1685 for (size_t i = 0; i < phi_inputs_.size(); ++i) {
1686 phi_nodes_.push_back(CreatePhi(representations[i], phi_inputs_[i]));
1687 }
1688 return phi_nodes_;
1689 }
1690
PushExceptionHandler(CodeAssemblerExceptionHandlerLabel * label)1691 void CodeAssemblerState::PushExceptionHandler(
1692 CodeAssemblerExceptionHandlerLabel* label) {
1693 exception_handler_labels_.push_back(label);
1694 }
1695
PopExceptionHandler()1696 void CodeAssemblerState::PopExceptionHandler() {
1697 exception_handler_labels_.pop_back();
1698 }
1699
ScopedExceptionHandler(CodeAssembler * assembler,CodeAssemblerExceptionHandlerLabel * label)1700 ScopedExceptionHandler::ScopedExceptionHandler(
1701 CodeAssembler* assembler, CodeAssemblerExceptionHandlerLabel* label)
1702 : has_handler_(label != nullptr),
1703 assembler_(assembler),
1704 compatibility_label_(nullptr),
1705 exception_(nullptr) {
1706 if (has_handler_) {
1707 assembler_->state()->PushExceptionHandler(label);
1708 }
1709 }
1710
ScopedExceptionHandler(CodeAssembler * assembler,CodeAssemblerLabel * label,TypedCodeAssemblerVariable<Object> * exception)1711 ScopedExceptionHandler::ScopedExceptionHandler(
1712 CodeAssembler* assembler, CodeAssemblerLabel* label,
1713 TypedCodeAssemblerVariable<Object>* exception)
1714 : has_handler_(label != nullptr),
1715 assembler_(assembler),
1716 compatibility_label_(label),
1717 exception_(exception) {
1718 if (has_handler_) {
1719 label_ = std::make_unique<CodeAssemblerExceptionHandlerLabel>(
1720 assembler, CodeAssemblerLabel::kDeferred);
1721 assembler_->state()->PushExceptionHandler(label_.get());
1722 }
1723 }
1724
~ScopedExceptionHandler()1725 ScopedExceptionHandler::~ScopedExceptionHandler() {
1726 if (has_handler_) {
1727 assembler_->state()->PopExceptionHandler();
1728 }
1729 if (label_ && label_->is_used()) {
1730 CodeAssembler::Label skip(assembler_);
1731 bool inside_block = assembler_->state()->InsideBlock();
1732 if (inside_block) {
1733 assembler_->Goto(&skip);
1734 }
1735 TNode<Object> e;
1736 assembler_->Bind(label_.get(), &e);
1737 if (exception_ != nullptr) *exception_ = e;
1738 assembler_->Goto(compatibility_label_);
1739 if (inside_block) {
1740 assembler_->Bind(&skip);
1741 }
1742 }
1743 }
1744
1745 } // namespace compiler
1746
1747 } // namespace internal
1748 } // namespace v8
1749