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
12namespace dart {
13
14using Pair = std::pair<intptr_t, TokenPosition>;
15using YieldPoints = ZoneGrowableArray<Pair>;
16
17int LowestFirst(const Pair* a, const Pair* b) {
18 return a->first - b->first;
19}
20
21static 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
42static 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
55void 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
124ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableJIT) {
125 RunTestInMode(CompilerPass::kJIT);
126}
127
128ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableAOT) {
129 RunTestInMode(CompilerPass::kAOT);
130}
131
132} // namespace dart
133