Files
sdk/runtime/vm/compiler/backend/flow_graph_test.cc
T
Alexander Markov f38a2804a3 [vm/compiler] Fix computation of ParameterInstr type in a catch block
In order to compute type, ParameterInstr::ComputeType() uses
environment index to get a LocalVariable from LocalScope,
assuming that environment index matches a variable index in the scope.
This is only true for direct parameters (which are not copied in
prologue).

This change limits use of LocalVariable type for ParameterInstr
corresponding to direct parameters. Note that it only affects
Parameter instructions used in catch block entries, as
ParameterInstr in function entry always corresponds to a direct
parameter.

TEST=runtime/tests/vm/dart/regress_flutter110715_il_test.dart
Fixes https://github.com/flutter/flutter/issues/110715

Change-Id: I68d423860928d7e65143844522e3006d9ccfcf66
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/257441
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
2022-09-07 17:58:10 +00:00

413 lines
13 KiB
C++

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/compiler/backend/flow_graph.h"
#include <vector>
#include "platform/text_buffer.h"
#include "platform/utils.h"
#include "vm/compiler/backend/block_builder.h"
#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/backend/type_propagator.h"
#include "vm/unit_test.h"
namespace dart {
#if defined(TARGET_ARCH_IS_64_BIT)
ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxInt64Phi) {
using compiler::BlockBuilder;
CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
FlowGraphBuilderHelper H(/*num_parameters=*/1);
H.AddVariable("v0", AbstractType::ZoneHandle(Type::IntType()),
new CompileType(CompileType::Int()));
auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
auto loop_header = H.JoinEntry();
auto loop_body = H.TargetEntry();
auto loop_exit = H.TargetEntry();
Definition* v0;
PhiInstr* loop_var;
Definition* add1;
{
BlockBuilder builder(H.flow_graph(), normal_entry);
v0 = builder.AddParameter(0, 0, /*with_frame=*/true, kTagged);
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
}
{
BlockBuilder builder(H.flow_graph(), loop_header);
loop_var = H.Phi(loop_header, {{normal_entry, v0}, {loop_body, &add1}});
builder.AddPhi(loop_var);
builder.AddBranch(new RelationalOpInstr(
InstructionSource(), Token::kLT, new Value(loop_var),
new Value(H.IntConstant(50)), kMintCid,
S.GetNextDeoptId(), Instruction::kNotSpeculative),
loop_body, loop_exit);
}
{
BlockBuilder builder(H.flow_graph(), loop_body);
add1 = builder.AddDefinition(new BinaryInt64OpInstr(
Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
S.GetNextDeoptId(), Instruction::kNotSpeculative));
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
}
{
BlockBuilder builder(H.flow_graph(), loop_exit);
builder.AddReturn(new Value(loop_var));
}
H.FinishGraph();
FlowGraphTypePropagator::Propagate(H.flow_graph());
H.flow_graph()->SelectRepresentations();
EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
}
#endif // defined(TARGET_ARCH_IS_64_BIT)
ISOLATE_UNIT_TEST_CASE(FlowGraph_LateVariablePhiUnboxing) {
using compiler::BlockBuilder;
CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
FlowGraphBuilderHelper H;
auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
auto loop_header = H.JoinEntry();
auto loop_body = H.TargetEntry();
auto loop_exit = H.TargetEntry();
ConstantInstr* sentinel = H.flow_graph()->GetConstant(Object::sentinel());
PhiInstr* loop_var;
PhiInstr* late_var;
Definition* add1;
{
BlockBuilder builder(H.flow_graph(), normal_entry);
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
}
{
BlockBuilder builder(H.flow_graph(), loop_header);
loop_var = H.Phi(loop_header,
{{normal_entry, H.IntConstant(0)}, {loop_body, &add1}});
builder.AddPhi(loop_var);
loop_var->UpdateType(CompileType::Int());
loop_var->UpdateType(CompileType::FromAbstractType(
Type::ZoneHandle(Type::IntType()), CompileType::kCannotBeNull,
CompileType::kCanBeSentinel));
late_var =
H.Phi(loop_header, {{normal_entry, sentinel}, {loop_body, &add1}});
builder.AddPhi(late_var);
builder.AddBranch(new RelationalOpInstr(
InstructionSource(), Token::kLT, new Value(loop_var),
new Value(H.IntConstant(10)), kMintCid,
S.GetNextDeoptId(), Instruction::kNotSpeculative),
loop_body, loop_exit);
}
{
BlockBuilder builder(H.flow_graph(), loop_body);
add1 = builder.AddDefinition(new BinaryInt64OpInstr(
Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
S.GetNextDeoptId(), Instruction::kNotSpeculative));
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
}
{
BlockBuilder builder(H.flow_graph(), loop_exit);
builder.AddReturn(new Value(late_var));
}
H.FinishGraph();
FlowGraphTypePropagator::Propagate(H.flow_graph());
H.flow_graph()->SelectRepresentations();
#if defined(TARGET_ARCH_IS_64_BIT)
EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
#endif
EXPECT_PROPERTY(late_var, it.representation() == kTagged);
}
void TestLargeFrame(const char* type,
const char* zero,
const char* one,
const char* main) {
SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 1000);
TextBuffer printer(256 * KB);
intptr_t num_locals = 2000;
printer.Printf("import 'dart:typed_data';\n");
printer.Printf("@pragma('vm:never-inline')\n");
printer.Printf("%s one() { return %s; }\n", type, one);
printer.Printf("@pragma('vm:never-inline')\n");
printer.Printf("%s largeFrame(int n) {\n", type);
for (intptr_t i = 0; i < num_locals; i++) {
printer.Printf(" %s local%" Pd " = %s;\n", type, i, zero);
}
printer.Printf(" for (int i = 0; i < n; i++) {\n");
for (intptr_t i = 0; i < num_locals; i++) {
printer.Printf(" local%" Pd " += one();\n", i);
}
printer.Printf(" }\n");
printer.Printf(" %s sum = %s;\n", type, zero);
for (intptr_t i = 0; i < num_locals; i++) {
printer.Printf(" sum += local%" Pd ";\n", i);
}
printer.Printf(" return sum;\n");
printer.Printf("}\n");
printer.AddString(main);
const auto& root_library = Library::Handle(LoadTestScript(printer.buffer()));
Invoke(root_library, "main");
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Int) {
TestLargeFrame("int", "0", "1",
"main() {\n"
" for (var i = 0; i < 100; i++) {\n"
" var r = largeFrame(1);\n"
" if (r != 2000) throw r;\n"
" }\n"
" return 'Okay';\n"
"}\n");
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Double) {
TestLargeFrame("double", "0.0", "1.0",
"main() {\n"
" for (var i = 0; i < 100; i++) {\n"
" var r = largeFrame(1);\n"
" if (r != 2000.0) throw r;\n"
" }\n"
" return 'Okay';\n"
"}\n");
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Int32x4) {
TestLargeFrame("Int32x4", "Int32x4(0, 0, 0, 0)", "Int32x4(1, 2, 3, 4)",
"main() {\n"
" for (var i = 0; i < 100; i++) {\n"
" var r = largeFrame(1);\n"
" if (r.x != 2000) throw r;\n"
" if (r.y != 4000) throw r;\n"
" if (r.z != 6000) throw r;\n"
" if (r.w != 8000) throw r;\n"
" }\n"
" return 'Okay';\n"
"}\n");
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Float32x4) {
TestLargeFrame("Float32x4", "Float32x4(0.0, 0.0, 0.0, 0.0)",
"Float32x4(1.0, 2.0, 3.0, 4.0)",
"main() {\n"
" for (var i = 0; i < 100; i++) {\n"
" var r = largeFrame(1);\n"
" if (r.x != 2000.0) throw r;\n"
" if (r.y != 4000.0) throw r;\n"
" if (r.z != 6000.0) throw r;\n"
" if (r.w != 8000.0) throw r;\n"
" }\n"
" return 'Okay';\n"
"}\n");
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Float64x2) {
TestLargeFrame("Float64x2", "Float64x2(0.0, 0.0)", "Float64x2(1.0, 2.0)",
"main() {\n"
" for (var i = 0; i < 100; i++) {\n"
" var r = largeFrame(1);\n"
" if (r.x != 2000.0) throw r;\n"
" if (r.y != 4000.0) throw r;\n"
" }\n"
" return 'Okay';\n"
"}\n");
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Double) {
if (!FlowGraphCompiler::SupportsUnboxedDoubles()) {
return;
}
const char* kScript = R"(
double foo(double sum, int n) {
if (sum == null) return 0.0;
for (int i = 0; i < n; i++) {
sum += 1.0;
}
return sum;
}
main() {
foo(0.0, 10);
}
)";
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
Invoke(root_library, "main");
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({});
auto entry = flow_graph->graph_entry()->normal_entry();
ILMatcher cursor(flow_graph, entry, /*trace=*/true,
ParallelMovesHandling::kSkip);
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveFunctionEntry,
}));
if (FLAG_sound_null_safety != kNullSafetyOptionStrong) {
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveBranchFalse,
kMatchAndMoveTargetEntry,
}));
}
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveUnbox, // outside of loop
kMatchAndMoveCheckSmi,
kMoveGlob,
// Loop header
kMatchAndMoveJoinEntry,
kMatchAndMoveCheckStackOverflow,
kMatchAndMoveBranchTrue,
// Loop body
kMatchAndMoveTargetEntry,
kMatchAndMoveBinaryDoubleOp,
kMatchAndMoveBinarySmiOp,
kMatchAndMoveGoto,
// Loop header, again
kMatchAndMoveJoinEntry,
kMatchAndMoveCheckStackOverflow,
kMatchAndMoveBranchFalse,
// After loop
kMatchAndMoveTargetEntry,
kMatchAndMoveBox,
kMatchReturn,
}));
}
static void TestPhiUnboxingHeuristicSimd(const char* script) {
if (!FlowGraphCompiler::SupportsUnboxedSimd128()) {
return;
}
const auto& root_library = Library::Handle(LoadTestScript(script));
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
Invoke(root_library, "main");
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({});
auto entry = flow_graph->graph_entry()->normal_entry();
ILMatcher cursor(flow_graph, entry, /*trace=*/true,
ParallelMovesHandling::kSkip);
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveFunctionEntry,
}));
if (FLAG_sound_null_safety != kNullSafetyOptionStrong) {
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveBranchFalse,
kMatchAndMoveTargetEntry,
}));
}
RELEASE_ASSERT(cursor.TryMatch({
kMatchAndMoveUnbox, // outside of loop
kMatchAndMoveCheckSmi,
kMoveGlob,
// Loop header
kMatchAndMoveJoinEntry,
kMatchAndMoveCheckStackOverflow,
kMatchAndMoveBranchTrue,
// Loop body
kMatchAndMoveTargetEntry,
kMatchAndMoveSimdOp,
kMatchAndMoveBinarySmiOp,
kMatchAndMoveGoto,
// Loop header, again
kMatchAndMoveJoinEntry,
kMatchAndMoveCheckStackOverflow,
kMatchAndMoveBranchFalse,
// After loop
kMatchAndMoveTargetEntry,
kMatchAndMoveBox,
kMatchReturn,
}));
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Float32x4) {
const char* kScript = R"(
import 'dart:typed_data';
Float32x4 foo(Float32x4 sum, int n) {
if (sum == null) return Float32x4(0.0, 0.0, 0.0, 0.0);
for (int i = 0; i < n; i++) {
sum += Float32x4(1.0, 2.0, 3.0, 4.0);
}
return sum;
}
main() {
foo(Float32x4(0.0, 0.0, 0.0, 0.0), 10);
}
)";
TestPhiUnboxingHeuristicSimd(kScript);
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Float64x2) {
const char* kScript = R"(
import 'dart:typed_data';
Float64x2 foo(Float64x2 sum, int n) {
if (sum == null) return Float64x2(0.0, 0.0);
for (int i = 0; i < n; i++) {
sum += Float64x2(1.0, 2.0);
}
return sum;
}
main() {
foo(Float64x2(0.0, 0.0), 10);
}
)";
TestPhiUnboxingHeuristicSimd(kScript);
}
ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Int32x4) {
const char* kScript = R"(
import 'dart:typed_data';
Int32x4 foo(Int32x4 sum, int n) {
if (sum == null) return Int32x4(0, 0, 0, 0);
for (int i = 0; i < n; i++) {
sum += Int32x4(1, 2, 3, 4);
}
return sum;
}
main() {
foo(Int32x4(0, 0, 0, 0), 10);
}
)";
TestPhiUnboxingHeuristicSimd(kScript);
}
} // namespace dart