1 // Copyright 2017 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_TORQUE_IMPLEMENTATION_VISITOR_H_
6 #define V8_TORQUE_IMPLEMENTATION_VISITOR_H_
7
8 #include <memory>
9 #include <string>
10
11 #include "src/base/macros.h"
12 #include "src/torque/ast.h"
13 #include "src/torque/cfg.h"
14 #include "src/torque/declarations.h"
15 #include "src/torque/global-context.h"
16 #include "src/torque/type-oracle.h"
17 #include "src/torque/types.h"
18 #include "src/torque/utils.h"
19
20 namespace v8 {
21 namespace internal {
22 namespace torque {
23
24 template <typename T>
25 class Binding;
26 class LocalValue;
27 class ImplementationVisitor;
28
29 // LocationReference is the representation of an l-value, so a value that might
30 // allow for assignment. For uniformity, this class can also represent
31 // unassignable temporaries. Assignable values fall in two categories:
32 // - stack ranges that represent mutable variables, including structs.
33 // - field or element access expressions that generate operator calls.
34 class LocationReference {
35 public:
36 // An assignable stack range.
37 static LocationReference VariableAccess(
38 VisitResult variable,
39 base::Optional<Binding<LocalValue>*> binding = base::nullopt) {
40 DCHECK(variable.IsOnStack());
41 LocationReference result;
42 result.variable_ = std::move(variable);
43 result.binding_ = binding;
44 return result;
45 }
46 // An unassignable value. {description} is only used for error messages.
Temporary(VisitResult temporary,std::string description)47 static LocationReference Temporary(VisitResult temporary,
48 std::string description) {
49 LocationReference result;
50 result.temporary_ = std::move(temporary);
51 result.temporary_description_ = std::move(description);
52 return result;
53 }
54 // A heap reference, that is, a tagged value and an offset to encode an inner
55 // pointer.
HeapReference(VisitResult heap_reference)56 static LocationReference HeapReference(VisitResult heap_reference) {
57 LocationReference result;
58 DCHECK(TypeOracle::MatchReferenceGeneric(heap_reference.type()));
59 result.heap_reference_ = std::move(heap_reference);
60 return result;
61 }
62 // A reference to an array on the heap. That is, a tagged value, an offset to
63 // encode an inner pointer, and the number of elements.
HeapSlice(VisitResult heap_slice)64 static LocationReference HeapSlice(VisitResult heap_slice) {
65 LocationReference result;
66 DCHECK(Type::MatchUnaryGeneric(heap_slice.type(),
67 TypeOracle::GetSliceGeneric()));
68 result.heap_slice_ = std::move(heap_slice);
69 return result;
70 }
ArrayAccess(VisitResult base,VisitResult offset)71 static LocationReference ArrayAccess(VisitResult base, VisitResult offset) {
72 LocationReference result;
73 result.eval_function_ = std::string{"[]"};
74 result.assign_function_ = std::string{"[]="};
75 result.call_arguments_ = {base, offset};
76 return result;
77 }
FieldAccess(VisitResult object,std::string fieldname)78 static LocationReference FieldAccess(VisitResult object,
79 std::string fieldname) {
80 LocationReference result;
81 result.eval_function_ = "." + fieldname;
82 result.assign_function_ = "." + fieldname + "=";
83 result.call_arguments_ = {object};
84 return result;
85 }
BitFieldAccess(const LocationReference & object,BitField field)86 static LocationReference BitFieldAccess(const LocationReference& object,
87 BitField field) {
88 LocationReference result;
89 result.bit_field_struct_ = std::make_shared<LocationReference>(object);
90 result.bit_field_ = std::move(field);
91 return result;
92 }
93
IsConst()94 bool IsConst() const {
95 if (IsHeapReference()) {
96 bool is_const;
97 bool success =
98 TypeOracle::MatchReferenceGeneric(heap_reference().type(), &is_const)
99 .has_value();
100 CHECK(success);
101 return is_const;
102 }
103 return IsTemporary();
104 }
105
IsVariableAccess()106 bool IsVariableAccess() const { return variable_.has_value(); }
variable()107 const VisitResult& variable() const {
108 DCHECK(IsVariableAccess());
109 return *variable_;
110 }
IsTemporary()111 bool IsTemporary() const { return temporary_.has_value(); }
temporary()112 const VisitResult& temporary() const {
113 DCHECK(IsTemporary());
114 return *temporary_;
115 }
IsHeapReference()116 bool IsHeapReference() const { return heap_reference_.has_value(); }
heap_reference()117 const VisitResult& heap_reference() const {
118 DCHECK(IsHeapReference());
119 return *heap_reference_;
120 }
IsHeapSlice()121 bool IsHeapSlice() const { return heap_slice_.has_value(); }
heap_slice()122 const VisitResult& heap_slice() const {
123 DCHECK(IsHeapSlice());
124 return *heap_slice_;
125 }
IsBitFieldAccess()126 bool IsBitFieldAccess() const {
127 bool is_bitfield_access = bit_field_struct_ != nullptr;
128 DCHECK_EQ(is_bitfield_access, bit_field_.has_value());
129 return is_bitfield_access;
130 }
bit_field_struct_location()131 const LocationReference& bit_field_struct_location() const {
132 DCHECK(IsBitFieldAccess());
133 return *bit_field_struct_;
134 }
bit_field()135 const BitField& bit_field() const {
136 DCHECK(IsBitFieldAccess());
137 return *bit_field_;
138 }
139
ReferencedType()140 base::Optional<const Type*> ReferencedType() const {
141 if (IsHeapReference()) {
142 return *TypeOracle::MatchReferenceGeneric(heap_reference().type());
143 }
144 if (IsHeapSlice()) {
145 return *Type::MatchUnaryGeneric(heap_slice().type(),
146 TypeOracle::GetSliceGeneric());
147 }
148 if (IsBitFieldAccess()) {
149 return bit_field_->name_and_type.type;
150 }
151 if (IsVariableAccess() || IsHeapSlice() || IsTemporary()) {
152 return GetVisitResult().type();
153 }
154 return base::nullopt;
155 }
156
GetVisitResult()157 const VisitResult& GetVisitResult() const {
158 if (IsVariableAccess()) return variable();
159 if (IsHeapSlice()) return heap_slice();
160 DCHECK(IsTemporary());
161 return temporary();
162 }
163
164 // For error reporting.
temporary_description()165 const std::string& temporary_description() const {
166 DCHECK(IsTemporary());
167 return *temporary_description_;
168 }
169
IsCallAccess()170 bool IsCallAccess() const {
171 bool is_call_access = eval_function_.has_value();
172 DCHECK_EQ(is_call_access, assign_function_.has_value());
173 return is_call_access;
174 }
call_arguments()175 const VisitResultVector& call_arguments() const {
176 DCHECK(IsCallAccess());
177 return call_arguments_;
178 }
eval_function()179 const std::string& eval_function() const {
180 DCHECK(IsCallAccess());
181 return *eval_function_;
182 }
assign_function()183 const std::string& assign_function() const {
184 DCHECK(IsCallAccess());
185 return *assign_function_;
186 }
binding()187 base::Optional<Binding<LocalValue>*> binding() const {
188 DCHECK(IsVariableAccess());
189 return binding_;
190 }
191
192 private:
193 base::Optional<VisitResult> variable_;
194 base::Optional<VisitResult> temporary_;
195 base::Optional<std::string> temporary_description_;
196 base::Optional<VisitResult> heap_reference_;
197 base::Optional<VisitResult> heap_slice_;
198 base::Optional<std::string> eval_function_;
199 base::Optional<std::string> assign_function_;
200 VisitResultVector call_arguments_;
201 base::Optional<Binding<LocalValue>*> binding_;
202
203 // The location of the bitfield struct that contains this bitfield, if this
204 // reference is a bitfield access. Uses a shared_ptr so that LocationReference
205 // is copyable, allowing us to set this field equal to a copy of a
206 // stack-allocated LocationReference.
207 std::shared_ptr<const LocationReference> bit_field_struct_;
208 base::Optional<BitField> bit_field_;
209
210 LocationReference() = default;
211 };
212
213 struct InitializerResults {
214 std::vector<Identifier*> names;
215 std::map<std::string, VisitResult> field_value_map;
216 };
217
218 struct LayoutForInitialization {
219 std::map<std::string, VisitResult> array_lengths;
220 std::map<std::string, VisitResult> offsets;
221 VisitResult size;
222 };
223
224 template <class T>
225 class Binding;
226
227 template <class T>
228 class BindingsManager {
229 public:
TryLookup(const std::string & name)230 base::Optional<Binding<T>*> TryLookup(const std::string& name) {
231 if (name.length() >= 2 && name[0] == '_' && name[1] != '_') {
232 Error("Trying to reference '", name, "' which is marked as unused.")
233 .Throw();
234 }
235 auto binding = current_bindings_[name];
236 if (binding) {
237 (*binding)->SetUsed();
238 }
239 return binding;
240 }
241
242 private:
243 friend class Binding<T>;
244 std::unordered_map<std::string, base::Optional<Binding<T>*>>
245 current_bindings_;
246 };
247
248 template <class T>
249 class Binding : public T {
250 public:
251 template <class... Args>
Binding(BindingsManager<T> * manager,const std::string & name,Args &&...args)252 Binding(BindingsManager<T>* manager, const std::string& name, Args&&... args)
253 : T(std::forward<Args>(args)...),
254 manager_(manager),
255 name_(name),
256 previous_binding_(this),
257 used_(false),
258 written_(false) {
259 std::swap(previous_binding_, manager_->current_bindings_[name]);
260 }
261 template <class... Args>
Binding(BindingsManager<T> * manager,const Identifier * name,Args &&...args)262 Binding(BindingsManager<T>* manager, const Identifier* name, Args&&... args)
263 : Binding(manager, name->value, std::forward<Args>(args)...) {
264 declaration_position_ = name->pos;
265 }
~Binding()266 ~Binding() {
267 if (!used_ && !SkipLintCheck()) {
268 Lint(BindingTypeString(), "'", name_,
269 "' is never used. Prefix with '_' if this is intentional.")
270 .Position(declaration_position_);
271 }
272
273 if (CheckWritten() && !written_ && !SkipLintCheck()) {
274 Lint(BindingTypeString(), "'", name_,
275 "' is never assigned to. Use 'const' instead of 'let'.")
276 .Position(declaration_position_);
277 }
278
279 manager_->current_bindings_[name_] = previous_binding_;
280 }
281
282 std::string BindingTypeString() const;
283 bool CheckWritten() const;
284
name()285 const std::string& name() const { return name_; }
declaration_position()286 SourcePosition declaration_position() const { return declaration_position_; }
287
Used()288 bool Used() const { return used_; }
SetUsed()289 void SetUsed() { used_ = true; }
290
Written()291 bool Written() const { return written_; }
SetWritten()292 void SetWritten() { written_ = true; }
293
294 private:
SkipLintCheck()295 bool SkipLintCheck() const { return name_.length() > 0 && name_[0] == '_'; }
296
297 BindingsManager<T>* manager_;
298 const std::string name_;
299 base::Optional<Binding*> previous_binding_;
300 SourcePosition declaration_position_ = CurrentSourcePosition::Get();
301 bool used_;
302 bool written_;
303 DISALLOW_COPY_AND_ASSIGN(Binding);
304 };
305
306 template <class T>
307 class BlockBindings {
308 public:
BlockBindings(BindingsManager<T> * manager)309 explicit BlockBindings(BindingsManager<T>* manager) : manager_(manager) {}
310 void Add(std::string name, T value, bool mark_as_used = false) {
311 ReportErrorIfAlreadyBound(name);
312 auto binding =
313 std::make_unique<Binding<T>>(manager_, name, std::move(value));
314 if (mark_as_used) binding->SetUsed();
315 bindings_.push_back(std::move(binding));
316 }
317
318 void Add(const Identifier* name, T value, bool mark_as_used = false) {
319 ReportErrorIfAlreadyBound(name->value);
320 auto binding =
321 std::make_unique<Binding<T>>(manager_, name, std::move(value));
322 if (mark_as_used) binding->SetUsed();
323 bindings_.push_back(std::move(binding));
324 }
325
bindings()326 std::vector<Binding<T>*> bindings() const {
327 std::vector<Binding<T>*> result;
328 result.reserve(bindings_.size());
329 for (auto& b : bindings_) {
330 result.push_back(b.get());
331 }
332 return result;
333 }
334
335 private:
ReportErrorIfAlreadyBound(const std::string & name)336 void ReportErrorIfAlreadyBound(const std::string& name) {
337 for (const auto& binding : bindings_) {
338 if (binding->name() == name) {
339 ReportError(
340 "redeclaration of name \"", name,
341 "\" in the same block is illegal, previous declaration at: ",
342 binding->declaration_position());
343 }
344 }
345 }
346
347 BindingsManager<T>* manager_;
348 std::vector<std::unique_ptr<Binding<T>>> bindings_;
349 };
350
351 class LocalValue {
352 public:
LocalValue(LocationReference reference)353 explicit LocalValue(LocationReference reference)
354 : value(std::move(reference)) {}
LocalValue(std::string inaccessible_explanation)355 explicit LocalValue(std::string inaccessible_explanation)
356 : inaccessible_explanation(std::move(inaccessible_explanation)) {}
357
GetLocationReference(Binding<LocalValue> * binding)358 LocationReference GetLocationReference(Binding<LocalValue>* binding) {
359 if (value) {
360 const LocationReference& ref = *value;
361 if (ref.IsVariableAccess()) {
362 // Attach the binding to enable the never-assigned-to lint check.
363 return LocationReference::VariableAccess(ref.GetVisitResult(), binding);
364 }
365 return ref;
366 } else {
367 Error("Cannot access ", binding->name(), ": ", inaccessible_explanation)
368 .Throw();
369 }
370 }
371
IsAccessible()372 bool IsAccessible() const { return value.has_value(); }
373
374 private:
375 base::Optional<LocationReference> value;
376 std::string inaccessible_explanation;
377 };
378
379 struct LocalLabel {
380 Block* block;
381 std::vector<const Type*> parameter_types;
382
383 explicit LocalLabel(Block* block,
384 std::vector<const Type*> parameter_types = {})
blockLocalLabel385 : block(block), parameter_types(std::move(parameter_types)) {}
386 };
387
388 template <>
BindingTypeString()389 inline std::string Binding<LocalValue>::BindingTypeString() const {
390 return "Variable ";
391 }
392 template <>
CheckWritten()393 inline bool Binding<LocalValue>::CheckWritten() const {
394 // Do the check only for non-const variables and non struct types.
395 auto binding = *manager_->current_bindings_[name_];
396 if (!binding->IsAccessible()) return false;
397 const LocationReference& ref = binding->GetLocationReference(binding);
398 if (!ref.IsVariableAccess()) return false;
399 return !ref.GetVisitResult().type()->StructSupertype();
400 }
401 template <>
BindingTypeString()402 inline std::string Binding<LocalLabel>::BindingTypeString() const {
403 return "Label ";
404 }
405 template <>
CheckWritten()406 inline bool Binding<LocalLabel>::CheckWritten() const {
407 return false;
408 }
409
410 struct Arguments {
411 VisitResultVector parameters;
412 std::vector<Binding<LocalLabel>*> labels;
413 };
414
415 // Determine if a callable should be considered as an overload.
416 bool IsCompatibleSignature(const Signature& sig, const TypeVector& types,
417 size_t label_count);
418
419 class ImplementationVisitor {
420 public:
421 void GenerateBuiltinDefinitionsAndInterfaceDescriptors(
422 const std::string& output_directory);
423 void GenerateClassFieldOffsets(const std::string& output_directory);
424 void GenerateBitFields(const std::string& output_directory);
425 void GeneratePrintDefinitions(const std::string& output_directory);
426 void GenerateClassDefinitions(const std::string& output_directory);
427 void GenerateBodyDescriptors(const std::string& output_directory);
428 void GenerateInstanceTypes(const std::string& output_directory);
429 void GenerateClassVerifiers(const std::string& output_directory);
430 void GenerateEnumVerifiers(const std::string& output_directory);
431 void GenerateClassDebugReaders(const std::string& output_directory);
432 void GenerateExportedMacrosAssembler(const std::string& output_directory);
433 void GenerateCSATypes(const std::string& output_directory);
434
435 VisitResult Visit(Expression* expr);
436 const Type* Visit(Statement* stmt);
437
438 template <typename T>
439 void CheckInitializersWellformed(
440 const std::string& aggregate_name, const std::vector<T>& aggregate_fields,
441 const std::vector<NameAndExpression>& initializers,
442 bool ignore_first_field = false) {
443 size_t fields_offset = ignore_first_field ? 1 : 0;
444 size_t fields_size = aggregate_fields.size() - fields_offset;
445 for (size_t i = 0; i < std::min(fields_size, initializers.size()); i++) {
446 const std::string& field_name =
447 aggregate_fields[i + fields_offset].name_and_type.name;
448 Identifier* found_name = initializers[i].name;
449 if (field_name != found_name->value) {
450 Error("Expected field name \"", field_name, "\" instead of \"",
451 found_name->value, "\"")
452 .Position(found_name->pos)
453 .Throw();
454 }
455 }
456 if (fields_size != initializers.size()) {
457 ReportError("expected ", fields_size, " initializers for ",
458 aggregate_name, " found ", initializers.size());
459 }
460 }
461
462 InitializerResults VisitInitializerResults(
463 const ClassType* class_type,
464 const std::vector<NameAndExpression>& expressions);
465 LocationReference GenerateFieldReference(VisitResult object,
466 const Field& field,
467 const ClassType* class_type);
468 LocationReference GenerateFieldReferenceForInit(
469 VisitResult object, const Field& field,
470 const LayoutForInitialization& layout);
471 VisitResult GenerateArrayLength(
472 Expression* array_length, Namespace* nspace,
473 const std::map<std::string, LocalValue>& bindings);
474 VisitResult GenerateArrayLength(VisitResult object, const Field& field);
475 VisitResult GenerateArrayLength(const ClassType* class_type,
476 const InitializerResults& initializer_results,
477 const Field& field);
478 LayoutForInitialization GenerateLayoutForInitialization(
479 const ClassType* class_type,
480 const InitializerResults& initializer_results);
481
482 void InitializeClass(const ClassType* class_type, VisitResult allocate_result,
483 const InitializerResults& initializer_results,
484 const LayoutForInitialization& layout);
485
486 VisitResult Visit(StructExpression* decl);
487
488 LocationReference GetLocationReference(Expression* location);
489 LocationReference LookupLocalValue(const std::string& name);
490 LocationReference GetLocationReference(IdentifierExpression* expr);
491 LocationReference GetLocationReference(DereferenceExpression* expr);
492 LocationReference GetLocationReference(FieldAccessExpression* expr);
493 LocationReference GenerateFieldAccess(
494 LocationReference reference, const std::string& fieldname,
495 bool ignore_stuct_field_constness = false,
496 base::Optional<SourcePosition> pos = {});
497 LocationReference GetLocationReference(ElementAccessExpression* expr);
498
499 VisitResult GenerateFetchFromLocation(const LocationReference& reference);
500
501 VisitResult GetBuiltinCode(Builtin* builtin);
502
503 VisitResult Visit(LocationExpression* expr);
504 VisitResult Visit(FieldAccessExpression* expr);
505
506 void VisitAllDeclarables();
507 void Visit(Declarable* delarable);
508 void Visit(TypeAlias* decl);
509 VisitResult InlineMacro(Macro* macro,
510 base::Optional<LocationReference> this_reference,
511 const std::vector<VisitResult>& arguments,
512 const std::vector<Block*> label_blocks);
513 void VisitMacroCommon(Macro* macro);
Visit(ExternMacro * macro)514 void Visit(ExternMacro* macro) {}
515 void Visit(TorqueMacro* macro);
516 void Visit(Method* macro);
517 void Visit(Builtin* builtin);
518 void Visit(NamespaceConstant* decl);
519
520 VisitResult Visit(CallExpression* expr, bool is_tail = false);
521 VisitResult Visit(CallMethodExpression* expr);
522 VisitResult Visit(IntrinsicCallExpression* intrinsic);
523 const Type* Visit(TailCallStatement* stmt);
524
525 VisitResult Visit(ConditionalExpression* expr);
526
527 VisitResult Visit(LogicalOrExpression* expr);
528 VisitResult Visit(LogicalAndExpression* expr);
529
530 VisitResult Visit(IncrementDecrementExpression* expr);
531 VisitResult Visit(AssignmentExpression* expr);
532 VisitResult Visit(StringLiteralExpression* expr);
533 VisitResult Visit(NumberLiteralExpression* expr);
534 VisitResult Visit(AssumeTypeImpossibleExpression* expr);
535 VisitResult Visit(TryLabelExpression* expr);
536 VisitResult Visit(StatementExpression* expr);
537 VisitResult Visit(NewExpression* expr);
538 VisitResult Visit(SpreadExpression* expr);
539
540 const Type* Visit(ReturnStatement* stmt);
541 const Type* Visit(GotoStatement* stmt);
542 const Type* Visit(IfStatement* stmt);
543 const Type* Visit(WhileStatement* stmt);
544 const Type* Visit(BreakStatement* stmt);
545 const Type* Visit(ContinueStatement* stmt);
546 const Type* Visit(ForLoopStatement* stmt);
547 const Type* Visit(VarDeclarationStatement* stmt);
548 const Type* Visit(VarDeclarationStatement* stmt,
549 BlockBindings<LocalValue>* block_bindings);
550 const Type* Visit(BlockStatement* block);
551 const Type* Visit(ExpressionStatement* stmt);
552 const Type* Visit(DebugStatement* stmt);
553 const Type* Visit(AssertStatement* stmt);
554
555 void BeginGeneratedFiles();
556 void EndGeneratedFiles();
557 // TODO(tebbi): Switch to per-file generation for runtime macros and merge
558 // these functions into {Begin,End}GeneratedFiles().
559 void BeginRuntimeMacrosFile();
560 void EndRuntimeMacrosFile();
561
562 void GenerateImplementation(const std::string& dir);
563
564 DECLARE_CONTEXTUAL_VARIABLE(ValueBindingsManager,
565 BindingsManager<LocalValue>);
566 DECLARE_CONTEXTUAL_VARIABLE(LabelBindingsManager,
567 BindingsManager<LocalLabel>);
568 DECLARE_CONTEXTUAL_VARIABLE(CurrentCallable, Callable*);
569 DECLARE_CONTEXTUAL_VARIABLE(CurrentFileStreams,
570 GlobalContext::PerFileStreams*);
571 DECLARE_CONTEXTUAL_VARIABLE(CurrentReturnValue, base::Optional<VisitResult>);
572
573 // A BindingsManagersScope has to be active for local bindings to be created.
574 // Shadowing an existing BindingsManagersScope by creating a new one hides all
575 // existing bindings while the additional BindingsManagersScope is active.
576 struct BindingsManagersScope {
577 ValueBindingsManager::Scope value_bindings_manager;
578 LabelBindingsManager::Scope label_bindings_manager;
579 };
580
SetDryRun(bool is_dry_run)581 void SetDryRun(bool is_dry_run) { is_dry_run_ = is_dry_run; }
582
583 private:
584 base::Optional<Block*> GetCatchBlock();
585 void GenerateCatchBlock(base::Optional<Block*> catch_block);
586
587 // {StackScope} records the stack height at creation time and reconstructs it
588 // when being destructed by emitting a {DeleteRangeInstruction}, except for
589 // the slots protected by {StackScope::Yield}. Calling {Yield(v)} deletes all
590 // slots above the initial stack height except for the slots of {v}, which are
591 // moved to form the only slots above the initial height and marks them to
592 // survive destruction of the {StackScope}. A typical pattern is the
593 // following:
594 //
595 // VisitResult result;
596 // {
597 // StackScope stack_scope(this);
598 // // ... create temporary slots ...
599 // result = stack_scope.Yield(surviving_slots);
600 // }
601 class StackScope {
602 public:
StackScope(ImplementationVisitor * visitor)603 explicit StackScope(ImplementationVisitor* visitor) : visitor_(visitor) {
604 base_ = visitor_->assembler().CurrentStack().AboveTop();
605 }
Yield(VisitResult result)606 VisitResult Yield(VisitResult result) {
607 DCHECK(!closed_);
608 closed_ = true;
609 if (!result.IsOnStack()) {
610 if (!visitor_->assembler().CurrentBlockIsComplete()) {
611 visitor_->assembler().DropTo(base_);
612 }
613 return result;
614 }
615 DCHECK_LE(base_, result.stack_range().begin());
616 DCHECK_LE(result.stack_range().end(),
617 visitor_->assembler().CurrentStack().AboveTop());
618 visitor_->assembler().DropTo(result.stack_range().end());
619 visitor_->assembler().DeleteRange(
620 StackRange{base_, result.stack_range().begin()});
621 base_ = visitor_->assembler().CurrentStack().AboveTop();
622 return VisitResult(result.type(), visitor_->assembler().TopRange(
623 result.stack_range().Size()));
624 }
625
Close()626 void Close() {
627 DCHECK(!closed_);
628 closed_ = true;
629 if (!visitor_->assembler().CurrentBlockIsComplete()) {
630 visitor_->assembler().DropTo(base_);
631 }
632 }
633
~StackScope()634 ~StackScope() {
635 if (closed_) {
636 DCHECK_IMPLIES(
637 !visitor_->assembler().CurrentBlockIsComplete(),
638 base_ == visitor_->assembler().CurrentStack().AboveTop());
639 } else {
640 Close();
641 }
642 }
643
644 private:
645 ImplementationVisitor* visitor_;
646 BottomOffset base_;
647 bool closed_ = false;
648 };
649
650 class BreakContinueActivator {
651 public:
BreakContinueActivator(Block * break_block,Block * continue_block)652 BreakContinueActivator(Block* break_block, Block* continue_block)
653 : break_binding_{&LabelBindingsManager::Get(), kBreakLabelName,
654 LocalLabel{break_block}},
Get()655 continue_binding_{&LabelBindingsManager::Get(), kContinueLabelName,
656 LocalLabel{continue_block}} {}
657
658 private:
659 Binding<LocalLabel> break_binding_;
660 Binding<LocalLabel> continue_binding_;
661 };
662
663 base::Optional<Binding<LocalValue>*> TryLookupLocalValue(
664 const std::string& name);
665 base::Optional<Binding<LocalLabel>*> TryLookupLabel(const std::string& name);
666 Binding<LocalLabel>* LookupLabel(const std::string& name);
667 Block* LookupSimpleLabel(const std::string& name);
668 template <class Container>
669 Callable* LookupCallable(const QualifiedName& name,
670 const Container& declaration_container,
671 const TypeVector& types,
672 const std::vector<Binding<LocalLabel>*>& labels,
673 const TypeVector& specialization_types,
674 bool silence_errors = false);
675 bool TestLookupCallable(const QualifiedName& name,
676 const TypeVector& parameter_types);
677
678 template <class Container>
679 Callable* LookupCallable(const QualifiedName& name,
680 const Container& declaration_container,
681 const Arguments& arguments,
682 const TypeVector& specialization_types);
683
684 Method* LookupMethod(const std::string& name,
685 const AggregateType* receiver_type,
686 const Arguments& arguments,
687 const TypeVector& specialization_types);
688
689 TypeArgumentInference InferSpecializationTypes(
690 GenericCallable* generic, const TypeVector& explicit_specialization_types,
691 const TypeVector& explicit_arguments);
692
693 const Type* GetCommonType(const Type* left, const Type* right);
694
695 VisitResult GenerateCopy(const VisitResult& to_copy);
696
697 void GenerateAssignToLocation(const LocationReference& reference,
698 const VisitResult& assignment_value);
699
700 void AddCallParameter(Callable* callable, VisitResult parameter,
701 const Type* parameter_type,
702 std::vector<VisitResult>* converted_arguments,
703 StackRange* argument_range,
704 std::vector<std::string>* constexpr_arguments,
705 bool inline_macro);
706
707 VisitResult GenerateCall(Callable* callable,
708 base::Optional<LocationReference> this_parameter,
709 Arguments parameters,
710 const TypeVector& specialization_types = {},
711 bool tail_call = false);
712 VisitResult GenerateCall(const QualifiedName& callable_name,
713 Arguments parameters,
714 const TypeVector& specialization_types = {},
715 bool tail_call = false);
716 VisitResult GenerateCall(std::string callable_name, Arguments parameters,
717 const TypeVector& specialization_types = {},
718 bool tail_call = false) {
719 return GenerateCall(QualifiedName(std::move(callable_name)),
720 std::move(parameters), specialization_types, tail_call);
721 }
722 VisitResult GeneratePointerCall(Expression* callee,
723 const Arguments& parameters, bool tail_call);
724
725 void GenerateBranch(const VisitResult& condition, Block* true_block,
726 Block* false_block);
727
728 VisitResult GenerateBoolConstant(bool constant);
729
730 void GenerateExpressionBranch(Expression* expression, Block* true_block,
731 Block* false_block);
732
733 void GenerateMacroFunctionDeclaration(std::ostream& o,
734 Macro* macro);
735 std::vector<std::string> GenerateFunctionDeclaration(
736 std::ostream& o, const std::string& macro_prefix, const std::string& name,
737 const Signature& signature, const NameVector& parameter_names,
738 bool pass_code_assembler_state = true);
739
740 VisitResult GenerateImplicitConvert(const Type* destination_type,
741 VisitResult source);
742
743 StackRange GenerateLabelGoto(LocalLabel* label,
744 base::Optional<StackRange> arguments = {});
745
746 VisitResult GenerateSetBitField(const Type* bitfield_struct_type,
747 const BitField& bitfield,
748 VisitResult bitfield_struct,
749 VisitResult value,
750 bool starts_as_zero = false);
751
752 std::vector<Binding<LocalLabel>*> LabelsFromIdentifiers(
753 const std::vector<Identifier*>& names);
754
755 StackRange LowerParameter(const Type* type, const std::string& parameter_name,
756 Stack<std::string>* lowered_parameters);
757
758 void LowerLabelParameter(const Type* type, const std::string& parameter_name,
759 std::vector<std::string>* lowered_parameters);
760
761 std::string ExternalLabelName(const std::string& label_name);
762 std::string ExternalLabelParameterName(const std::string& label_name,
763 size_t i);
764 std::string ExternalParameterName(const std::string& name);
765
csa_ccfile()766 std::ostream& csa_ccfile() {
767 if (auto* streams = CurrentFileStreams::Get()) {
768 return output_type_ == OutputType::kCSA ? streams->csa_ccfile
769 : runtime_macros_cc_;
770 }
771 return null_stream_;
772 }
csa_headerfile()773 std::ostream& csa_headerfile() {
774 if (auto* streams = CurrentFileStreams::Get()) {
775 return output_type_ == OutputType::kCSA ? streams->csa_headerfile
776 : runtime_macros_h_;
777 }
778 return null_stream_;
779 }
class_definition_headerfile()780 std::ostream& class_definition_headerfile() {
781 if (auto* streams = CurrentFileStreams::Get()) {
782 return streams->class_definition_headerfile;
783 }
784 return null_stream_;
785 }
class_definition_inline_headerfile()786 std::ostream& class_definition_inline_headerfile() {
787 if (auto* streams = CurrentFileStreams::Get()) {
788 return streams->class_definition_inline_headerfile;
789 }
790 return null_stream_;
791 }
class_definition_ccfile()792 std::ostream& class_definition_ccfile() {
793 if (auto* streams = CurrentFileStreams::Get()) {
794 return streams->class_definition_ccfile;
795 }
796 return null_stream_;
797 }
798
assembler()799 CfgAssembler& assembler() { return *assembler_; }
800
SetReturnValue(VisitResult return_value)801 void SetReturnValue(VisitResult return_value) {
802 base::Optional<VisitResult>& current_return_value =
803 CurrentReturnValue::Get();
804 DCHECK_IMPLIES(current_return_value, *current_return_value == return_value);
805 current_return_value = std::move(return_value);
806 }
807
GetAndClearReturnValue()808 VisitResult GetAndClearReturnValue() {
809 VisitResult return_value = *CurrentReturnValue::Get();
810 CurrentReturnValue::Get() = base::nullopt;
811 return return_value;
812 }
813
WriteFile(const std::string & file,const std::string & content)814 void WriteFile(const std::string& file, const std::string& content) {
815 if (is_dry_run_) return;
816 ReplaceFileContentsIfDifferent(file, content);
817 }
818
TryGetSourceForBitfieldExpression(const Expression * expr)819 const Identifier* TryGetSourceForBitfieldExpression(
820 const Expression* expr) const {
821 auto it = bitfield_expressions_.find(expr);
822 if (it == bitfield_expressions_.end()) return nullptr;
823 return it->second;
824 }
825
PropagateBitfieldMark(const Expression * original,const Expression * derived)826 void PropagateBitfieldMark(const Expression* original,
827 const Expression* derived) {
828 if (const Identifier* source =
829 TryGetSourceForBitfieldExpression(original)) {
830 bitfield_expressions_[derived] = source;
831 }
832 }
833
834 base::Optional<CfgAssembler> assembler_;
835 NullOStream null_stream_;
836 bool is_dry_run_;
837
838 // Just for allowing us to emit warnings. After visiting an Expression, if
839 // that Expression is a bitfield load, plus an optional inversion or an
840 // equality check with a constant, then that Expression will be present in
841 // this map. The Identifier associated is the bitfield struct that contains
842 // the value to load.
843 std::unordered_map<const Expression*, const Identifier*>
844 bitfield_expressions_;
845
846 // The contents of the runtime macros output files. These contain all Torque
847 // macros that have been generated using the C++ backend. They're not yet
848 // split per source file like CSA macros, but eventually we should change them
849 // to generate -inl.inc files so that callers can easily inline their
850 // contents.
851 std::stringstream runtime_macros_cc_;
852 std::stringstream runtime_macros_h_;
853
854 OutputType output_type_ = OutputType::kCSA;
855 };
856
857 void ReportAllUnusedMacros();
858
859 } // namespace torque
860 } // namespace internal
861 } // namespace v8
862
863 #endif // V8_TORQUE_IMPLEMENTATION_VISITOR_H_
864