blob: b07eceb22e045d186c0436f44b8a40d594de4a94 [file]
// 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.
// Verify that load forwarding eliminates redundant indexed loads on
// _ImmutableList (kImmutableArrayCid) across side-effecting calls, including
// on lists returned by List.unmodifiable.
import 'package:expect/expect.dart';
import 'package:vm/testing/il_matchers.dart';
class Holder {
final List<int> items;
Holder(bool flag) : items = flag ? const [10, 20, 30] : const [40, 50, 60];
}
@pragma('vm:never-inline')
void sideEffect() {}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int sumAcrossCall(Holder h) {
final a = h.items[0];
sideEffect();
final b = h.items[0];
return a + b;
}
void matchIL$sumAcrossCall(FlowGraph graph) {
graph.match([
match.block('Graph'),
match.block('Function', [
'h' << match.Parameter(index: 0),
'items' << match.LoadField('h', slot: 'items'),
'elem' << match.LoadIndexed('items', match.any),
match.StaticCall(function: 'sideEffect'),
'unboxed' << match.UnboxInt64('elem'),
'sum' << match.BinaryInt64Op('unboxed', 'unboxed'),
match.DartReturn('sum'),
]),
]);
}
@pragma('vm:prefer-inline')
List<int> _buildFrozen(int x, int y) {
return List<int>.unmodifiable(<int>[x, y]);
}
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int buildThenFreeze(int x, int y) {
final list = _buildFrozen(x, y);
final a = list[0];
sideEffect();
final b = list[0];
final c = list[1];
return a + b + c;
}
void matchIL$buildThenFreeze(FlowGraph graph) {
graph.match([
match.block('Graph'),
match.block('Function', [
'a' << match.LoadIndexed(match.any, match.any),
match.StaticCall(function: 'sideEffect'),
'c' << match.LoadIndexed(match.any, match.any),
'a_unboxed' << match.UnboxInt64('a'),
'ab' << match.BinaryInt64Op('a_unboxed', 'a_unboxed'),
'c_unboxed' << match.UnboxInt64('c'),
'abc' << match.BinaryInt64Op('ab', 'c_unboxed'),
match.DartReturn('abc'),
]),
]);
}
void main(List<String> args) {
final flag = args.isEmpty;
Expect.equals(20, sumAcrossCall(Holder(flag)));
Expect.equals(80, sumAcrossCall(Holder(!flag)));
Expect.equals(35, buildThenFreeze(10, 15));
Expect.equals(70, buildThenFreeze(20, 30));
}