blob: 75609a4632a70b25bd566171121847e292a3c16a [file] [log] [blame]
// 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 "vm/compiler/backend/inliner.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/il_printer.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 {
// Test that the redefinition for an inlined polymorphic function used with
// multiple receiver cids does not have a concrete type.
ISOLATE_UNIT_TEST_CASE(Inliner_PolyInliningRedefinition) {
const char* kScript = R"(
abstract class A {
String toInline() { return "A"; }
class B extends A {}
class C extends A {
String toInline() { return "C";}
class D extends A {}
testInlining(A arg) {
main() {
for (var i = 0; i < 10; i++) {
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& function =
Function::Handle(GetFunction(root_library, "testInlining"));
Invoke(root_library, "main");
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({
auto entry = flow_graph->graph_entry()->normal_entry();
EXPECT(entry != nullptr);
EXPECT(entry->initial_definitions()->length() == 1);
ParameterInstr* param = entry->initial_definitions()->At(0)->AsParameter();
// First we find the start of the prelude for the inlined instruction,
// and also keep a reference to the LoadClassId instruction for later.
LoadClassIdInstr* lcid = nullptr;
BranchInstr* prelude = nullptr;
ILMatcher cursor(flow_graph, entry);
{kMatchLoadClassId, &lcid},
{kMatchBranch, &prelude},
const Class& cls = Class::Handle(
root_library.LookupLocalClass(String::Handle(Symbols::New(thread, "B"))));
Definition* cid_B = flow_graph->GetConstant(Smi::Handle(Smi::New(;
Instruction* current = prelude;
// We walk false branches until we either reach a branch instruction that uses
// B's cid for comparison to the value returned from the LCID instruction
// above, or a default case if there was no branch instruction for B's cid.
while (true) {
const ComparisonInstr* check = current->AsBranch()->comparison();
EXPECT(check->left()->definition() == lcid);
if (check->right()->definition() == cid_B) break;
current = current->SuccessorAt(1);
// By following false paths, we should be walking a series of blocks that
// looks like:
// B#[target]:#
// Branch if <check on class ID>
// If we end up not finding a branch, then we're in a default case
// that contains a class check.
current = current->next();
if (!current->IsBranch()) {
// If we found a branch that checks against the class ID, we follow the true
// branch to a block that contains only a goto to the desired join block.
if (current->IsBranch()) {
current = current->SuccessorAt(0);
} else {
// We're in the default case, which will check the class ID to make sure
// it's the one expected for the fallthrough. That check will be followed
// by a goto to the desired join block.
const auto redef = current->AsRedefinition();
EXPECT(redef->value()->definition() == lcid);
current = current->next();
EXPECT(current->AsCheckClassId()->value()->definition() == redef);
current = current->next();
current = current->AsGoto()->successor();
// Now we should be at a block that starts like:
// BY[join]:# pred(...)
// vW <- Redefinition(vV)
// where vV is a reference to the function parameter (the receiver of
// the inlined function).
current = current->next();
EXPECT(current->AsRedefinition()->value()->definition() == param);
EXPECT(current->AsRedefinition()->Type()->ToCid() == kDynamicCid);
} // namespace dart