[dart2wasm] Exclude value types from deferred loading partitioning

This reduces e main module by around -0.4% uncompressed
and -0.5% compressed

Value types don't have identiy, they are compared by value. As such
there's no need to have a unique int/double box for the same value -
each module can have their own box: The box identity cannot observed.

That in return means also we avoid exporting those from main module &
importing into deferred modules.

Change-Id: Ib63949b59263b7381396323210b4218662f56525
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/509321
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
Martin Kustermann
2026-06-05 02:10:35 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent e3cf529f87
commit 56e0c9fca3
19 changed files with 139 additions and 111 deletions
+61 -26
View File
@@ -152,6 +152,7 @@ typedef ConstantCodeGeneratorLazy =
class Constants {
final Translator translator;
final Map<Constant, ConstantInfo> constantInfo = {};
final Map<Constant, Map<w.ModuleBuilder, w.Global>> valueTypeConstants = {};
final Map<w.ModuleBuilder, w.DataSegmentBuilder> _byteSegments = {};
late final ClassInfo typeInfo = translator.classInfo[translator.typeClass]!;
@@ -619,7 +620,32 @@ class ConstantInstantiator extends ConstantVisitor<w.ValueType>
@override
w.ValueType visitIntConstant(IntConstant constant) {
if (expectedType is w.RefType) return defaultConstant(constant);
if (expectedType is w.RefType) {
final moduleMap = constants.valueTypeConstants.putIfAbsent(
constant,
() => {},
);
final perModuleGlobal = moduleMap.putIfAbsent(b.moduleBuilder, () {
final translator = constants.translator;
final info = translator.classInfo[translator.boxedIntClass]!;
final globalType = w.GlobalType(
w.RefType(info.struct, nullable: false),
mutable: false,
);
final definedGlobal = b.moduleBuilder.globals.define(
globalType,
constants._constantAccessor._constantName(constant),
);
definedGlobal.initializer
..i32_const(info.classId)
..i64_const(constant.value)
..struct_new(info.struct)
..end();
return definedGlobal;
});
b.global_get(perModuleGlobal);
return perModuleGlobal.type.type;
}
if (expectedType == w.NumType.i32) {
b.i32_const(constant.value);
return w.NumType.i32;
@@ -630,7 +656,32 @@ class ConstantInstantiator extends ConstantVisitor<w.ValueType>
@override
w.ValueType visitDoubleConstant(DoubleConstant constant) {
if (expectedType is w.RefType) return defaultConstant(constant);
if (expectedType is w.RefType) {
final moduleMap = constants.valueTypeConstants.putIfAbsent(
constant,
() => {},
);
final perModuleGlobal = moduleMap.putIfAbsent(b.moduleBuilder, () {
final translator = constants.translator;
final info = translator.classInfo[translator.boxedDoubleClass]!;
final globalType = w.GlobalType(
w.RefType(info.struct, nullable: false),
mutable: false,
);
final definedGlobal = b.moduleBuilder.globals.define(
globalType,
constants._constantAccessor._constantName(constant),
);
definedGlobal.initializer
..i32_const(info.classId)
..f64_const(constant.value)
..struct_new(info.struct)
..end();
return definedGlobal;
});
b.global_get(perModuleGlobal);
return perModuleGlobal.type.type;
}
b.f64_const(constant.value);
return w.NumType.f64;
}
@@ -677,6 +728,12 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
.constant;
ConstantInfo? ensureConstant(Constant constant) {
if (constant is IntConstant || constant is DoubleConstant) {
// Value types do not have identity and as such we don't need a unique box
// for them. They are lazily created at instantiation time.
return null;
}
// To properly canonicalize type literal constants, we normalize the
// type before canonicalization.
if (constant is TypeLiteralConstant) {
@@ -740,34 +797,12 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
@override
ConstantInfo? visitIntConstant(IntConstant constant) {
ClassInfo info = translator.classInfo[translator.boxedIntClass]!;
return createConstant(
constant,
const [],
info.nonNullableType,
canBeEager: true,
(_, b, _) {
b.i32_const(info.classId);
b.i64_const(constant.value);
b.struct_new(info.struct);
},
);
throw StateError('Int constants should be handled at instantiation');
}
@override
ConstantInfo? visitDoubleConstant(DoubleConstant constant) {
ClassInfo info = translator.classInfo[translator.boxedDoubleClass]!;
return createConstant(
constant,
const [],
info.nonNullableType,
canBeEager: true,
(_, b, _) {
b.i32_const(info.classId);
b.f64_const(constant.value);
b.struct_new(info.struct);
},
);
throw StateError('Double constants should be handled at instantiation');
}
@override
@@ -187,6 +187,10 @@ class _ConstantDependenciesCollector extends RecursiveVisitor {
@override
void defaultConstantReference(Constant node) {
if (node is IntConstant || node is DoubleConstant) {
// Integers and doubles don't have identity, they are compared by value.
return;
}
_directChildren.add(node);
}
}
@@ -619,12 +623,12 @@ class _ReferenceDependenciesCollector extends RecursiveVisitor {
@override
void visitIntLiteral(IntLiteral node) {
addConstant(IntConstant(node.value));
// Integers don't have identity, they are compared by value.
}
@override
void visitDoubleLiteral(DoubleLiteral node) {
addConstant(DoubleConstant(node.value));
// Doubles don't have identity, they are compared by value.
}
// The references needed by the codegen to handle
@@ -634,7 +638,12 @@ class _ReferenceDependenciesCollector extends RecursiveVisitor {
@override
void visitConstantExpression(ConstantExpression node) {
addConstant(node.constant);
final constant = node.constant;
if (constant is IntConstant || constant is DoubleConstant) {
// Integers and doubles don't have identity, they are compared by value.
return;
}
addConstant(constant);
}
void addReference(Reference used) {
@@ -55,10 +55,6 @@ Part 0
- pkg/compiler/test/custom_split/data/fuse_with_and/lib_010_0.dart::@methods::g_010_0
- pkg/compiler/test/custom_split/data/fuse_with_and/lib_100_0.dart::@methods::g_100_0
Constants
- IntConstant(20)
- IntConstant(256)
- IntConstant(36)
- IntConstant(92)
- StaticTearOffConstant(ExpectException._kEmptyString)
- StringConstant("(expected: <")
- StringConstant(") fails.")
@@ -39,10 +39,6 @@ Part 0
- pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart::@methods::f_111_1
- pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart::@methods::v
Constants
- IntConstant(20)
- IntConstant(256)
- IntConstant(36)
- IntConstant(92)
- StaticTearOffConstant(ExpectException._kEmptyString)
- StringConstant("(expected: <")
- StringConstant(") fails.")
@@ -49,10 +49,6 @@ Part 0
- pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart::@methods::v
- pkg/compiler/test/custom_split/data/fuse_with_or/lib_001_0.dart::@methods::g_001_0
Constants
- IntConstant(20)
- IntConstant(256)
- IntConstant(36)
- IntConstant(92)
- StaticTearOffConstant(ExpectException._kEmptyString)
- StringConstant("(expected: <")
- StringConstant(") fails.")
@@ -39,10 +39,6 @@ Part 0
- pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart::@methods::f_111_1
- pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart::@methods::v
Constants
- IntConstant(20)
- IntConstant(256)
- IntConstant(36)
- IntConstant(92)
- StaticTearOffConstant(ExpectException._kEmptyString)
- StringConstant("(expected: <")
- StringConstant(") fails.")
@@ -41,10 +41,6 @@ Part 0
- pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::f_111_1
- pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::v
Constants
- IntConstant(20)
- IntConstant(256)
- IntConstant(36)
- IntConstant(92)
- StaticTearOffConstant(ExpectException._kEmptyString)
- StringConstant("(expected: <")
- StringConstant(") fails.")
@@ -39,10 +39,6 @@ Part 0
- pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::f_111_1
- pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::v
Constants
- IntConstant(20)
- IntConstant(256)
- IntConstant(36)
- IntConstant(92)
- StaticTearOffConstant(ExpectException._kEmptyString)
- StringConstant("(expected: <")
- StringConstant(") fails.")
@@ -13,6 +13,7 @@
(field $field0 i32)
(field $field1 (mut i32)))))
(global $.h1-nonshared-const (import "" "h1-nonshared-const") (ref extern))
(global $.shared-const (import "" "shared-const") (ref extern))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 17 funcref)
(global $"\"bad\"" (ref $JSExternWrapper) <...>)
(global $MyConstClass (ref $MyConstClass)
@@ -23,9 +24,18 @@
(global.get $.h1-nonshared-const)
(struct.new $JSExternWrapper)
(struct.new $MyConstClass))
(global $MyConstClass_14 (ref $MyConstClass)
(i32.const 109)
(i32.const 0)
(i32.const 60)
(i32.const 0)
(global.get $.shared-const)
(struct.new $JSExternWrapper)
(struct.new $MyConstClass))
(elem $module0.cross-module-funcs-0
(set 0 (ref.func $int.parse))
(set 1 (ref.func $"mainImpl <noInline>")))
(set 1 (ref.func $"mainImpl <noInline>"))
(set 16 (ref.func $0)))
(func $"mainImpl <noInline>" (param $var0 i32)
i64.const 0
i32.const 2
@@ -50,13 +60,11 @@
end
)
(func $"modH1Use <noInline>" (param $var0 i32) (result (ref $MyConstClass))
global.get $MyConstClass
global.get $MyConstClass_14
local.get $var0
if (result (ref $MyConstClass))
global.get $MyConstClass
else
i32.const 16
call_indirect $module0.cross-module-funcs-0 (result (ref $MyConstClass))
end
select (ref $MyConstClass)
)
(func $null (result (ref $MyConstClass)) <...>)
(func $int.parse (result i64) <...>)
)
@@ -7,9 +7,7 @@
(global $".Foo1.doitDevirt(" (import "" "Foo1.doitDevirt(") (ref extern))
(global $".Foo1.doitDispatch(" (import "" "Foo1.doitDispatch(") (ref extern))
(global $".FooBase(" (import "" "FooBase(") (ref extern))
(global $"\")\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $1 (import "module0" "global2") (ref $BoxedInt))
(global $2 (import "module0" "global3") (ref $BoxedInt))
(global $"\")\"" (import "module0" "global2") (ref $JSExternWrapper))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 18 funcref)
(table $module0.dispatch0 (import "module0" "dispatch0") 657 funcref)
(global $"\"Foo0.doitDispatch(\"" (ref $JSExternWrapper)
@@ -32,6 +30,8 @@
(i32.const 0)
(global.get $".FooBase(")
(struct.new $JSExternWrapper))
(global $1 (ref $BoxedInt) <...>)
(global $2 (ref $BoxedInt) <...>)
(global $baseObj (mut (ref null $Object)) <...>)
(global $foo1Obj (mut (ref null $Object)) <...>)
(elem $module0.cross-module-funcs-0
@@ -3,10 +3,10 @@
(type $BoxedInt <...>)
(type $JSExternWrapper <...>)
(type $Object <...>)
(global $"\")\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $1 (import "module0" "global8") (ref $BoxedInt))
(global $"\")\"" (import "module0" "global3") (ref $JSExternWrapper))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref)
(global $"\"foo1Code(\"" (ref $JSExternWrapper) <...>)
(global $1 (ref $BoxedInt) <...>)
(global $FooConst1 (ref $Object)
(i32.const 110)
(i32.const 0)
@@ -15,12 +15,10 @@
(global $".FooConst4(" (import "" "FooConst4(") (ref extern))
(global $".FooConst5(" (import "" "FooConst5(") (ref extern))
(global $".FooConstBase(" (import "" "FooConstBase(") (ref extern))
(global $"\")\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $"\"[]\"" (import "module0" "global6") (ref $JSExternWrapper))
(global $1 (import "module0" "global8") (ref $BoxedInt))
(global $5 (import "module0" "global5") (ref $BoxedInt))
(global $FooConst0 (import "module0" "global7") (ref $Object))
(global $fooGlobal0 (import "module0" "global16") (ref null $#Top))
(global $"\")\"" (import "module0" "global3") (ref $JSExternWrapper))
(global $"\"[]\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $FooConst0 (import "module0" "global5") (ref $Object))
(global $fooGlobal0 (import "module0" "global12") (ref null $#Top))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref)
(table $module0.dispatch0 (import "module0" "dispatch0") 673 funcref)
(global $"\"0\"" (ref $JSExternWrapper) <...>)
@@ -64,6 +62,8 @@
(global.get $".FooConstBase(")
(struct.new $JSExternWrapper))
(global $"\"foo5Code(\"" (ref $JSExternWrapper) <...>)
(global $1 (ref $BoxedInt) <...>)
(global $5 (ref $BoxedInt) <...>)
(global $FooConst5 (ref $Object)
(i32.const 114)
(i32.const 0)
@@ -3,10 +3,10 @@
(type $BoxedInt <...>)
(type $JSExternWrapper <...>)
(type $Object <...>)
(global $"\")\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $2 (import "module0" "global19") (ref $BoxedInt))
(global $"\")\"" (import "module0" "global3") (ref $JSExternWrapper))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref)
(global $"\"foo2Code(\"" (ref $JSExternWrapper) <...>)
(global $2 (ref $BoxedInt) <...>)
(global $FooConst2 (ref $Object)
(i32.const 111)
(i32.const 0)
@@ -3,10 +3,10 @@
(type $BoxedInt <...>)
(type $JSExternWrapper <...>)
(type $Object <...>)
(global $"\")\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $3 (import "module0" "global18") (ref $BoxedInt))
(global $"\")\"" (import "module0" "global3") (ref $JSExternWrapper))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref)
(global $"\"foo3Code(\"" (ref $JSExternWrapper) <...>)
(global $3 (ref $BoxedInt) <...>)
(global $FooConst3 (ref $Object)
(i32.const 112)
(i32.const 0)
@@ -3,10 +3,10 @@
(type $BoxedInt <...>)
(type $JSExternWrapper <...>)
(type $Object <...>)
(global $"\")\"" (import "module0" "global4") (ref $JSExternWrapper))
(global $4 (import "module0" "global17") (ref $BoxedInt))
(global $"\")\"" (import "module0" "global3") (ref $JSExternWrapper))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref)
(global $"\"foo4Code(\"" (ref $JSExternWrapper) <...>)
(global $4 (ref $BoxedInt) <...>)
(global $FooConst4 (ref $Object)
(i32.const 113)
(i32.const 0)
@@ -7,13 +7,13 @@
(type $_Environment <...>)
(type $_InterfaceType <...>)
(type $_Type <...>)
(global $"\")\"_11" (import "$" "2") (ref $JSExternWrapper))
(global $_InterfaceType (import "$" "0") (ref $_InterfaceType))
(global $"\")\"_9" (import "$" "0") (ref $JSExternWrapper))
(global $_InterfaceType (import "$" ".") (ref $_InterfaceType))
(table $$.% (import "$" "%") 765 funcref)
(table $$.' (import "$" "'") 20 funcref)
(table $$.& (import "$" "&") 20 funcref)
(global $"\">.takeT(\"" (ref $JSExternWrapper) <...>)
(global $"\"Foo<\"" (ref $JSExternWrapper) <...>)
(elem $$.' <...>)
(elem $$.& <...>)
(elem $$.% <...>)
(func $"Foo.takeT (body) <noInline>" (param $var0 (ref $Foo)) (param $var1 (ref $#Top))
(local $var2 (ref $_InterfaceType))
@@ -22,12 +22,12 @@
struct.get $Foo $field2
global.get $"\">.takeT(\""
local.get $var1
global.get $"\")\"_11"
global.get $"\")\"_9"
array.new_fixed $Array<Object?> 5
i32.const 14
call_indirect $$.' (param (ref $Array<Object?>)) (result (ref $JSExternWrapper))
call_indirect $$.& (param (ref $Array<Object?>)) (result (ref $JSExternWrapper))
i32.const 18
call_indirect $$.' (param (ref null $#Top))
call_indirect $$.& (param (ref null $#Top))
global.get $_InterfaceType
local.set $var2
block $label0 (result i32)
@@ -51,7 +51,7 @@
local.get $var2
ref.null none
i32.const 19
call_indirect $$.' (param (ref $_Type) (ref null $_Environment) (ref $_Type) (ref null $_Environment)) (result i32)
call_indirect $$.& (param (ref $_Type) (ref null $_Environment) (ref $_Type) (ref null $_Environment)) (result i32)
i32.const 1
i32.ne
br_if $label0
@@ -61,12 +61,12 @@
i32.eqz
if
i32.const 2
call_indirect $$.'
call_indirect $$.&
unreachable
end
local.get $var0
i32.const 18
call_indirect $$.' (param (ref null $#Top))
call_indirect $$.& (param (ref null $#Top))
)
(func $"Foo.takeT (checked entry)" (param $var0 (ref $Foo)) (param $var1 (ref $#Top))
(local $var2 i32)
@@ -109,7 +109,7 @@
ref.as_non_null
local.get $var1
i32.const 4
call_indirect $$.' (param (ref $_Type) (ref $#Top)) (result i32)
call_indirect $$.& (param (ref $_Type) (ref $#Top)) (result i32)
br $label0
end
br $label1
@@ -126,7 +126,7 @@
ref.as_non_null
local.get $var1
i32.const 5
call_indirect $$.' (param (ref $_Type) (ref $#Top)) (result i32)
call_indirect $$.& (param (ref $_Type) (ref $#Top)) (result i32)
br $label0
end
br $label1
@@ -139,7 +139,7 @@
ref.as_non_null
local.get $var1
i32.const 6
call_indirect $$.' (param (ref $_Type) (ref $#Top)) (result i32)
call_indirect $$.& (param (ref $_Type) (ref $#Top)) (result i32)
br $label0
end
end $label1
@@ -156,7 +156,7 @@
i32.eqz
if
i32.const 2
call_indirect $$.'
call_indirect $$.&
unreachable
end
local.get $var0
+6 -6
View File
@@ -32,8 +32,8 @@
(i32.const 0)
(global.get $"WasmArray<_Type>[0]")
(global.get $"WasmArray<_Type>[0]")
(global.get $_TopType_290)
(global.get $_InterfaceType_29)
(global.get $_TopType_288)
(global.get $_InterfaceType_28)
(i32.const 9)
(i32.const 0)
(i32.const 1)
@@ -57,8 +57,8 @@
(i32.const 0)
(global.get $"WasmArray<_Type>[0]")
(global.get $"WasmArray<_Type>[0]")
(global.get $_TopType_290)
(global.get $_InterfaceType_29)
(global.get $_TopType_288)
(global.get $_InterfaceType_28)
(i32.const 9)
(i32.const 0)
(i32.const 1)
@@ -70,8 +70,8 @@
(global.get $"WasmArray<_NamedParameter>[0]")
(struct.new $_FunctionType)
(struct.new $#Closure-0-2))
(global $_InterfaceType_29 (ref $_InterfaceType) <...>)
(global $_TopType_290 (ref $_TopType) <...>)
(global $_InterfaceType_28 (ref $_InterfaceType) <...>)
(global $_TopType_288 (ref $_TopType) <...>)
(global $global0 (ref $"dummy struct") <...>)
(func $bar tear-off trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_116 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
@@ -32,8 +32,8 @@
(i32.const 0)
(global.get $"WasmArray<_Type>[0]")
(global.get $"WasmArray<_Type>[0]")
(global.get $_TopType_290)
(global.get $_InterfaceType_29)
(global.get $_TopType_288)
(global.get $_InterfaceType_28)
(i32.const 9)
(i32.const 0)
(i32.const 1)
@@ -57,8 +57,8 @@
(i32.const 0)
(global.get $"WasmArray<_Type>[0]")
(global.get $"WasmArray<_Type>[0]")
(global.get $_TopType_290)
(global.get $_InterfaceType_29)
(global.get $_TopType_288)
(global.get $_InterfaceType_28)
(i32.const 9)
(i32.const 0)
(i32.const 1)
@@ -70,8 +70,8 @@
(global.get $"WasmArray<_NamedParameter>[0]")
(struct.new $_FunctionType)
(struct.new $#Closure-0-2))
(global $_InterfaceType_29 (ref $_InterfaceType) <...>)
(global $_TopType_290 (ref $_TopType) <...>)
(global $_InterfaceType_28 (ref $_InterfaceType) <...>)
(global $_TopType_288 (ref $_TopType) <...>)
(global $global0 (ref $"dummy struct") <...>)
(func $bar tear-off trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_119 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)
@@ -36,9 +36,9 @@
(i32.const 0)
(global.get $"WasmArray<_Type>[0]")
(global.get $"WasmArray<_Type>[0]")
(global.get $_TopType_290)
(global.get $_InterfaceType_29)
(global.get $_InterfaceType_296)
(global.get $_TopType_288)
(global.get $_InterfaceType_28)
(global.get $_InterfaceType_295)
(array.new_fixed $Array<_Type> 2)
(i64.const 1)
(global.get $"WasmArray<_NamedParameter>[0]")
@@ -56,8 +56,8 @@
(i32.const 0)
(global.get $"WasmArray<_Type>[0]")
(global.get $"WasmArray<_Type>[0]")
(global.get $_TopType_290)
(global.get $_InterfaceType_29)
(global.get $_TopType_288)
(global.get $_InterfaceType_28)
(i32.const 9)
(i32.const 0)
(i32.const 1)
@@ -69,9 +69,9 @@
(global.get $"WasmArray<_NamedParameter>[0]")
(struct.new $_FunctionType)
(struct.new $#Closure-0-2))
(global $_InterfaceType_29 (ref $_InterfaceType) <...>)
(global $_InterfaceType_296 (ref $_InterfaceType) <...>)
(global $_TopType_290 (ref $_TopType) <...>)
(global $_InterfaceType_28 (ref $_InterfaceType) <...>)
(global $_InterfaceType_295 (ref $_InterfaceType) <...>)
(global $_TopType_288 (ref $_TopType) <...>)
(global $global0 (ref $"dummy struct") <...>)
(func $bar tear-off dynamic call entry (param $var0 (ref $#Closure-0-0)) (param $var1 (ref $Array<_Type>)) (param $var2 (ref $Array<Object?>)) (param $var3 (ref $Array<Object?>)) (result (ref null $#Top)) <...>)
(func $bar tear-off trampoline_122 (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) <...>)