f38a2804a3
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>
413 lines
13 KiB
C++
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
|