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