| 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 | |