1 | // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include <utility> |
6 | |
7 | #include "vm/compiler/backend/il_test_helper.h" |
8 | #include "vm/compiler/compiler_pass.h" |
9 | #include "vm/object.h" |
10 | #include "vm/unit_test.h" |
11 | |
12 | namespace dart { |
13 | |
14 | using Pair = std::pair<intptr_t, TokenPosition>; |
15 | using YieldPoints = ZoneGrowableArray<Pair>; |
16 | |
17 | int LowestFirst(const Pair* a, const Pair* b) { |
18 | return a->first - b->first; |
19 | } |
20 | |
21 | static YieldPoints* GetYieldPointsFromGraph(FlowGraph* flow_graph) { |
22 | auto array = new (flow_graph->zone()) YieldPoints(); |
23 | const auto& blocks = flow_graph->reverse_postorder(); |
24 | for (auto block : blocks) { |
25 | ForwardInstructionIterator it(block); |
26 | while (!it.Done()) { |
27 | if (auto return_instr = it.Current()->AsReturn()) { |
28 | if (return_instr->yield_index() != |
29 | PcDescriptorsLayout::kInvalidYieldIndex) { |
30 | ASSERT(return_instr->yield_index() > 0); |
31 | array->Add( |
32 | Pair(return_instr->yield_index(), return_instr->token_pos())); |
33 | } |
34 | } |
35 | it.Advance(); |
36 | } |
37 | } |
38 | array->Sort(LowestFirst); |
39 | return array; |
40 | } |
41 | |
42 | static YieldPoints* GetYieldPointsFromCode(const Code& code) { |
43 | auto array = new YieldPoints(); |
44 | const auto& pc_descriptor = PcDescriptors::Handle(code.pc_descriptors()); |
45 | PcDescriptors::Iterator it(pc_descriptor, PcDescriptorsLayout::kOther); |
46 | while (it.MoveNext()) { |
47 | if (it.YieldIndex() != PcDescriptorsLayout::kInvalidYieldIndex) { |
48 | array->Add(Pair(it.YieldIndex(), it.TokenPos())); |
49 | } |
50 | } |
51 | array->Sort(LowestFirst); |
52 | return array; |
53 | } |
54 | |
55 | void RunTestInMode(CompilerPass::PipelineMode mode) { |
56 | const char* kScript = |
57 | R"( |
58 | import 'dart:async'; |
59 | |
60 | Future foo() async { |
61 | print('pos-0'); |
62 | await 0; |
63 | print('pos-1'); |
64 | await 1; |
65 | print('pos-2'); |
66 | await 2; |
67 | } |
68 | )" ; |
69 | |
70 | SetupCoreLibrariesForUnitTest(); |
71 | |
72 | const auto& root_library = Library::Handle(LoadTestScript(kScript)); |
73 | // Ensure the outer function was compiled once, ensuring we have a closure |
74 | // function for the inner closure. |
75 | Invoke(root_library, "foo" ); |
76 | |
77 | const auto& outer_function = |
78 | Function::Handle(GetFunction(root_library, "foo" )); |
79 | |
80 | // Grab the inner, lazily created, closure from the object store. |
81 | const auto& closures = GrowableObjectArray::Handle( |
82 | Isolate::Current()->object_store()->closure_functions()); |
83 | ASSERT(!closures.IsNull()); |
84 | auto& closure = Object::Handle(); |
85 | for (intptr_t i = 0; i < closures.Length(); ++i) { |
86 | closure = closures.At(i); |
87 | if (Function::Cast(closure).parent_function() == outer_function.raw()) { |
88 | break; |
89 | } |
90 | closure = Object::null(); |
91 | } |
92 | RELEASE_ASSERT(closure.IsFunction()); |
93 | const auto& function = Function::Cast(closure); |
94 | |
95 | // Ensure we have 3 different return instructions with yield indices attached |
96 | // to them. |
97 | TestPipeline pipeline(function, mode); |
98 | FlowGraph* flow_graph = pipeline.RunPasses({ |
99 | CompilerPass::kComputeSSA, |
100 | }); |
101 | |
102 | auto validate_indices = [](const YieldPoints& yield_points) { |
103 | EXPECT_EQ(3, yield_points.length()); |
104 | |
105 | EXPECT_EQ(1, yield_points[0].first); |
106 | EXPECT_EQ(88, yield_points[0].second.value()); |
107 | EXPECT_EQ(2, yield_points[1].first); |
108 | EXPECT_EQ(129, yield_points[1].second.value()); |
109 | EXPECT_EQ(3, yield_points[2].first); |
110 | EXPECT_EQ(170, yield_points[2].second.value()); |
111 | }; |
112 | |
113 | validate_indices(*GetYieldPointsFromGraph(flow_graph)); |
114 | |
115 | // Ensure we have 3 different yield indices attached to the code via pc |
116 | // descriptors. |
117 | const auto& error = Error::Handle( |
118 | Compiler::EnsureUnoptimizedCode(Thread::Current(), function)); |
119 | RELEASE_ASSERT(error.IsNull()); |
120 | const auto& code = Code::Handle(function.CurrentCode()); |
121 | validate_indices(*GetYieldPointsFromCode(code)); |
122 | } |
123 | |
124 | ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableJIT) { |
125 | RunTestInMode(CompilerPass::kJIT); |
126 | } |
127 | |
128 | ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableAOT) { |
129 | RunTestInMode(CompilerPass::kAOT); |
130 | } |
131 | |
132 | } // namespace dart |
133 | |