[vm,dyn_modules] Fix single-target cid range dispatch for dynamically loaded classes
In AOT mode, when call site is transitioned from monomorphic state (with receiver cid1) to polymorphic (with receiver cid2), there is an optimization which checks if all _allocated_ classes in the class id range cid1..cid2 have the same dispatch target. If so, a specialized SingleTargetCall stub is used. The problem is that 'allocated' bit is only set during precompilation, and dynamically loaded classes were not considered as valid receiver classes by this optimization. As a result, the following situation could happen: cid1 < cid3 < cid2, cid1 dispatches to target1 cid2 dispatches to target1 cid3 should dispatch to target2, but it is still in range cid1..cid2 and SingleTargetCall stub would incorrectly dispatch it to target1. The fix is to treat all dynamically loaded classes as allocated when checking for single target optimization. TEST=pkg/dynamic_modules/test/data/single_target_cid_range_dispatch Fixes b/493677699 Change-Id: I42e407385a5d9b0a4a1017f713b47587c3c6a818 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/488920 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
committed by
Commit Queue
parent
aa3665ed78
commit
efce7c1cb0
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2026, 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.
|
||||
|
||||
callable:
|
||||
- library: 'dart:core'
|
||||
- library: 'shared/shared.dart'
|
||||
|
||||
extendable:
|
||||
- library: 'shared/shared.dart'
|
||||
class: ['Base1', 'Base2']
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
import '../../common/testing.dart' as helper;
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'shared/shared.dart';
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('wasm:never-inline')
|
||||
String callCreateElement(Base obj) {
|
||||
// Call site which dispatches to different dynamic objects.
|
||||
return obj.createElement();
|
||||
}
|
||||
|
||||
/// Regression test for b/493677699.
|
||||
/// Verifies that single target cid range dispatching works properly
|
||||
/// for dynamically loaded classes.
|
||||
void main() async {
|
||||
final createObj = (await helper.load('entry1.dart')) as Base Function(int);
|
||||
|
||||
// Transition call site from unlinked to monomorphic.
|
||||
Expect.equals('Element1', callCreateElement(createObj(1)));
|
||||
// Transition call site from monomorphic to a single target cid range
|
||||
// (if a dynamically loaded class C2 is not properly accounted).
|
||||
Expect.equals('Element1', callCreateElement(createObj(3)));
|
||||
// Class id of C2 is within [C1..C3] range, so single target dispatch
|
||||
// would result in the incorrect dispatch target.
|
||||
Expect.equals('Element2', callCreateElement(createObj(2)));
|
||||
|
||||
helper.done();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
import '../shared/shared.dart';
|
||||
|
||||
class C1 extends Base1 {}
|
||||
|
||||
class C2 extends Base2 {}
|
||||
|
||||
class C3 extends Base1 {}
|
||||
|
||||
@pragma('dyn-module:entry-point')
|
||||
Object? dynamicModuleEntrypoint() {
|
||||
return (int arg) => switch (arg) {
|
||||
1 => C1(),
|
||||
2 => C2(),
|
||||
3 => C3(),
|
||||
_ => throw 'Unexpected $arg',
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
abstract class Base {
|
||||
String createElement();
|
||||
}
|
||||
|
||||
class Base1 extends Base {
|
||||
@override
|
||||
String createElement() => 'Element1';
|
||||
}
|
||||
|
||||
class Base2 extends Base {
|
||||
@override
|
||||
String createElement() => 'Element2';
|
||||
}
|
||||
@@ -2559,7 +2559,7 @@ static bool IsSingleTarget(IsolateGroup* isolate_group,
|
||||
if (!table->HasValidClassAt(cid)) continue;
|
||||
cls = table->At(cid);
|
||||
if (cls.is_abstract()) continue;
|
||||
if (!cls.is_allocated()) continue;
|
||||
if (!cls.is_allocated() && !cls.is_declared_in_bytecode()) continue;
|
||||
other_target = Resolver::ResolveDynamicAnyArgs(zone, cls, name,
|
||||
/*allow_add=*/false);
|
||||
if (other_target.ptr() != target.ptr()) {
|
||||
|
||||
Reference in New Issue
Block a user