[dart2wasm] Move constants that are used by multiple modules out of main module

This reduces ACX gallery main module by 4.3%.

If a constant is used by multiple (non-main) modules this CL will
  * force the constant to be lazy now
  * make a global in the main module
  * make each use lazily initialize with a module-local initializer
    function (i.e. several modules may have the same initializer
    function)

I plan on doing more optimizations for this multi-module constant
use scenario.

Change-Id: If5c6d5de474f9794415c889f0bd98465b73e4b96
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/458860
Reviewed-by: Ömer Ağacan <omersa@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann
2025-11-04 00:33:45 -08:00
committed by Commit Queue
parent 090b7cbe40
commit 4bc9d31957
12 changed files with 406 additions and 78 deletions
+96 -61
View File
@@ -26,13 +26,24 @@ const int maxArrayNewFixedLength = 10000;
const bool forceDelayedConstantDefinition = false;
class ConstantDefinition {
final w.ModuleBuilder module;
final w.Global global;
final w.BaseFunction? _initFunction;
final Map<w.ModuleBuilder, w.BaseFunction>? _initFunctionPerUsingModule;
ConstantDefinition(this.module, this.global, this._initFunction);
ConstantDefinition(
this.global, this._initFunction, this._initFunctionPerUsingModule) {
assert(_initFunction == null || _initFunctionPerUsingModule == null);
}
bool get isLazy => _initFunction != null;
bool get isLazy =>
_initFunction != null || _initFunctionPerUsingModule != null;
w.BaseFunction initializer(w.ModuleBuilder usingModule) {
if (_initFunctionPerUsingModule != null) {
return _initFunctionPerUsingModule[usingModule]!;
}
return _initFunction!;
}
}
class ConstantInfo {
@@ -254,9 +265,10 @@ class Constants {
}
if (definition != null && !definition.isLazy) {
if (definition.module == usingModule) return true;
if (definition.module == baseModule) return true;
if (definition.module == translator.mainModule) return true;
final definingModule = definition.global.enclosingModule;
if (definingModule == usingModule.module) return true;
if (definingModule == baseModule.module) return true;
if (definingModule == translator.mainModule.module) return true;
}
return false;
@@ -1532,19 +1544,6 @@ class _ConstantAccessor {
rememberConstantUse(info);
// If we have uses in multiple modules, we should place it a module that is
// guaranteed to be loaded at the use time of those modules.
//
// => We are conservative and assign it to the main module.
//
// NOTE: Improve this by finding the closest common ancestor between the
// modules in the loading graph.
if (!forceDelayedConstantDefinition && moduleUses[info]!.length > 1) {
final definition =
_defineConstantInModuleRecursive(translator.mainModule, info);
return _readDefinedConstant(b, info, definition);
}
// The current module is the only one using the constant atm, but in the
// future other modules may also use it. So we don't know where to place
// it just yet.
@@ -1573,7 +1572,8 @@ class _ConstantAccessor {
w.ValueType _readDefinedConstant(w.InstructionsBuilder b, ConstantInfo info,
ConstantDefinition definition) {
final globalDefinition = definition.global;
final globalInitializer = definition._initFunction;
final globalInitializer =
definition.isLazy ? definition.initializer(b.moduleBuilder) : null;
// Eagerly initialized constant.
if (globalInitializer == null) {
@@ -1612,46 +1612,61 @@ class _ConstantAccessor {
//
// We want to choose a module that is available by the time the constant is
// used. If only one module uses the constant we place it in that module. If
// it's used by multiple modules we (conservatively) place it in the main
// module.
//
// NOTE: Improve this by finding the closest common ancestor between the
// modules in the loading graph.
// it's used by multiple modules we make a global in the main module and
// make all using modules bring an initializer function with them.
Set<w.ModuleBuilder>? deferredUses;
if (assignedModule == null) {
final uses = moduleUses[info]!;
assert(uses.isNotEmpty);
assignedModule = uses.length == 1 ? uses.single : translator.mainModule;
if (uses.length == 1) {
assignedModule = uses.single;
} else if (uses.contains(translator.mainModule)) {
assignedModule = translator.mainModule;
} else {
// Will become lazy constant with global in main module and initializer
// in all using modules.
deferredUses = uses;
}
}
return _defineConstantInModule(assignedModule, info);
return _defineConstantInModule(assignedModule, deferredUses, info);
}
ConstantDefinition _defineConstantInModule(
w.ModuleBuilder targetModule, ConstantInfo info) {
ConstantDefinition _defineConstantInModule(w.ModuleBuilder? targetModule,
Set<w.ModuleBuilder>? deferredUses, ConstantInfo info) {
assert((targetModule != null) != (deferredUses != null));
assert(deferredUses == null ||
deferredUses.length > 1 &&
!deferredUses.contains(translator.mainModule));
final constant = info.constant;
// The constant itself may be forced to be lazy (e.g. array size too large).
bool lazy = !info.canBeEager;
// If there's uses in N different deferred modules, we make it lazy, define
// global in main module & initializer in each using module.
lazy |= deferredUses != null;
// The constant's children may influence laziness.
if (!lazy) {
for (final child in info.children) {
final definition = child._definition!;
// If the child is lazy, this constant becomes lazy.
if (definition._initFunction != null) {
if (definition.isLazy) {
lazy = true;
break;
}
// If we place the constant in a module that may be loaded before the
// constants of children, it must get initialized lazily.
final childModule = definition.module;
final childModule = definition.global.enclosingModule;
final baseModule = translator.isDynamicSubmodule
? translator.dynamicSubmodule
: translator.mainModule;
if (childModule != targetModule &&
childModule != translator.mainModule &&
childModule != baseModule) {
if (childModule != targetModule?.module &&
childModule != translator.mainModule.module &&
childModule != baseModule.module) {
lazy = true;
break;
}
@@ -1663,17 +1678,28 @@ class _ConstantAccessor {
if (!lazy) {
// The constant codegen code may decide to make it lazy depending on which
// module it's going to be placed in.
lazy = info._forceLazy(info, targetModule);
lazy = info._forceLazy(info, targetModule!);
}
// Define the lazy or non-lazy constant in the module.
final ConstantDefinition definition;
if (lazy) {
final (global, initFunction) = _createLazyConstant(targetModule, info);
definition = ConstantDefinition(targetModule, global, initFunction);
if (targetModule == null) {
final name = _constantName(info.constant);
final global = _createLazyGlobal(translator.mainModule, name, info);
final initFunctions = {
for (final usingModule in deferredUses!)
usingModule:
_createLazyInitializer(usingModule, global, name, info),
};
definition = ConstantDefinition(global, null, initFunctions);
} else {
final (global, initFunction) = _createLazyConstant(targetModule, info);
definition = ConstantDefinition(global, initFunction, null);
}
} else {
final global = _createNonLazyConstant(targetModule, info);
definition = ConstantDefinition(targetModule, global, null);
final global = _createNonLazyConstant(targetModule!, info);
definition = ConstantDefinition(global, null, null);
}
info.setDefinition(definition);
@@ -1681,7 +1707,7 @@ class _ConstantAccessor {
assert(translator.dynamicModuleSupportEnabled &&
!translator.isDynamicSubmodule);
translator.exporter.exportDynamicConstant(
targetModule, constant, definition.global,
targetModule!, constant, definition.global,
initializer: definition._initFunction);
}
return definition;
@@ -1713,40 +1739,49 @@ class _ConstantAccessor {
globalName, fakeInitializer);
}
info._definition =
ConstantDefinition(fakeMainApp, fakeGlobal, fakeInitializer);
info._definition = ConstantDefinition(fakeGlobal, fakeInitializer, null);
}
(w.GlobalBuilder, w.FunctionBuilder) _createLazyConstant(
w.ModuleBuilder targetModule, ConstantInfo info) {
final constant = info.constant;
final type = info.type;
final generator = info._codeGen;
final name = _constantName(info.constant);
// Create uninitialized global and function to initialize it.
final definedGlobal = _createLazyGlobal(targetModule, name, info);
final initFunction =
_createLazyInitializer(targetModule, definedGlobal, name, info);
final name = _constantName(constant);
final globalType = w.GlobalType(type.withNullability(true));
final definedGlobal = targetModule.globals.define(globalType, name);
return (definedGlobal, initFunction);
}
w.GlobalBuilder _createLazyGlobal(
w.ModuleBuilder module, String name, ConstantInfo info) {
final globalType = w.GlobalType(info.type.withNullability(true));
final definedGlobal = module.globals.define(globalType, name);
definedGlobal.initializer.ref_null(w.HeapType.none);
definedGlobal.initializer.end();
return definedGlobal;
}
w.FunctionBuilder _createLazyInitializer(w.ModuleBuilder module,
w.GlobalBuilder definedGlobal, String name, ConstantInfo info) {
final type = info.type;
final initFunctionType =
translator.typesBuilder.defineFunction(const [], [type]);
final initFunction = targetModule.functions
.define(initFunctionType, '$name (lazy initializer)}');
final b2 = initFunction.body;
generator(info, b2, true);
final initFunction =
module.functions.define(initFunctionType, '$name (lazy initializer)}');
final b = initFunction.body;
info._codeGen(info, b, true);
if (info.needsRuntimeCanonicalization) {
final valueLocal = b2.addLocal(type);
constant.accept(ConstantCanonicalizer(translator, b2, valueLocal));
final valueLocal = b.addLocal(type);
info.constant.accept(ConstantCanonicalizer(translator, b, valueLocal));
}
w.Local temp = b2.addLocal(type);
b2.local_tee(temp);
b2.global_set(definedGlobal);
b2.local_get(temp);
b2.end();
return (definedGlobal, initFunction);
w.Local temp = b.addLocal(type);
b.local_tee(temp);
translator.globals.writeGlobal(b, definedGlobal);
b.local_get(temp);
b.end();
return initFunction;
}
w.GlobalBuilder _createNonLazyConstant(
+19
View File
@@ -72,6 +72,25 @@ class Globals {
return global.type.type;
}
/// Dual to [readGlobal]
void writeGlobal(w.InstructionsBuilder b, w.Global global) {
final owningModule = translator.moduleToBuilder[global.enclosingModule]!;
final callingModule = b.moduleBuilder;
if (owningModule == callingModule) {
b.global_set(global);
} else if (translator.isMainModule(owningModule)) {
final importedGlobal = _globalsModuleMap.get(global, callingModule);
b.global_set(importedGlobal);
} else {
// NOTE: Currently unused but we can support this just like in
// [readGlobal] via an indirect call to a setter function that's installed
// by the global-owning module.
throw UnsupportedError(
'Currently we can only write to globals in the main module or in the '
'local module in which the write happens.');
}
}
/// Return (and if needed create) the Wasm global corresponding to a static
/// field.
w.Global getGlobalForStaticField(Field field) {
@@ -0,0 +1,32 @@
// 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.
// functionFilter=mainImpl
// functionFilter=mod.*Use
// functionFilter=MyConstClass
// functionFilter=shared-const
// tableFilter=static[0-9]+
// globalFilter=MyConstClass
// globalFilter=shared-const
// type=MyConstClass
// compilerOption=--enable-deferred-loading
// compilerOption=--no-minify
import 'deferred.constant.multi_module_use.h.0.dart' deferred as h0;
import 'deferred.constant.multi_module_use.h.1.dart' deferred as h1;
void main() async {
await h0.loadLibrary();
await h1.loadLibrary();
final returnShared = int.parse('1') == 0;
mainImpl(returnShared);
}
@pragma('wasm:never-inline')
void mainImpl(bool returnShared) {
if (!identical(h0.modH0Use(returnShared), h1.modH1Use(returnShared))) {
throw 'bad';
}
}
@@ -0,0 +1,12 @@
// 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 'deferred.constant.multi_module_use.h.2.dart';
@pragma('wasm:never-inline')
MyConstClass modH0Use(bool nonShared) {
return nonShared
? const MyConstClass('h0-nonshared-const')
: const MyConstClass('shared-const');
}
@@ -0,0 +1,12 @@
// 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 'deferred.constant.multi_module_use.h.2.dart';
@pragma('wasm:never-inline')
MyConstClass modH1Use(bool nonShared) {
return nonShared
? const MyConstClass('h1-nonshared-const')
: const MyConstClass('shared-const');
}
@@ -0,0 +1,8 @@
// 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.
class MyConstClass {
final String b;
const MyConstClass(this.b);
}
@@ -0,0 +1,46 @@
(module $module0
(type $#Top (struct
(field $field0 i32)))
(type $Object (sub $#Top (struct
(field $field0 i32)
(field $field1 (mut i32)))))
(type $JSStringImpl (sub final $Object (struct
(field $field0 i32)
(field $field1 (mut i32))
(field $_ref externref))))
(type $MyConstClass (sub final $Object (struct
(field $field0 i32)
(field $field1 (mut i32))
(field $b (ref $JSStringImpl)))))
(type $type1 (func
(param $var0 i32)
(result (ref $MyConstClass))))
(global $"C370 \"bad\"" (ref $JSStringImpl) <...>)
(global $"C489 \"shared-const\"" (mut (ref null $JSStringImpl))
(ref.null none))
(global $"C490 MyConstClass" (mut (ref null $MyConstClass))
(ref.null none))
(table $static0-0 (export "static0-0") 2 (ref null $type1))
(func $Error._throwWithCurrentStackTrace (param $var0 (ref $#Top)) <...>)
(func $"mainImpl <noInline>" (param $var0 i32)
(local $var1 (ref $MyConstClass))
i64.const 0
call $checkLibraryIsLoadedFromLoadId
local.get $var0
i32.const 0
call_indirect $static0-0 (param i32) (result (ref $MyConstClass))
i64.const 1
call $checkLibraryIsLoadedFromLoadId
local.get $var0
i32.const 1
call_indirect $static0-0 (param i32) (result (ref $MyConstClass))
ref.eq
i32.eqz
if
global.get $"C370 \"bad\""
call $Error._throwWithCurrentStackTrace
unreachable
end
)
(func $checkLibraryIsLoadedFromLoadId (param $var0 i64) <...>)
)
@@ -0,0 +1,57 @@
(module $module1
(type $#Top (struct
(field $field0 i32)))
(type $Object (sub $#Top (struct
(field $field0 i32)
(field $field1 (mut i32)))))
(type $JSStringImpl (sub final $Object (struct
(field $field0 i32)
(field $field1 (mut i32))
(field $_ref externref))))
(type $MyConstClass (sub final $Object (struct
(field $field0 i32)
(field $field1 (mut i32))
(field $b (ref $JSStringImpl)))))
(global $S.h1-nonshared-const (import "S" "h1-nonshared-const") (ref extern))
(global $S.shared-const (import "S" "shared-const") (ref extern))
(global $"C489 \"shared-const\"" (import "module0" "global0") (ref null $JSStringImpl))
(global $"C490 MyConstClass" (import "module0" "global1") (ref null $MyConstClass))
(global $"C488 MyConstClass" (ref $MyConstClass)
(i32.const 107)
(i32.const 0)
(i32.const 4)
(i32.const 0)
(global.get $S.h1-nonshared-const)
(struct.new $JSStringImpl)
(struct.new $MyConstClass))
(func $"modH1Use <noInline>" (param $var0 i32) (result (ref $MyConstClass))
(local $var1 (ref $JSStringImpl))
(local $var2 (ref $MyConstClass))
local.get $var0
if (result (ref $MyConstClass))
global.get $"C488 MyConstClass"
else
block $label0 (result (ref $MyConstClass))
global.get $"C490 MyConstClass"
br_on_non_null $label0
i32.const 107
i32.const 0
block $label1 (result (ref $JSStringImpl))
global.get $"C489 \"shared-const\""
br_on_non_null $label1
i32.const 4
i32.const 0
global.get $S.shared-const
struct.new $JSStringImpl
local.tee $var1
global.set $"C489 \"shared-const\""
local.get $var1
end $label1
struct.new $MyConstClass
local.tee $var2
global.set $"C490 MyConstClass"
local.get $var2
end $label0
end
)
)
@@ -0,0 +1,2 @@
(module $module2
)
@@ -0,0 +1,57 @@
(module $module3
(type $#Top (struct
(field $field0 i32)))
(type $Object (sub $#Top (struct
(field $field0 i32)
(field $field1 (mut i32)))))
(type $JSStringImpl (sub final $Object (struct
(field $field0 i32)
(field $field1 (mut i32))
(field $_ref externref))))
(type $MyConstClass (sub final $Object (struct
(field $field0 i32)
(field $field1 (mut i32))
(field $b (ref $JSStringImpl)))))
(global $S.shared-const (import "S" "shared-const") (ref extern))
(global $"C489 \"shared-const\"" (import "module0" "global0") (ref null $JSStringImpl))
(global $"C490 MyConstClass" (import "module0" "global1") (ref null $MyConstClass))
(global $S.h0-nonshared-const (import "S" "h0-nonshared-const") (ref extern))
(global $"C492 MyConstClass" (ref $MyConstClass)
(i32.const 107)
(i32.const 0)
(i32.const 4)
(i32.const 0)
(global.get $S.h0-nonshared-const)
(struct.new $JSStringImpl)
(struct.new $MyConstClass))
(func $"modH0Use <noInline>" (param $var0 i32) (result (ref $MyConstClass))
(local $var1 (ref $JSStringImpl))
(local $var2 (ref $MyConstClass))
local.get $var0
if (result (ref $MyConstClass))
global.get $"C492 MyConstClass"
else
block $label0 (result (ref $MyConstClass))
global.get $"C490 MyConstClass"
br_on_non_null $label0
i32.const 107
i32.const 0
block $label1 (result (ref $JSStringImpl))
global.get $"C489 \"shared-const\""
br_on_non_null $label1
i32.const 4
i32.const 0
global.get $S.shared-const
struct.new $JSStringImpl
local.tee $var1
global.set $"C489 \"shared-const\""
local.get $var1
end $label1
struct.new $MyConstClass
local.tee $var2
global.set $"C490 MyConstClass"
local.get $var2
end $label0
end
)
)
@@ -6,6 +6,8 @@
(type $Array<_Type> <...>)
(type $JSStringImpl <...>)
(type $type20 <...>)
(type $_TopType <...>)
(type $Array<_NamedParameter> <...>)
(type $_FunctionType <...>)
(type $#ClosureBase <...>)
(type $#Vtable-0-1 <...>)
@@ -23,8 +25,12 @@
(func $print (import "module0" "func5") (param (ref null $#Top)) (result (ref null $#Top)))
(func $JSStringImpl._interpolate (import "module0" "func6") (param (ref $Array<Object?>)) (result (ref $JSStringImpl)))
(global $"C21 \")\"" (import "module0" "global0") (ref $JSStringImpl))
(global $"C28 _InterfaceType" (import "module0" "global5") (ref $_InterfaceType))
(global $"C455 _FunctionType" (import "module0" "global7") (ref $_FunctionType))
(global $"C1 WasmArray<_Type>[0]" (import "module0" "global1") (ref $Array<_Type>))
(global $"C331 _TopType" (import "module0" "global2") (ref $_TopType))
(global $"C62 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
(global $"C306 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
(global $"C455 _FunctionType" (import "module0" "global5") (ref null $_FunctionType))
(global $"C28 _InterfaceType" (import "module0" "global8") (ref $_InterfaceType))
(global $S.globalH1Bar< (import "S" "globalH1Bar<") (ref extern))
(global $global7 (ref $#Vtable-1-1) <...>)
(global $global4 (ref $#DummyStruct) <...>)
@@ -97,8 +103,9 @@
(func $instantiation constant trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $"C462 H1 (lazy initializer)}" (result (ref $H1))
(local $var0 (ref $#Closure-1-1))
(local $var1 (ref $#Closure-0-1))
(local $var2 (ref $H1))
(local $var1 (ref $_FunctionType))
(local $var2 (ref $#Closure-0-1))
(local $var3 (ref $H1))
i32.const 105
i32.const 0
block $label0 (result (ref $#Closure-0-1))
@@ -125,15 +132,32 @@
ref.func $"#dummy function (ref struct) -> (ref null #Top)"
ref.func $"instantiation constant trampoline"
struct.new $#Vtable-0-1
global.get $"C455 _FunctionType"
block $label2 (result (ref $_FunctionType))
global.get $"C455 _FunctionType"
br_on_non_null $label2
i32.const 12
i32.const 0
i32.const 0
i64.const 0
global.get $"C1 WasmArray<_Type>[0]"
global.get $"C1 WasmArray<_Type>[0]"
global.get $"C331 _TopType"
global.get $"C62 WasmArray<_Type>[1]"
i64.const 1
global.get $"C306 WasmArray<_NamedParameter>[0]"
struct.new $_FunctionType
local.tee $var1
global.set $"C455 _FunctionType"
local.get $var1
end $label2
struct.new $#Closure-0-1
local.tee $var1
local.tee $var2
global.set $"C461 InstantiationConstant(globalH1Foo<int>)"
local.get $var1
local.get $var2
end $label0
struct.new $H1
local.tee $var2
local.tee $var3
global.set $"C462 H1"
local.get $var2
local.get $var3
)
)
@@ -4,6 +4,7 @@
(type $Array<Object?> <...>)
(type $JSStringImpl <...>)
(type $Array<_Type> <...>)
(type $Array<_NamedParameter> <...>)
(type $_FunctionType <...>)
(type $#ClosureBase <...>)
(type $#Vtable-0-1 <...>)
@@ -13,8 +14,13 @@
(field $field1 (mut i32))
(field $fun (ref $#Closure-0-1)))))
(type $#DummyStruct <...>)
(type $_TopType <...>)
(func $print (import "module0" "func5") (param (ref null $#Top)) (result (ref null $#Top)))
(global $"C455 _FunctionType" (import "module0" "global7") (ref $_FunctionType))
(global $"C1 WasmArray<_Type>[0]" (import "module0" "global1") (ref $Array<_Type>))
(global $"C331 _TopType" (import "module0" "global2") (ref $_TopType))
(global $"C62 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
(global $"C306 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
(global $"C455 _FunctionType" (import "module0" "global5") (ref null $_FunctionType))
(global $S.globalH0Foo (import "S" "globalH0Foo") (ref extern))
(global $global6 (ref $#Vtable-0-1) <...>)
(global $global3 (ref $#DummyStruct) <...>)
@@ -40,8 +46,9 @@
call $print
)
(func $"C466 H0 (lazy initializer)}" (result (ref $H0))
(local $var0 (ref $#Closure-0-1))
(local $var1 (ref $H0))
(local $var0 (ref $_FunctionType))
(local $var1 (ref $#Closure-0-1))
(local $var2 (ref $H0))
i32.const 106
i32.const 0
block $label0 (result (ref $#Closure-0-1))
@@ -51,15 +58,32 @@
i32.const 0
global.get $global3
global.get $global6
global.get $"C455 _FunctionType"
block $label1 (result (ref $_FunctionType))
global.get $"C455 _FunctionType"
br_on_non_null $label1
i32.const 12
i32.const 0
i32.const 0
i64.const 0
global.get $"C1 WasmArray<_Type>[0]"
global.get $"C1 WasmArray<_Type>[0]"
global.get $"C331 _TopType"
global.get $"C62 WasmArray<_Type>[1]"
i64.const 1
global.get $"C306 WasmArray<_NamedParameter>[0]"
struct.new $_FunctionType
local.tee $var0
global.set $"C455 _FunctionType"
local.get $var0
end $label1
struct.new $#Closure-0-1
local.tee $var0
local.tee $var1
global.set $"C465 globalH0Foo tear-off"
local.get $var0
local.get $var1
end $label0
struct.new $H0
local.tee $var1
local.tee $var2
global.set $"C466 H0"
local.get $var1
local.get $var2
)
)