| // Copyright (c) 2019, 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 <utility> |
| |
| #include "vm/closure_functions_cache.h" |
| #include "vm/compiler/backend/il_test_helper.h" |
| #include "vm/compiler/compiler_pass.h" |
| #include "vm/object.h" |
| #include "vm/unit_test.h" |
| |
| namespace dart { |
| |
| using Pair = std::pair<intptr_t, TokenPosition>; |
| using YieldPoints = ZoneGrowableArray<Pair>; |
| |
| int LowestFirst(const Pair* a, const Pair* b) { |
| return a->first - b->first; |
| } |
| |
| static YieldPoints* GetYieldPointsFromGraph(FlowGraph* flow_graph) { |
| auto array = new (flow_graph->zone()) YieldPoints(); |
| const auto& blocks = flow_graph->reverse_postorder(); |
| for (auto block : blocks) { |
| ForwardInstructionIterator it(block); |
| while (!it.Done()) { |
| if (auto return_instr = it.Current()->AsReturn()) { |
| if (return_instr->yield_index() != |
| UntaggedPcDescriptors::kInvalidYieldIndex) { |
| ASSERT(return_instr->yield_index() > 0); |
| array->Add( |
| Pair(return_instr->yield_index(), return_instr->token_pos())); |
| } |
| } |
| it.Advance(); |
| } |
| } |
| array->Sort(LowestFirst); |
| return array; |
| } |
| |
| static YieldPoints* GetYieldPointsFromCode(const Code& code) { |
| auto array = new YieldPoints(); |
| const auto& pc_descriptor = PcDescriptors::Handle(code.pc_descriptors()); |
| PcDescriptors::Iterator it(pc_descriptor, UntaggedPcDescriptors::kOther); |
| while (it.MoveNext()) { |
| if (it.YieldIndex() != UntaggedPcDescriptors::kInvalidYieldIndex) { |
| array->Add(Pair(it.YieldIndex(), it.TokenPos())); |
| } |
| } |
| array->Sort(LowestFirst); |
| return array; |
| } |
| |
| void RunTestInMode(CompilerPass::PipelineMode mode) { |
| const char* kScript = |
| R"( |
| import 'dart:async'; |
| |
| Future foo() async { |
| print('pos-0'); |
| await 0; |
| print('pos-1'); |
| await 1; |
| print('pos-2'); |
| await 2; |
| } |
| )"; |
| |
| SetupCoreLibrariesForUnitTest(); |
| |
| const auto& root_library = Library::Handle(LoadTestScript(kScript)); |
| // Ensure the outer function was compiled once, ensuring we have a closure |
| // function for the inner closure. |
| Invoke(root_library, "foo"); |
| |
| const auto& outer_function = |
| Function::Handle(GetFunction(root_library, "foo")); |
| |
| // Grab the inner, lazily created, closure from the object store. |
| const auto& function = Function::Handle( |
| ClosureFunctionsCache::GetUniqueInnerClosure(outer_function)); |
| RELEASE_ASSERT(function.IsFunction()); |
| |
| // Ensure we have 3 different return instructions with yield indices attached |
| // to them. |
| TestPipeline pipeline(function, mode); |
| FlowGraph* flow_graph = pipeline.RunPasses({ |
| CompilerPass::kComputeSSA, |
| }); |
| |
| auto validate_indices = [](const YieldPoints& yield_points) { |
| EXPECT_EQ(3, yield_points.length()); |
| |
| EXPECT_EQ(1, yield_points[0].first); |
| EXPECT_EQ(88, yield_points[0].second.Pos()); |
| EXPECT_EQ(2, yield_points[1].first); |
| EXPECT_EQ(129, yield_points[1].second.Pos()); |
| EXPECT_EQ(3, yield_points[2].first); |
| EXPECT_EQ(170, yield_points[2].second.Pos()); |
| }; |
| |
| validate_indices(*GetYieldPointsFromGraph(flow_graph)); |
| |
| // Ensure we have 3 different yield indices attached to the code via pc |
| // descriptors. |
| const auto& error = Error::Handle( |
| Compiler::EnsureUnoptimizedCode(Thread::Current(), function)); |
| RELEASE_ASSERT(error.IsNull()); |
| const auto& code = Code::Handle(function.CurrentCode()); |
| validate_indices(*GetYieldPointsFromCode(code)); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableJIT) { |
| RunTestInMode(CompilerPass::kJIT); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableAOT) { |
| RunTestInMode(CompilerPass::kAOT); |
| } |
| |
| } // namespace dart |