[dart2wasm] Generate local names in the names section

Local names for function value parameters and for "precise this", return
values are generated, state indices in `async` and `sync*` functions are
generated.

We can generate names for more locals as needed.

Change-Id: Ie919f030f0bfae8adbca90408509dd04a7414278
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419200
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Ömer Ağacan
2025-04-02 01:52:18 -07:00
committed by Commit Queue
parent 52da6a0037
commit 82282b6287
8 changed files with 92 additions and 32 deletions
+12 -8
View File
@@ -26,8 +26,9 @@ mixin AsyncCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
// (1) Create async state.
final asyncStateLocal =
b.addLocal(w.RefType(asyncSuspendStateInfo.struct, nullable: false));
final asyncStateLocal = b.addLocal(
w.RefType(asyncSuspendStateInfo.struct, nullable: false),
name: "asyncState");
// AsyncResumeFun _resume
translator.globals.readGlobal(b, translator.makeFunctionRef(resumeFun));
@@ -195,13 +196,16 @@ class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator {
Context? localContext = context;
while (localContext != null) {
if (!localContext.isEmpty) {
localContext.currentLocal =
b.addLocal(w.RefType.def(localContext.struct, nullable: true));
localContext.currentLocal = b.addLocal(
w.RefType.def(localContext.struct, nullable: true),
name: "context");
if (localContext.containsThis) {
assert(thisLocal == null);
thisLocal = b.addLocal(localContext
.struct.fields[localContext.thisFieldIndex].type.unpacked
.withNullability(false));
thisLocal = b.addLocal(
localContext
.struct.fields[localContext.thisFieldIndex].type.unpacked
.withNullability(false),
name: "this");
translator
.getDummyValuesCollectorForModule(b.module)
.instantiateDummyValue(b, thisLocal!.type);
@@ -214,7 +218,7 @@ class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator {
}
// Read target index from the suspend state.
targetIndexLocal = addLocal(w.NumType.i32);
targetIndexLocal = addLocal(w.NumType.i32, name: "targetIndex");
b.local_get(_suspendStateLocal);
b.struct_get(
asyncSuspendStateInfo.struct, FieldIndex.asyncSuspendStateTargetIndex);
+20 -12
View File
@@ -115,9 +115,8 @@ abstract class AstCodeGenerator
w.ValueType translateType(DartType type) => translator.translateType(type);
w.Local addLocal(w.ValueType type) {
return b.addLocal(type);
}
w.Local addLocal(w.ValueType type, {String? name}) =>
b.addLocal(type, name: name);
DartType dartTypeOf(Expression exp) {
if (exp is ConstantExpression) {
@@ -257,7 +256,12 @@ abstract class AstCodeGenerator
int index,
Constant? defaultValue,
bool isRequired) {
w.Local local = paramLocals[implicitParams + index];
final localIndex = implicitParams + index;
w.Local local = paramLocals[localIndex];
final variableName = variable.name;
if (variableName != null) {
b.localNames[local.index] = variableName;
}
if (defaultValue == ParameterInfo.defaultValueSentinel) {
// The default value for this parameter differs between implementations
// within the same selector. This means that callers will pass the
@@ -357,7 +361,8 @@ abstract class AstCodeGenerator
if (local.type == w.RefType.extern(nullable: true) &&
!(parameterType is InterfaceType &&
parameterType.classNode == translator.wasmExternRefClass)) {
w.Local newLocal = addLocal(translateType(parameterType));
w.Local newLocal =
addLocal(translateType(parameterType), name: parameter.name);
b.local_get(local);
translator.convertType(b, local.type, newLocal.type);
b.local_set(newLocal);
@@ -490,9 +495,10 @@ abstract class AstCodeGenerator
member.isInstanceMember || reference.isConstructorBodyReference;
if (hasThis) {
thisLocal = paramLocals[0];
b.localNames[thisLocal!.index] = "this";
final preciseThisType = translator.preciseThisFor(member);
if (translator.needsConversion(thisLocal!.type, preciseThisType)) {
preciseThisLocal = addLocal(preciseThisType);
preciseThisLocal = addLocal(preciseThisType, name: "preciseThis");
b.local_get(thisLocal!);
translator.convertType(b, thisLocal!.type, preciseThisType);
b.local_set(preciseThisLocal!);
@@ -537,9 +543,10 @@ abstract class AstCodeGenerator
}
if (context.containsThis) {
thisLocal = addLocal(context
.struct.fields[context.thisFieldIndex].type.unpacked
.withNullability(false));
thisLocal = addLocal(
context.struct.fields[context.thisFieldIndex].type.unpacked
.withNullability(false),
name: "this");
preciseThisLocal = thisLocal;
b.struct_get(context.struct, context.thisFieldIndex);
@@ -794,7 +801,7 @@ abstract class AstCodeGenerator
w.Local? local;
Capture? capture = closures.captures[node];
if (capture == null || !capture.written) {
local = addLocal(type);
local = addLocal(type, name: node.name);
locals[node] = local;
}
@@ -836,7 +843,7 @@ abstract class AstCodeGenerator
w.Local? local;
final Capture? capture = closures.captures[node];
if (capture == null || !capture.written) {
local = addLocal(type);
local = addLocal(type, name: node.name);
locals[node] = local;
}
@@ -1352,7 +1359,8 @@ abstract class AstCodeGenerator
// Since the flow of the return value through the returnValueLocal
// crosses control-flow constructs, the local needs to always have a
// defaultable type in order for the Wasm code to validate.
returnValueLocal ??= addLocal(returnType.withNullability(true));
returnValueLocal ??=
addLocal(returnType.withNullability(true), name: "returnValue");
b.local_set(returnValueLocal!);
}
b.br(returnFinalizers.last.label);
+2 -1
View File
@@ -1761,7 +1761,8 @@ class Intrinsifier {
w.ArrayType arrayType =
(functionType.outputs.single as w.RefType).heapType as w.ArrayType;
w.Local object = paramLocals[0];
w.Local preciseObject = codeGen.addLocal(classInfo.nonNullableType);
w.Local preciseObject =
codeGen.addLocal(classInfo.nonNullableType, name: "this");
b.local_get(object);
b.ref_cast(classInfo.nonNullableType);
b.local_set(preciseObject);
+9 -6
View File
@@ -168,13 +168,16 @@ class SyncStarStateMachineCodeGenerator extends StateMachineCodeGenerator {
Context? localContext = context;
while (localContext != null) {
if (!localContext.isEmpty) {
localContext.currentLocal =
b.addLocal(w.RefType.def(localContext.struct, nullable: true));
localContext.currentLocal = b.addLocal(
w.RefType.def(localContext.struct, nullable: true),
name: "context");
if (localContext.containsThis) {
assert(thisLocal == null);
thisLocal = b.addLocal(localContext
.struct.fields[localContext.thisFieldIndex].type.unpacked
.withNullability(false));
thisLocal = b.addLocal(
localContext
.struct.fields[localContext.thisFieldIndex].type.unpacked
.withNullability(false),
name: "this");
translator
.getDummyValuesCollectorForModule(b.module)
.instantiateDummyValue(b, thisLocal!.type);
@@ -187,7 +190,7 @@ class SyncStarStateMachineCodeGenerator extends StateMachineCodeGenerator {
}
// Read target index from the suspend state.
targetIndexLocal = addLocal(w.NumType.i32);
targetIndexLocal = addLocal(w.NumType.i32, name: "targetIndex");
b.local_get(_suspendStateLocal);
b.struct_get(suspendStateInfo.struct, FieldIndex.suspendStateTargetIndex);
b.local_set(targetIndexLocal);
@@ -171,6 +171,12 @@ class InstructionsBuilder with Builder<ir.Instructions> {
/// Locals declared in this body, including parameters.
final List<ir.Local> locals = [];
/// Names of the locals in `locals`.
///
/// Most of the locals won't have names, so this is a [Map] instead of [List]
/// like [locals], with local indices as keys and names as values.
final Map<int, String> localNames = {};
/// Whether a textual trace of the instruction stream should be recorded when
/// emitting instructions (provided asserts are enabled).
///
@@ -251,8 +257,8 @@ class InstructionsBuilder with Builder<ir.Instructions> {
}
@override
ir.Instructions forceBuild() => ir.Instructions(
locals, _instructions, _stackTraces, _traceLines, _sourceMappings);
ir.Instructions forceBuild() => ir.Instructions(locals, localNames,
_instructions, _stackTraces, _traceLines, _sourceMappings);
void _add(ir.Instruction i) {
assert(!_constantExpression || i.isConstant,
@@ -271,7 +277,11 @@ class InstructionsBuilder with Builder<ir.Instructions> {
return local;
}
ir.Local addLocal(ir.ValueType type) {
ir.Local addLocal(ir.ValueType type, {String? name}) {
if (name != null) {
final index = locals.length;
localNames[index] = name;
}
final local = ir.Local(locals.length, type);
locals.add(local);
_localInitialized.add(type.defaultable);
@@ -46,6 +46,8 @@ class DefinedFunction extends BaseFunction implements Serializable {
/// All local variables defined in the function, including its inputs.
List<Local> get locals => body.locals;
Map<int, String> get localNames => body.localNames;
DefinedFunction(
super.enclosingModule, this.body, super.finalizableIndex, super.type,
[super.functionName]);
@@ -10,6 +10,12 @@ class Instructions implements Serializable {
/// The locals used by this group of instructions.
final List<Local> locals;
/// Names of the locals in `locals`.
///
/// Most of the locals won't have names, so this is a [Map] instead of [List]
/// like [locals], with local indices as keys and names as values.
final Map<int, String> localNames;
/// A sequence of Wasm instructions.
final List<Instruction> instructions;
@@ -27,8 +33,8 @@ class Instructions implements Serializable {
final List<SourceMapping>? _sourceMappings;
/// Create a new instruction sequence.
Instructions(this.locals, this.instructions, this._stackTraces,
this._traceLines, this._sourceMappings);
Instructions(this.locals, this.localNames, this.instructions,
this._stackTraces, this._traceLines, this._sourceMappings);
@override
void serialize(Serializer s) {
@@ -471,6 +471,28 @@ class NameSection extends CustomSection {
}
}
final localNameSubsection = Serializer();
List<ir.DefinedFunction> functionsWithLocalNames = [];
for (final function in functions) {
if (function is ir.DefinedFunction) {
if (function.localNames.isNotEmpty) {
functionsWithLocalNames.add(function);
}
}
}
localNameSubsection.writeUnsigned(functionsWithLocalNames.length);
if (functionsWithLocalNames.isNotEmpty) {
for (final function in functionsWithLocalNames) {
localNameSubsection.writeUnsigned(function.finalizableIndex.value);
localNameSubsection.writeUnsigned(function.localNames.length);
for (final entry in function.localNames.entries) {
localNameSubsection.writeUnsigned(entry.key);
localNameSubsection.writeName(entry.value);
}
}
}
s.writeByte(0); // Module name subsection
s.writeUnsigned(moduleNameSubsection.data.length);
s.writeData(moduleNameSubsection);
@@ -479,6 +501,10 @@ class NameSection extends CustomSection {
s.writeUnsigned(functionNameSubsection.data.length);
s.writeData(functionNameSubsection);
s.writeByte(2); // Local names substion
s.writeUnsigned(localNameSubsection.data.length);
s.writeData(localNameSubsection);
s.writeByte(4); // Type names subsection
s.writeUnsigned(typeNameSubsection.data.length);
s.writeData(typeNameSubsection);