[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>
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index 257fbe3..f3b898a 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -26,13 +26,24 @@
 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 @@
     }
 
     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 @@
 
     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 @@
   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 @@
     //
     // 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 @@
     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 @@
       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 @@
           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(
diff --git a/pkg/dart2wasm/lib/globals.dart b/pkg/dart2wasm/lib/globals.dart
index 5f3762e..5c8ae0f 100644
--- a/pkg/dart2wasm/lib/globals.dart
+++ b/pkg/dart2wasm/lib/globals.dart
@@ -72,6 +72,25 @@
     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) {
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.dart
new file mode 100644
index 0000000..b21780f
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.dart
@@ -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';
+  }
+}
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.0.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.0.dart
new file mode 100644
index 0000000..9f5a099
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.0.dart
@@ -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');
+}
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.1.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.1.dart
new file mode 100644
index 0000000..b886989
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.1.dart
@@ -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');
+}
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.2.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.2.dart
new file mode 100644
index 0000000..47f2914
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.h.2.dart
@@ -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);
+}
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.wat
new file mode 100644
index 0000000..36006f2
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use.wat
@@ -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) <...>)
+)
\ No newline at end of file
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat
new file mode 100644
index 0000000..a1102cf
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat
@@ -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
+  )
+)
\ No newline at end of file
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat
new file mode 100644
index 0000000..e7a7378
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat
@@ -0,0 +1,2 @@
+(module $module2
+)
\ No newline at end of file
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat
new file mode 100644
index 0000000..7b5e2cc
--- /dev/null
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat
@@ -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
+  )
+)
\ No newline at end of file
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat
index c63406b..07affdc 100644
--- a/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat
@@ -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
   )
 )
\ No newline at end of file
diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat
index 3326c9e..0743efa 100644
--- a/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat
+++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat
@@ -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
   )
 )
\ No newline at end of file