blob: a3082f9bf6315363e8a972b44809de88bc00e1d0 [file] [log] [blame] [edit]
// Copyright (c) 2025, 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 'dart:math' as math;
import '../ir/ir.dart' as ir;
import 'builder.dart';
/// Builder for the elements section of a wasm module.
class ElementsBuilder with Builder<ir.Elements> {
final ModuleBuilder _moduleBuilder;
final _functionTableBuilders = <ir.Table, ActiveFunctionSegmentBuilder>{};
late final declarativeSegmentBuilder =
DeclarativeSegmentBuilder(_moduleBuilder);
ElementsBuilder(this._moduleBuilder);
ActiveFunctionSegmentBuilder activeFunctionSegmentBuilderFor(ir.Table table) {
assert(table.type.isSubtypeOf(ir.RefType.func(nullable: true)));
assert(table.enclosingModule == _moduleBuilder.module);
return _functionTableBuilders.putIfAbsent(
table, () => ActiveFunctionSegmentBuilder(table));
}
@override
ir.Elements forceBuild() {
final segments = <ir.ElementSegment>[];
for (final b in _functionTableBuilders.values) {
segments.addAll(b.build());
}
if (declarativeSegmentBuilder._declaredFunctions.isNotEmpty) {
segments.add(declarativeSegmentBuilder.build());
}
return ir.Elements(segments);
}
}
class DeclarativeSegmentBuilder with Builder<ir.DeclarativeElementSegment> {
final ModuleBuilder _moduleBuilder;
final _declaredFunctions = <ir.BaseFunction>{};
DeclarativeSegmentBuilder(this._moduleBuilder);
/// Pre-declare [function] in a declared segment which allows using that
/// function in `ref.func` constant instructions.
void declare(ir.BaseFunction function) {
assert(function.enclosingModule == _moduleBuilder.module);
_declaredFunctions.add(function);
}
@override
ir.DeclarativeElementSegment forceBuild() =>
ir.DeclarativeElementSegment(_declaredFunctions.toList());
}
class ActiveFunctionSegmentBuilder with Builder<List<ir.ActiveElementSegment>> {
final ir.Table table;
final Map<int, ir.BaseFunction> _functions = {};
ActiveFunctionSegmentBuilder(this.table);
void setFunctionAt(int index, ir.BaseFunction function) {
assert(function.enclosingModule == table.enclosingModule);
assert(table.maxSize == null || index < table.maxSize!,
'Index $index greater than max table size ${table.maxSize}');
_functions[index] = function;
table.minSize = math.max(table.minSize, index + 1);
}
@override
List<ir.ActiveElementSegment> forceBuild() {
final entries = _functions.entries.toList()
..sort((a, b) => a.key.compareTo(b.key));
final segments = <ir.ActiveElementSegment>[];
if (const ir.RefType.func(nullable: false).isSubtypeOf(table.type)) {
ir.ActiveFunctionElementSegment? current;
int lastIndex = -2;
for (final entry in entries) {
final index = entry.key;
final function = entry.value;
if (index != lastIndex + 1) {
current = ir.ActiveFunctionElementSegment(table, index);
segments.add(current);
}
current!.entries.add(function);
lastIndex = index;
}
} else {
ir.ActiveExpressionElementSegment? current;
int lastIndex = -2;
for (final entry in entries) {
final index = entry.key;
final function = entry.value;
if (index != lastIndex + 1) {
current = ir.ActiveExpressionElementSegment(table, table.type, index);
segments.add(current);
}
current!.expressions.add([ir.RefFunc(function), ir.End()]);
lastIndex = index;
}
}
return segments;
}
}