[vm] Decouple flow graph building of implicit getters and setters from kernel reading
Change-Id: I514fa59e88afaa63f989b9bd373a20ea42d1d0e3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96204
Reviewed-by: Aart Bik <ajcbik@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Auto-Submit: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index ac67edd..13de1df 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -67,79 +67,18 @@
B->last_used_block_id_, prologue_info);
}
-FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldAccessor(
- LocalVariable* setter_value) {
+void StreamingFlowGraphBuilder::EvaluateConstFieldValue(const Field& field) {
+ ASSERT(field.is_const() && field.IsUninitialized());
+
FieldHelper field_helper(this);
- field_helper.ReadUntilIncluding(FieldHelper::kCanonicalName);
+ field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
+ Tag initializer_tag = ReadTag(); // read first part of initializer.
- const Function& function = parsed_function()->function();
+ ASSERT(initializer_tag == kSomething);
- // Instead of building a dynamic invocation forwarder that checks argument
- // type and then invokes original setter we simply generate the type check
- // and inlined field store. Scope builder takes care of setting correct
- // type check mode in this case.
- const bool is_setter = function.IsDynamicInvocationForwader() ||
- function.IsImplicitSetterFunction();
- const bool is_method = !function.IsStaticFunction();
- Field& field = Field::ZoneHandle(
- Z, H.LookupFieldByKernelField(field_helper.canonical_name_));
-
- B->graph_entry_ =
- new (Z) GraphEntryInstr(*parsed_function(), Compiler::kNoOSRDeoptId);
-
- auto normal_entry = B->BuildFunctionEntry(B->graph_entry_);
- B->graph_entry_->set_normal_entry(normal_entry);
-
- Fragment body(normal_entry);
- if (is_setter) {
- // We only expect to generate a dynamic invocation forwarder if
- // the value needs type check.
- ASSERT(!function.IsDynamicInvocationForwader() ||
- setter_value->needs_type_check());
- if (is_method) {
- body += LoadLocal(scopes()->this_variable);
- }
- body += LoadLocal(setter_value);
- if (I->argument_type_checks() && setter_value->needs_type_check()) {
- body += CheckArgumentType(setter_value, setter_value->type());
- }
- if (is_method) {
- body += flow_graph_builder_->StoreInstanceFieldGuarded(field, false);
- } else {
- body += StoreStaticField(TokenPosition::kNoSource, field);
- }
- body += NullConstant();
- } else if (is_method) {
- body += LoadLocal(scopes()->this_variable);
- body += flow_graph_builder_->LoadField(field);
- } else if (field.is_const()) {
- field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
- Tag initializer_tag = ReadTag(); // read first part of initializer.
-
- // If the parser needs to know the value of an uninitialized constant field
- // it will set the value to the transition sentinel (used to detect circular
- // initialization) and then call the implicit getter. Thus, the getter
- // cannot contain the InitStaticField instruction that normal static getters
- // contain because it would detect spurious circular initialization when it
- // checks for the transition sentinel.
- ASSERT(initializer_tag == kSomething);
- body += Constant(Instance::ZoneHandle(
- Z, constant_evaluator_.EvaluateExpression(ReaderOffset())));
- } else {
- // The field always has an initializer because static fields without
- // initializers are initialized eagerly and do not have implicit getters.
- ASSERT(field.has_initializer());
- body += Constant(field);
- body += flow_graph_builder_->InitStaticField(field);
- body += Constant(field);
- body += LoadStaticField();
- }
- body += Return(TokenPosition::kNoSource);
-
- PrologueInfo prologue_info(-1, -1);
- return new (Z)
- FlowGraph(*parsed_function(), flow_graph_builder_->graph_entry_,
- flow_graph_builder_->last_used_block_id_, prologue_info);
+ Instance& value = Instance::Handle(
+ Z, constant_evaluator_.EvaluateExpression(ReaderOffset()));
+ field.SetStaticValue(value);
}
void StreamingFlowGraphBuilder::SetupDefaultParameterValues() {
@@ -1211,13 +1150,18 @@
case RawFunction::kImplicitGetter:
case RawFunction::kImplicitStaticFinalGetter:
case RawFunction::kImplicitSetter: {
- return IsFieldInitializer(function, Z)
- ? BuildGraphOfFieldInitializer()
- : BuildGraphOfFieldAccessor(scopes()->setter_value);
+ if (IsFieldInitializer(function, Z)) {
+ return BuildGraphOfFieldInitializer();
+ }
+ const Field& field = Field::Handle(Z, function.accessor_field());
+ if (field.is_const() && field.IsUninitialized()) {
+ EvaluateConstFieldValue(field);
+ }
+ return B->BuildGraphOfFieldAccessor(function);
}
case RawFunction::kDynamicInvocationForwarder:
if (PeekTag() == kField) {
- return BuildGraphOfFieldAccessor(scopes()->setter_value);
+ return B->BuildGraphOfFieldAccessor(function);
} else {
ReadUntilFunctionNode();
return BuildGraphOfDynamicInvocationForwarder();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index b554501..c8192c4 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -57,7 +57,7 @@
Thread* thread() const { return flow_graph_builder_->thread_; }
FlowGraph* BuildGraphOfFieldInitializer();
- FlowGraph* BuildGraphOfFieldAccessor(LocalVariable* setter_value);
+ void EvaluateConstFieldValue(const Field& field);
void SetupDefaultParameterValues();
void ReadDefaultFunctionTypeArguments(const Function& function);
Fragment BuildFieldInitializer(NameIndex canonical_name);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 7fa7051..01bd29a 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -2092,6 +2092,79 @@
prologue_info);
}
+FlowGraph* FlowGraphBuilder::BuildGraphOfFieldAccessor(
+ const Function& function) {
+ ASSERT(function.IsImplicitGetterOrSetter() ||
+ function.IsDynamicInvocationForwader());
+
+ // Instead of building a dynamic invocation forwarder that checks argument
+ // type and then invokes original setter we simply generate the type check
+ // and inlined field store. Scope builder takes care of setting correct
+ // type check mode in this case.
+ const bool is_setter = function.IsDynamicInvocationForwader() ||
+ function.IsImplicitSetterFunction();
+ const bool is_method = !function.IsStaticFunction();
+
+ Field& field = Field::ZoneHandle(Z, function.accessor_field());
+
+ graph_entry_ =
+ new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
+
+ auto normal_entry = BuildFunctionEntry(graph_entry_);
+ graph_entry_->set_normal_entry(normal_entry);
+
+ auto scope = parsed_function_->node_sequence()->scope();
+
+ Fragment body(normal_entry);
+ if (is_setter) {
+ LocalVariable* setter_value = scope->VariableAt(is_method ? 1 : 0);
+
+ // We only expect to generate a dynamic invocation forwarder if
+ // the value needs type check.
+ ASSERT(!function.IsDynamicInvocationForwader() ||
+ setter_value->needs_type_check());
+ if (is_method) {
+ body += LoadLocal(scope->VariableAt(0));
+ }
+ body += LoadLocal(setter_value);
+ if (I->argument_type_checks() && setter_value->needs_type_check()) {
+ body += CheckAssignable(setter_value->type(), setter_value->name(),
+ AssertAssignableInstr::kParameterCheck);
+ }
+ if (is_method) {
+ body += StoreInstanceFieldGuarded(field, false);
+ } else {
+ body += StoreStaticField(TokenPosition::kNoSource, field);
+ }
+ body += NullConstant();
+ } else if (is_method) {
+ body += LoadLocal(scope->VariableAt(0));
+ body += LoadField(field);
+ } else if (field.is_const()) {
+ // If the parser needs to know the value of an uninitialized constant field
+ // it will set the value to the transition sentinel (used to detect circular
+ // initialization) and then call the implicit getter. Thus, the getter
+ // cannot contain the InitStaticField instruction that normal static getters
+ // contain because it would detect spurious circular initialization when it
+ // checks for the transition sentinel.
+ ASSERT(!field.IsUninitialized());
+ body += Constant(Instance::ZoneHandle(Z, field.StaticValue()));
+ } else {
+ // The field always has an initializer because static fields without
+ // initializers are initialized eagerly and do not have implicit getters.
+ ASSERT(field.has_initializer());
+ body += Constant(field);
+ body += InitStaticField(field);
+ body += Constant(field);
+ body += LoadStaticField();
+ }
+ body += Return(TokenPosition::kNoSource);
+
+ PrologueInfo prologue_info(-1, -1);
+ return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
+ prologue_info);
+}
+
void FlowGraphBuilder::SetCurrentTryCatchBlock(TryCatchBlock* try_catch_block) {
try_catch_block_ = try_catch_block;
SetCurrentTryIndex(try_catch_block == nullptr ? kInvalidTryIndex
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index f1aeb23..fdbae6a 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -268,6 +268,17 @@
//
FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
+ // Builds flow graph of implicit field getter, setter, or a
+ // dynamic invocation forwarder to a field setter.
+ //
+ // If field is const, its value should be evaluated and stored in
+ // - StaticValue()
+ //
+ // Scope should be populated with parameter variables including
+ // - needs_type_check()
+ //
+ FlowGraph* BuildGraphOfFieldAccessor(const Function& function);
+
TranslationHelper translation_helper_;
Thread* thread_;
Zone* zone_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 864555d..314c18f 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5857,7 +5857,8 @@
RawField* Function::accessor_field() const {
ASSERT(kind() == RawFunction::kImplicitGetter ||
kind() == RawFunction::kImplicitSetter ||
- kind() == RawFunction::kImplicitStaticFinalGetter);
+ kind() == RawFunction::kImplicitStaticFinalGetter ||
+ kind() == RawFunction::kDynamicInvocationForwarder);
return Field::RawCast(raw_ptr()->data_);
}