[vm] Treat static final fields with trivial initializers as const

When loading a value of a static final field with trivial initializer
generate a Constant instruction instead of LoadStaticField.

Initializer of a static field is considered trivial if it is null, int,
double, String or bool literal.

TEST=vm/cc/StreamingFlowGraphBuilder_StaticGetFinalFieldWithTrivialInitializer
Fixes https://github.com/dart-lang/sdk/issues/47120

Change-Id: Id2bfc3da8c14376f7f1ef829265cca29a018bad1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212873
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2021-09-13 16:51:59 +00:00 committed by commit-bot@chromium.org
parent 1d6f4c742f
commit 30abce99f3
5 changed files with 53 additions and 3 deletions

View file

@ -552,7 +552,7 @@ ISOLATE_UNIT_TEST_CASE(TypePropagator_NonNullableLoadStaticField) {
const char* kScript = R"(
const y = 0xDEADBEEF;
final int x = 0xFEEDFEED;
final int x = int.parse('0xFEEDFEED');
void main(List<String> args) {
print(x);

View file

@ -2776,6 +2776,10 @@ Fragment StreamingFlowGraphBuilder::BuildStaticGet(TokenPosition* p) {
Class::Handle(field.Owner()).Name() == Symbols::ClassID().ptr());
return Constant(Instance::ZoneHandle(
Z, Instance::RawCast(field.StaticConstFieldValue())));
} else if (field.is_final() && field.has_trivial_initializer()) {
// Final fields with trivial initializers are effectively constant.
return Constant(Instance::ZoneHandle(
Z, Instance::RawCast(field.StaticConstFieldValue())));
} else {
const Class& owner = Class::Handle(Z, field.Owner());
const String& getter_name = H.DartGetterName(target);

View file

@ -340,4 +340,44 @@ ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_TypedClosureCall) {
// clang-format on
}
ISOLATE_UNIT_TEST_CASE(
StreamingFlowGraphBuilder_StaticGetFinalFieldWithTrivialInitializer) {
const char* kScript = R"(
final int x = 0xFEEDFEED;
test() {
return x;
}
)";
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& function = Function::Handle(GetFunction(root_library, "test"));
Invoke(root_library, "test");
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({
CompilerPass::kComputeSSA,
});
auto entry = flow_graph->graph_entry()->normal_entry();
EXPECT(entry != nullptr);
ReturnInstr* return_instr = nullptr;
ILMatcher cursor(flow_graph, entry);
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveFunctionEntry,
kMatchAndMoveCheckStackOverflow,
kMoveDebugStepChecks,
{kMatchReturn, &return_instr},
}));
EXPECT(return_instr != nullptr);
ConstantInstr* const_value =
return_instr->value()->definition()->AsConstant();
EXPECT(const_value != nullptr);
EXPECT(const_value->value().IsInteger());
EXPECT_EQ(0xFEEDFEED, Integer::Cast(const_value->value()).AsInt64Value());
}
} // namespace dart

View file

@ -881,7 +881,8 @@ void KernelLoader::CheckForInitializer(const Field& field) {
SimpleExpressionConverter converter(&H, &helper_);
const bool has_simple_initializer =
converter.IsSimple(helper_.ReaderOffset() + 1);
if (!has_simple_initializer || !converter.SimpleValue().IsNull()) {
if (!has_simple_initializer ||
(!field.is_static() && !converter.SimpleValue().IsNull())) {
field.set_has_nontrivial_initializer(true);
}
return;

View file

@ -11172,7 +11172,8 @@ ErrorPtr Field::InitializeStatic() const {
}
ObjectPtr Field::StaticConstFieldValue() const {
ASSERT(is_static() && is_const());
ASSERT(is_static() &&
(is_const() || (is_final() && has_trivial_initializer())));
auto thread = Thread::Current();
auto zone = thread->zone();
@ -11183,7 +11184,11 @@ ObjectPtr Field::StaticConstFieldValue() const {
auto& value = Object::Handle(
zone, initial_field_table->At(field_id(), /*concurrent_use=*/true));
if (value.ptr() == Object::sentinel().ptr()) {
// Fields with trivial initializers get their initial value
// eagerly when they are registered.
ASSERT(is_const());
ASSERT(has_initializer());
ASSERT(has_nontrivial_initializer());
value = EvaluateInitializer();
if (!value.IsError()) {
ASSERT(value.IsNull() || value.IsInstance());