From 9ac6dac154b0545bdd1de0df4969b7448154b2cd Mon Sep 17 00:00:00 2001 From: David Mak Date: Fri, 13 Mar 2026 19:22:06 +0800 Subject: [PATCH 01/16] wasm: Deprecate `NumIdx` for external use --- hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala index 155e881def..b90797bfcc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala @@ -158,6 +158,9 @@ type HeapType = AbsHeapType | TypeIdx sealed abstract class Index extends ToWat /** A numeric index. */ +@deprecated( + "NumIdx is only used for internal bookkeeping and should not be used in WAT generation; Use SymIdx instead.", +) case class NumIdx(val index: Int) extends Index: def toWat: Document = doc"${index.toString}" From 89f16fc3c8edc9e4010b6b4ded969c89f9ecac5c Mon Sep 17 00:00:00 2001 From: David Mak Date: Fri, 13 Mar 2026 19:43:09 +0800 Subject: [PATCH 02/16] wasm: Store struct fields as `Seq` and require `SymIdx` for fields --- .../scala/hkmc2/codegen/wasm/text/Wasm.scala | 10 +++++----- .../hkmc2/codegen/wasm/text/WatBuilder.scala | 18 ++++++++--------- .../shared/src/test/mlscript/wasm/Basics.mls | 20 +++++++++---------- .../src/test/mlscript/wasm/Matching.mls | 8 ++++---- .../src/test/mlscript/wasm/ScopedLocals.mls | 6 +++--- .../src/test/mlscript/wasm/Singletons.mls | 6 +++--- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala index b90797bfcc..cc291d85b7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala @@ -113,23 +113,23 @@ case class FunctionType(sigType: SignatureType) extends ToWat: doc"(func${sigType.toWat.surroundUnlessEmpty(doc" ")})" /** A type representing a struct field. */ -case class Field(ty: Type, mutable: Bool, id: Opt[Str]) extends ToWat: +case class Field(ty: Type, mutable: Bool, id: SymIdx) extends ToWat: def toWat: Document = - doc"(field ${id.fold(doc"")(id => doc"$$$id ")}${ + doc"(field ${id.toWat} ${ if mutable then doc"(mut ${ty.toWat})" else ty.toWat })" /** A type representing a structure type. */ case class StructType( - fields: Map[DefinitionSymbol[?], NumIdx -> Field], + fields: Seq[DefinitionSymbol[?] -> Field], parents: Seq[TypeIdx] = Seq.empty, isSubtype: Bool = false, ) extends ToWat: - def fieldSeq: Seq[Field] = fields.values.toSeq.sortBy(_._1.index).map(_._2) + lazy val fieldsBySym: Map[DefinitionSymbol[?], Field] = fields.toMap def toWat: Document = - doc"(struct${fieldSeq.map(_.toWat).mkDocument(doc" ").surroundUnlessEmpty(doc" ")})" + doc"(struct${fields.map(_._2.toWat).mkDocument(doc" ").surroundUnlessEmpty(doc" ")})" /** A type representing an array type. */ case class ArrayType(elemType: Type, mutable: Bool) extends ToWat: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 8465d5e6b3..85a13283c2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -147,16 +147,14 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: private def createDefnTypes(b: Block)(using Ctx): Unit = b match case Define(defn: ClsLikeDefn, rst) => if isSupportedTopLevelClass(defn) then - val inheritedFields = baseObjectStruct.fields.toMap + val inheritedFields = baseObjectStruct.fields val inheritedSize = inheritedFields.size - val classFields: Map[DefinitionSymbol[?], NumIdx -> Field] = (defn.publicFields.map(_._2) ++ defn.privateFields) - .zipWithIndex - .map: (f, index) => - f -> (NumIdx(index + inheritedSize) -> Field(RefType.anyref, mutable = true, id = S(f.nme))) - .toMap + val classFields = (defn.publicFields.map(_._2) ++ defn.privateFields) + .map: f => + f -> Field(RefType.anyref, mutable = true, id = SymIdx(f.nme)) - val allFields: Map[DefinitionSymbol[?], NumIdx -> Field] = inheritedFields ++ classFields + val allFields = inheritedFields ++ classFields // Only parent is base Object for now. For general inheritance add other parents. ctx.addType( @@ -495,7 +493,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: )(using Ctx, Raise): FieldIdx = val structInfo = ctx.getTypeInfo_!(thisSym) val symToField = structInfo.compType match - case ty: StructType => ty.fields + case ty: StructType => ty.fieldsBySym case _ => lastWords(s"Cannot select field from non-struct type: ${structInfo.compType.toWat}") val fieldIdx = symToField.get(sym) .orElse: @@ -504,7 +502,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: case trmSym: TermSymbol if trmSym.owner.flatMap(_.asBlkMember).exists(_ == thisSym) => symToField.find((fieldSym, _) => fieldSym.nme == sym.nme).map((_, v) => v) case _ => N - .map((fieldidx, _) => fieldidx) + .map(_.id) FieldIdx( fieldIdx getOrElse: lastWords( @@ -1489,7 +1487,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: TypeInfo( id = S(SymIdx("Object")), StructType( - Map(tagFieldSym -> (NumIdx(0) -> Field(I32Type, mutable = true, id = S("$tag")))), + Seq(tagFieldSym -> Field(I32Type, mutable = true, id = SymIdx("$tag"))), isSubtype = true, ), ), diff --git a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls index 5d9f5691aa..b3c1e55e92 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls @@ -130,7 +130,7 @@ class Foo(val a) //│ (local.get $this)) //│ (i32.const 1)) //│ (block -//│ (struct.set $Foo 1 +//│ (struct.set $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $a)) @@ -149,7 +149,7 @@ class Foo(val a) //│ (call $Foo //│ (ref.i31 //│ (i32.const 42)))) -//│ (struct.get $Foo 1 +//│ (struct.get $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $tmp)))))) //│ (export "entry" (func $entry)) @@ -184,15 +184,15 @@ class Foo(val x) with //│ (local.get $this)) //│ (i32.const 1)) //│ (block -//│ (struct.set $Foo 1 +//│ (struct.set $Foo $x //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $x)) //│ (block -//│ (struct.set $Foo 2 +//│ (struct.set $Foo $y //│ (ref.cast (ref $Foo) //│ (local.get $this)) -//│ (struct.get $Foo 1 +//│ (struct.get $Foo $x //│ (ref.cast (ref $Foo) //│ (local.get $this)))) //│ (nop))) @@ -210,7 +210,7 @@ class Foo(val x) with //│ (call $Foo //│ (ref.i31 //│ (i32.const 42)))) -//│ (struct.get $Foo 2 +//│ (struct.get $Foo $y //│ (ref.cast (ref $Foo) //│ (local.get $tmp)))))) //│ (export "entry" (func $entry)) @@ -243,16 +243,16 @@ O.y //│ (local.get $this)) //│ (i32.const 1)) //│ (block -//│ (struct.set $O 1 +//│ (struct.set $O $x //│ (ref.cast (ref $O) //│ (local.get $this)) //│ (ref.i31 //│ (i32.const 1))) //│ (block -//│ (struct.set $O 2 +//│ (struct.set $O $y //│ (ref.cast (ref $O) //│ (local.get $this)) -//│ (struct.get $O 1 +//│ (struct.get $O $x //│ (ref.cast (ref $O) //│ (local.get $this)))) //│ (nop))) @@ -267,7 +267,7 @@ O.y //│ (local $O (ref null any)) //│ (block (result (ref null any)) //│ (nop) -//│ (struct.get $O 2 +//│ (struct.get $O $y //│ (ref.cast (ref $O) //│ (global.get $O$inst))))) //│ (export "entry" (func $entry)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls index 0b9d3acc9e..5d96862b9b 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls @@ -59,7 +59,7 @@ if Bar(true) is //│ (local.get $this)) //│ (i32.const 1)) //│ (block -//│ (struct.set $Bar 1 +//│ (struct.set $Bar $y //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (local.get $y)) @@ -78,7 +78,7 @@ if Bar(true) is //│ (local.get $this)) //│ (i32.const 2)) //│ (block -//│ (struct.set $Baz 1 +//│ (struct.set $Baz $z //│ (ref.cast (ref $Baz) //│ (local.get $this)) //│ (local.get $z)) @@ -126,7 +126,7 @@ if Bar(true) is //│ (local.set $matchRes //│ (block (result (ref null any)) //│ (local.set $arg$Bar$0$ -//│ (struct.get $Bar 1 +//│ (struct.get $Bar $y //│ (ref.cast (ref $Bar) //│ (local.get $scrut)))) //│ (block (result (ref null any)) @@ -168,7 +168,7 @@ if Bar(true) is //│ (local.set $matchRes //│ (block (result (ref null any)) //│ (local.set $arg$Baz$0$ -//│ (struct.get $Baz 1 +//│ (struct.get $Baz $z //│ (ref.cast (ref $Baz) //│ (local.get $scrut)))) //│ (block (result (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls index 775d0a2e3c..5b82a49b94 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls @@ -181,12 +181,12 @@ class Foo(val a, val b) //│ (local.get $this)) //│ (i32.const 1)) //│ (block -//│ (struct.set $Foo 1 +//│ (struct.set $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $a)) //│ (block -//│ (struct.set $Foo 2 +//│ (struct.set $Foo $b //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $b)) @@ -207,7 +207,7 @@ class Foo(val a, val b) //│ (i32.const 42)) //│ (ref.i31 //│ (i32.const 1)))) -//│ (struct.get $Foo 1 +//│ (struct.get $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $tmp)))))) //│ (export "entry" (func $entry)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls index dc31b18afb..ec60b20877 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls @@ -31,7 +31,7 @@ Bar.y //│ (local.get $this)) //│ (i32.const 1)) //│ (block -//│ (struct.set $Foo 1 +//│ (struct.set $Foo $x //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (ref.i31 @@ -49,7 +49,7 @@ Bar.y //│ (local.get $this)) //│ (i32.const 2)) //│ (block -//│ (struct.set $Bar 1 +//│ (struct.set $Bar $y //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (ref.i31 @@ -72,7 +72,7 @@ Bar.y //│ (nop) //│ (block (result (ref null any)) //│ (nop) -//│ (struct.get $Bar 1 +//│ (struct.get $Bar $y //│ (ref.cast (ref $Bar) //│ (global.get $Bar$inst)))))) //│ (export "entry" (func $entry)) From c19ac6a862290f983fc3e2b75e38337e83cded55 Mon Sep 17 00:00:00 2001 From: David Mak Date: Fri, 13 Mar 2026 20:00:43 +0800 Subject: [PATCH 03/16] wasm: Store numeric indices as integers --- .../scala/hkmc2/codegen/wasm/text/Ctx.scala | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index b94858d368..16ed6def66 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -241,17 +241,17 @@ end Ctx */ class Ctx( types: ArrayBuf[TypeInfo], - namedTypes: MutMap[BlockMemberSymbol, NumIdx], + namedTypes: MutMap[BlockMemberSymbol, Int], memoryImports: ArrayBuf[MemoryImport], functionImports: ArrayBuf[FuncImport], dataSegments: ArrayBuf[DataSegment], funcs: ArrayBuf[FuncInfo], - funcInfosByIndex: MutMap[NumIdx, FuncInfo], + funcInfosByIndex: MutMap[Int, FuncInfo], globals: ArrayBuf[GlobalInfo], - namedFuncs: MutMap[Symbol, NumIdx], + namedFuncs: MutMap[Symbol, Int], tags: ArrayBuf[TagInfo], - namedGlobals: MutMap[Symbol, NumIdx], - var locals: Ls[MutMap[Local, NumIdx]], + namedGlobals: MutMap[Symbol, Int], + var locals: Ls[MutMap[Local, Int]], private var startFunc: Opt[FuncIdx], ) extends ToWat: @@ -283,20 +283,20 @@ class Ctx( /** Adds a type into this context. */ def addType(sym: Opt[BlockMemberSymbol], typeInfo: TypeInfo): TypeIdx = - val numIdx = NumIdx(types.size) + val numIdx = types.size types += typeInfo sym.foreach: namedTypes(_) = numIdx - TypeIdx(typeInfo.id.getOrElse(numIdx)) + TypeIdx(typeInfo.id.getOrElse(NumIdx(numIdx))) /** Returns the [[TypeIdx]] of the given `typeref`, optionally resolving the symbolic index into a numeric index. */ def getType(typeref: TypeIdx | BlockMemberSymbol, resolveSymIdx: Bool = false): Opt[TypeIdx] = typeref match case TypeIdx(SymIdx(nme)) if resolveSymIdx => - namedTypes.find(_._1.nme == nme).map(t => TypeIdx(t._2)) + namedTypes.find(_._1.nme == nme).map(t => TypeIdx(NumIdx(t._2))) case typeidx: TypeIdx => S(typeidx) - case sym: BlockMemberSymbol if resolveSymIdx => namedTypes.get(sym).map(TypeIdx(_)) + case sym: BlockMemberSymbol if resolveSymIdx => namedTypes.get(sym).map(idx => TypeIdx(NumIdx(idx))) case sym: BlockMemberSymbol => getType(sym, resolveSymIdx = true).map: numIdx => getTypeInfo(numIdx).flatMap(_.id).fold(numIdx)(TypeIdx(_)) @@ -310,8 +310,8 @@ class Ctx( def getTypeInfo(typeref: TypeIdx | BlockMemberSymbol): Opt[TypeInfo] = typeref match case TypeIdx(NumIdx(idx)) => types.unapply(idx.toInt) case TypeIdx(SymIdx(nme)) => - namedTypes.find(_._1.nme == nme).flatMap(t => getTypeInfo(TypeIdx(t._2))) - case sym: BlockMemberSymbol => namedTypes.get(sym).flatMap(idx => getTypeInfo(TypeIdx(idx))) + namedTypes.find(_._1.nme == nme).flatMap(t => getTypeInfo(TypeIdx(NumIdx(t._2)))) + case sym: BlockMemberSymbol => namedTypes.get(sym).flatMap(idx => getTypeInfo(TypeIdx(NumIdx(idx)))) /** Same as [[getTypeInfo]] but throws an exception when the `typeref` is not found. */ def getTypeInfo_!(typeref: TypeIdx | BlockMemberSymbol): TypeInfo = @@ -320,23 +320,23 @@ class Ctx( /** Adds a function into this context. */ def addFunc(sym: Opt[Symbol], funcInfo: FuncInfo): FuncIdx = - val numIdx = NumIdx(functionImports.size + funcs.size) + val numIdx = functionImports.size + funcs.size funcs += funcInfo funcInfosByIndex(numIdx) = funcInfo sym.foreach: namedFuncs(_) = numIdx - FuncIdx(funcInfo.id.getOrElse(numIdx)) + FuncIdx(funcInfo.id.getOrElse(NumIdx(numIdx))) /** Adds a function import into this context. * * Returns the function index in the global function index space. */ def addFunctionImport(sym: Opt[Symbol], funcImport: FuncImport): FuncIdx = - val numIdx = NumIdx(functionImports.size + funcs.size) + val numIdx = functionImports.size + funcs.size functionImports += funcImport sym.foreach: namedFuncs(_) = numIdx - FuncIdx(funcImport.id.getOrElse(numIdx)) + FuncIdx(funcImport.id.getOrElse(NumIdx(numIdx))) /** Returns the cached function import for (`module`, `name`), creating it with `createImport` if needed. */ @@ -379,9 +379,9 @@ class Ctx( */ def getFunc(funcref: FuncIdx | Symbol, resolveSymIdx: Bool = false): Opt[FuncIdx] = funcref match case FuncIdx(SymIdx(nme)) if resolveSymIdx => - namedFuncs.find(_._1.nme == nme).map(f => FuncIdx(f._2)) + namedFuncs.find(_._1.nme == nme).map(f => FuncIdx(NumIdx(f._2))) case funcidx: FuncIdx => S(funcidx) - case sym: Symbol if resolveSymIdx => namedFuncs.get(sym).map(FuncIdx(_)) + case sym: Symbol if resolveSymIdx => namedFuncs.get(sym).map(idx => FuncIdx(NumIdx(idx))) case sym: Symbol => getFunc(sym, resolveSymIdx = true).map: numIdx => getFuncInfo(numIdx).flatMap(_.id).fold(numIdx)(FuncIdx(_)) @@ -393,8 +393,8 @@ class Ctx( /** Returns the [[FuncInfo]] instance associated with the given `funcref`. */ def getFuncInfo(funcref: FuncIdx | Symbol): Opt[FuncInfo] = funcref match - case FuncIdx(numIdx @ NumIdx(idx)) => - funcInfosByIndex.get(numIdx).orElse: + case FuncIdx(NumIdx(idx)) => + funcInfosByIndex.get(idx).orElse: val localIdx = idx.toInt - functionImports.size if localIdx < 0 then N else funcs.unapply(localIdx) case funcref => getFunc(funcref, resolveSymIdx = true).flatMap(getFuncInfo(_)) @@ -412,9 +412,9 @@ class Ctx( /** Adds a new local variable into the top-most variable scope. */ def addLocal(sym: Local): LocalIdx = - val numIdx = NumIdx(locals.head.size) + val numIdx = locals.head.size locals.head(sym) = numIdx - LocalIdx(numIdx) + LocalIdx(NumIdx(numIdx)) /** Adds a [[Seq]] of local variables into the top-most variable scope. */ def addLocals(syms: Seq[Local]): Seq[LocalIdx] = @@ -425,7 +425,7 @@ class Ctx( /** Adds a new variable into the global variable scope. */ def addGlobal(sym: Symbol, globalInfo: GlobalInfo): GlobalIdx = - val numIdx = NumIdx(globals.size) + val numIdx = globals.size globals += globalInfo namedGlobals(sym) = numIdx GlobalIdx(globalInfo.id) @@ -472,8 +472,8 @@ class Ctx( /** Converts a [[Map]] of symbols and their respective numeric identifiers into a [[Seq]] of symbols sorted by its * numeric index. */ - private def wasmLocalsToSeq(scope: Map[Symbol, NumIdx]): Seq[Local] = - scope.toSeq.sortBy(_._2.index).map(_._1) + private def wasmLocalsToSeq(scope: Map[Symbol, Int]): Seq[Local] = + scope.toSeq.sortBy(_._2).map(_._1) /** Returns a tuple containing the variables in the current `global` and `local` scopes respectively. */ From 87706c2dc578d588db148c7a42994f4429ea3a87 Mon Sep 17 00:00:00 2001 From: David Mak Date: Fri, 13 Mar 2026 20:02:53 +0800 Subject: [PATCH 04/16] wasm: Return `SymIdx` from `addLocal` --- hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 16ed6def66..10926ae89b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -414,7 +414,7 @@ class Ctx( def addLocal(sym: Local): LocalIdx = val numIdx = locals.head.size locals.head(sym) = numIdx - LocalIdx(NumIdx(numIdx)) + LocalIdx(SymIdx(sym.nme)) /** Adds a [[Seq]] of local variables into the top-most variable scope. */ def addLocals(syms: Seq[Local]): Seq[LocalIdx] = From 090adb75b2c8fcc4c2da3958dfbe3c3eacc5f0bf Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 00:22:06 +0800 Subject: [PATCH 05/16] wasm: Make symbolic index for types mandatory - User-defined types will use the type name as its symbolic index ID - Synthetic types will use a generated name based on its context as its symbolic index ID --- .../scala/hkmc2/codegen/wasm/text/Ctx.scala | 54 +++++++++++-------- .../hkmc2/codegen/wasm/text/WatBuilder.scala | 45 +++++++++------- .../shared/src/test/mlscript/wasm/Basics.mls | 36 ++++++------- .../test/mlscript/wasm/BuiltinOperators.mls | 24 ++++----- .../src/test/mlscript/wasm/ControlFlow.mls | 22 ++++---- .../src/test/mlscript/wasm/Exceptions.mls | 8 +-- .../src/test/mlscript/wasm/Matching.mls | 14 ++--- .../src/test/mlscript/wasm/ScopedLocals.mls | 30 +++++------ .../src/test/mlscript/wasm/SingletonUnit.mls | 8 +-- .../src/test/mlscript/wasm/Singletons.mls | 4 +- .../shared/src/test/mlscript/wasm/Strings.mls | 8 +-- .../shared/src/test/mlscript/wasm/Tuples.mls | 4 +- 12 files changed, 137 insertions(+), 120 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 10926ae89b..df9280ab60 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -8,7 +8,7 @@ import hkmc2.utils.* import document.* import document.Document -import semantics.* +import semantics.*, Elaborator.State import text.Param as WasmParam import Instructions.* @@ -122,28 +122,30 @@ end GlobalInfo * @param compType * The composite type this type definition represents. */ -class TypeInfo(val id: Opt[SymIdx], val compType: CompType) extends ToWat: +class TypeInfo(val id: SymIdx, val compType: CompType) extends ToWat: /** @param sym * The source [[BlockMemberSymbol]] which this type is generated from. * @param compType * The composite type this type definition represents. */ - def this(sym: BlockMemberSymbol, compType: CompType) = this( - sym.optionIf(_.nameIsMeaningful).map(sym => SymIdx(sym.nme)), + def this(sym: BlockMemberSymbol, compType: CompType)(using Raise, Scope) = this( + SymIdx(sym.optionIf(_.nameIsMeaningful).fold(summon[Scope].allocateName(sym))(_.nme)), compType, ) - private def idDoc: Document = id.fold(doc"")(_.toWat) + @deprecated("Consider providing a symbolic identifier by using `Scope.allocateName` with a `TempSymbol`.") + def this(id: Opt[SymIdx], compType: CompType)(using Raise, Scope, State) = + this(id.getOrElse(SymIdx(summon[Scope].allocateName(TempSymbol(N, "")))), compType) def toWat: Document = compType match case struct: StructType if struct.isSubtype => val parentsDoc = struct.parents.optionIf(_.nonEmpty).fold(doc""): parents => parents.map(_.toWat).mkDocument(doc" ") val structDoc = struct.copy(isSubtype = false).toWat - doc"(type${idDoc.surroundUnlessEmpty(doc" ")} (sub${parentsDoc.surroundUnlessEmpty(doc" ")} ${structDoc}))" + doc"(type ${id.toWat} (sub${parentsDoc.surroundUnlessEmpty(doc" ")} ${structDoc}))" case _ => - doc"(type${idDoc.surroundUnlessEmpty(doc" ")} ${compType.toWat})" + doc"(type ${id.toWat} ${compType.toWat})" end TypeInfo /** A WebAssembly exception tag declaration. @@ -287,31 +289,41 @@ class Ctx( types += typeInfo sym.foreach: namedTypes(_) = numIdx - TypeIdx(typeInfo.id.getOrElse(NumIdx(numIdx))) + TypeIdx(typeInfo.id) + + @deprecated("Use the overload without `resolveSymIdx` instead.") + def getType(typeref: TypeIdx | BlockMemberSymbol, resolveSymIdx: Bool): Opt[TypeIdx] = + if resolveSymIdx then + typeref match + case TypeIdx(SymIdx(nme)) => + namedTypes.find(_._1.nme == nme).map(t => TypeIdx(NumIdx(t._2))) + case typeidx: TypeIdx => S(typeidx) + case sym: BlockMemberSymbol => namedTypes.get(sym).map(idx => TypeIdx(NumIdx(idx))) + else getType(typeref) /** Returns the [[TypeIdx]] of the given `typeref`, optionally resolving the symbolic index into a numeric index. */ - def getType(typeref: TypeIdx | BlockMemberSymbol, resolveSymIdx: Bool = false): Opt[TypeIdx] = - typeref match - case TypeIdx(SymIdx(nme)) if resolveSymIdx => - namedTypes.find(_._1.nme == nme).map(t => TypeIdx(NumIdx(t._2))) - case typeidx: TypeIdx => S(typeidx) - case sym: BlockMemberSymbol if resolveSymIdx => namedTypes.get(sym).map(idx => TypeIdx(NumIdx(idx))) - case sym: BlockMemberSymbol => - getType(sym, resolveSymIdx = true).map: numIdx => - getTypeInfo(numIdx).flatMap(_.id).fold(numIdx)(TypeIdx(_)) + def getType(typeref: TypeIdx | BlockMemberSymbol): Opt[TypeIdx] = typeref match + case typeidx: TypeIdx => S(typeidx) + case sym: BlockMemberSymbol => getTypeInfo(typeref).map(ti => TypeIdx(ti.id)) - /** Same as [[getType]] but throws an exception when the `typeref` is not found. */ - def getType_!(typeref: TypeIdx | BlockMemberSymbol, resolveSymIdx: Bool = false): TypeIdx = + @deprecated("Use the overload without `resolveSymIdx` instead.") + def getType_!(typeref: TypeIdx | BlockMemberSymbol, resolveSymIdx: Bool): TypeIdx = getType(typeref, resolveSymIdx).getOrElse: lastWords(s"Missing type definition for ${typeref.prettyString}") + /** Same as [[getType]] but throws an exception when the `typeref` is not found. */ + def getType_!(typeref: TypeIdx | BlockMemberSymbol): TypeIdx = + getType(typeref).getOrElse: + lastWords(s"Missing type definition for ${typeref.prettyString}") + /** Returns the [[TypeInfo]] instance associated with the given `typeref`. */ def getTypeInfo(typeref: TypeIdx | BlockMemberSymbol): Opt[TypeInfo] = typeref match case TypeIdx(NumIdx(idx)) => types.unapply(idx.toInt) case TypeIdx(SymIdx(nme)) => - namedTypes.find(_._1.nme == nme).flatMap(t => getTypeInfo(TypeIdx(NumIdx(t._2)))) - case sym: BlockMemberSymbol => namedTypes.get(sym).flatMap(idx => getTypeInfo(TypeIdx(NumIdx(idx)))) + // TODO(Derppening): Consider adding a `Map[SymIdx, TypeInfo]` for faster lookup + types.find(_.id.id == nme) + case sym: BlockMemberSymbol => namedTypes.get(sym).map(idx => types(idx)) /** Same as [[getTypeInfo]] but throws an exception when the `typeref` is not found. */ def getTypeInfo_!(typeref: TypeIdx | BlockMemberSymbol): TypeInfo = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 85a13283c2..4142324682 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -144,7 +144,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: end registerSingletonInit /** Recursively declares supported top-level class types (needed for nested function codegen). */ - private def createDefnTypes(b: Block)(using Ctx): Unit = b match + private def createDefnTypes(b: Block)(using Ctx, Raise, Scope): Unit = b match case Define(defn: ClsLikeDefn, rst) => if isSupportedTopLevelClass(defn) then val inheritedFields = baseObjectStruct.fields @@ -160,7 +160,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ctx.addType( sym = S(defn.sym), typeInfo = TypeInfo( - id = S(SymIdx(defn.sym.nme)), + sym = defn.sym, compType = StructType(fields = allFields, parents = Seq(baseObjectTypeIdx), isSubtype = true), ), ) @@ -192,7 +192,8 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: case _: BlockTail => () /** Gets (and caches) the exception tag used for MLX `throw`. */ - private def exnTagIdx(using Ctx): TagIdx = + private def exnTagIdx(using Ctx, Raise, Scope): TagIdx = + val symNme = scope.allocateName(TempSymbol(N, "mlx_exn")) ctx.getOrCreateWasmIntrinsicTag( "mlx_exn", ctx.addTag(TagInfo( @@ -200,7 +201,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: typeIdx = ctx.addType( sym = N, TypeInfo( - id = N, + id = SymIdx(symNme), FunctionType(params = Seq(WasmParam(N, RefType.anyref)), results = Seq.empty), ), ), @@ -229,7 +230,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: /** Ensures imports required for string materialization exist and returns the constructor function. */ - private def getOrLoadStrCtorFunction(using Ctx): FuncIdx = + private def getOrLoadStrCtorFunction(using Ctx, Raise, Scope): FuncIdx = val minBytes = nextStringDataOffset val pageSize = ExternIntrinsics.WasmPageSizeBytes val minPages = @@ -244,10 +245,11 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: module = ExternIntrinsics.SystemModule, name = ExternIntrinsics.StringFromUtf16ImportName, ): + val importTyNme = scope.allocateName(TempSymbol(N, ExternIntrinsics.StringFromUtf16ImportName)) val importTy = ctx.addType( sym = N, TypeInfo( - id = N, + id = SymIdx(importTyNme), FunctionType( params = Seq(WasmParam(N, RefType.anyref), WasmParam(N, RefType.anyref)), results = Seq(Result(RefType.anyref)), @@ -264,7 +266,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: /** Gets (and caches) the Wasm GC array type used for tuples (`mut` selects mutability). */ - private def tupleArrayType(mut: Bool)(using Ctx): TypeIdx = + private def tupleArrayType(mut: Bool)(using Ctx, Raise, Scope): TypeIdx = ctx.getOrCreateWasmIntrinsicType(WasmIntrinsicType.TupleArray(mutable = mut)): val suffix = if mut then "Mut" else "" val sym = BlockMemberSymbol(s"TupleArray$suffix", Nil) @@ -736,12 +738,12 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: /** Gets (or creates) the intrinsic function implementing the wasm operator `name`. */ - private def getIntrinsic(name: Str)(using Ctx, Scope): FuncIdx = + private def getIntrinsic(name: Str)(using Ctx, Raise, Scope): FuncIdx = ctx.getOrCreateWasmIntrinsic(name, createIntrinsic(name)) /** Creates the intrinsic definition for `name`. */ - private def createIntrinsic(name: Str)(using Ctx, Scope): FuncIdx = + private def createIntrinsic(name: Str)(using Ctx, Raise, Scope): FuncIdx = if binaryOps.contains(name) then createBinaryInt31Func(name, binaryOps(name)) else if unaryOps.contains(name) then createUnaryInt31Func(name, unaryOps(name)) else lastWords(s"Unsupported wasm intrinsic '$name'") @@ -751,7 +753,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: private def createBinaryInt31Func( name: Str, op: (Expr, Expr) => Expr, - )(using Ctx, Scope): FuncIdx = + )(using Ctx, Raise, Scope): FuncIdx = val params = mkIntrinsicParams(name, Seq("lhs", "rhs")) val lhsName = params.head._2 val rhsName = params(1)._2 @@ -760,7 +762,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: /** Creates a unary Int31 intrinsic with a single parameter and body built from `op`. */ - private def createUnaryInt31Func(name: Str, op: Expr => Expr)(using Ctx, Scope): FuncIdx = + private def createUnaryInt31Func(name: Str, op: Expr => Expr)(using Ctx, Raise, Scope): FuncIdx = val params = mkIntrinsicParams(name, Seq("arg")) val argName = params.head._2 val body = unaryInt31Body(argName, op) @@ -772,11 +774,11 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: name: Str, params: Seq[(TempSymbol, Str)], body: Expr, - )(using Ctx): FuncIdx = + )(using Ctx, Raise, Scope): FuncIdx = val funcTy = ctx.addType( sym = N, TypeInfo( - id = N, + id = SymIdx(scope.allocateName(TempSymbol(N, name))), FunctionType( params = params.map((_, nme) => WasmParam(S(nme), RefType.anyref)), results = Seq(Result(RefType.anyref)), @@ -1014,7 +1016,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: val funcTy = ctx.addType( sym = N, TypeInfo( - id = N, + id = SymIdx(scope.allocateName(TempSymbol(N, sym.nme))), FunctionType( params = params.map(_._1), results = Seq.fill(bodyWat.resultTypes.length)(Result(RefType.anyref)), @@ -1116,12 +1118,12 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: break(errUnimplExpr("newCtorAuxParams.nonEmpty")) val funcTyId = - if isSingletonObj then S(SymIdx(s"${clsLikeDefn.sym.nme}_ctor")) - else N + if isSingletonObj then s"${clsLikeDefn.sym.nme}_ctor" + else scope.allocateName(TempSymbol(N, s"${clsLikeDefn.sym.nme}_ctor")) val funcTy = ctx.addType( sym = N, TypeInfo( - id = funcTyId, + id = SymIdx(funcTyId), FunctionType( params = ctorParams.map(p => WasmParam(S(p._2), RefType.anyref)), results = Seq(Result(RefType.anyref)), @@ -1485,7 +1487,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ctx.addType( sym = S(baseObjectSym), TypeInfo( - id = S(SymIdx("Object")), + id = SymIdx("Object"), StructType( Seq(tagFieldSym -> Field(I32Type, mutable = true, id = SymIdx("$tag"))), isSubtype = true, @@ -1509,7 +1511,10 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: val entryFnTy = ctx.addType( sym = N, - TypeInfo(id = N, FunctionType(params = Seq.empty, results = Seq(Result(RefType.anyref)))), + TypeInfo( + id = SymIdx(scope.allocateName(TempSymbol(N, entryNme))), + FunctionType(params = Seq.empty, results = Seq(Result(RefType.anyref))), + ), ) val entryFnInfo = FuncInfo( id = S(SymIdx(entryNme)), @@ -1532,7 +1537,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: val initTy = ctx.addType( sym = N, TypeInfo( - id = S(SymIdx("start")), + id = SymIdx(scope.allocateName(TempSymbol(N, "start"))), FunctionType(params = Seq.empty, results = Seq.empty), ), ) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls index b3c1e55e92..c449e8a9ac 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls @@ -49,16 +49,16 @@ foo() + foo() //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (result (ref null any)))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $foo (type 1) (result (ref null any)) +//│ (type $foo1 (func (result (ref null any)))) +//│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $foo (type $foo1) (result (ref null any)) //│ (return //│ (ref.i31 //│ (i32.const 42)))) //│ (export "foo" (func $foo)) //│ (elem declare func $foo) -//│ (func (type 2) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -76,7 +76,7 @@ foo() + foo() //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $foo (ref null any)) //│ (local $tmp (ref null any)) //│ (local $tmp1 (ref null any)) @@ -84,11 +84,11 @@ foo() + foo() //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp -//│ (call_ref 1 +//│ (call_ref $foo1 //│ (ref.func $foo))) //│ (block (result (ref null any)) //│ (local.set $tmp1 -//│ (call_ref 1 +//│ (call_ref $foo1 //│ (ref.func $foo))) //│ (call 1 //│ (local.get $tmp) @@ -118,9 +118,9 @@ class Foo(val a) //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $a (mut (ref null any)))))) -//│ (type (func (param $a (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $Foo (type 2) (param $a (ref null any)) (result (ref null any)) +//│ (type $Foo_ctor (func (param $a (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $Foo (type $Foo_ctor) (param $a (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -139,7 +139,7 @@ class Foo(val a) //│ (local.get $this)))) //│ (export "Foo" (func $Foo)) //│ (elem declare func $Foo) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Foo (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) @@ -172,9 +172,9 @@ class Foo(val x) with //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any))) (field $y (mut (ref null any)))))) -//│ (type (func (param $x (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $Foo (type 2) (param $x (ref null any)) (result (ref null any)) +//│ (type $Foo_ctor (func (param $x (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $Foo (type $Foo_ctor) (param $x (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -200,7 +200,7 @@ class Foo(val x) with //│ (local.get $this)))) //│ (export "Foo" (func $Foo)) //│ (elem declare func $Foo) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Foo (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) @@ -229,7 +229,7 @@ O.y //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $O (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any))) (field $y (mut (ref null any)))))) //│ (type $O_ctor (func (result (ref null any)))) -//│ (type (func (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $O$inst (mut (ref null $O)) (ref.null $O)) //│ (start 1) @@ -263,7 +263,7 @@ O.y //│ (global.set $O$inst //│ (ref.cast (ref null $O) //│ (call 0))))) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $O (ref null any)) //│ (block (result (ref null any)) //│ (nop) diff --git a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls index 0f50f09946..21e9c29e75 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls @@ -7,9 +7,9 @@ //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func (type 1) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -27,7 +27,7 @@ //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func $entry (type 2) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (call 0 //│ (ref.i31 //│ (i32.const 1)) @@ -43,9 +43,9 @@ //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $arg (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func (type 1) (param $arg (ref null any)) (result (ref null any)) +//│ (type $neg_impl (func (param $arg (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func (type $neg_impl) (param $arg (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (ref.test (ref null i31) //│ (local.get $arg)) @@ -58,7 +58,7 @@ //│ (local.get $arg)))))) //│ (else //│ (unreachable)))) -//│ (func $entry (type 2) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (call 0 //│ (ref.i31 //│ (i32.const 2)))) @@ -92,9 +92,9 @@ not true //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func (type 1) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (type $eq_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func (type $eq_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -112,7 +112,7 @@ not true //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func $entry (type 2) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (call 0 //│ (ref.i31 //│ (i32.const 1)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index a2e2077136..cc38e40a2f 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -10,15 +10,15 @@ let i = 0 in //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $lt_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $Unit (sub $Object (struct (field $$tag (mut i32))))) //│ (type $Unit_ctor (func (result (ref null any)))) -//│ (type (func (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Unit$inst (mut (ref null $Unit)) (ref.null $Unit)) //│ (start 3) -//│ (func (type 1) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func (type $lt_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -36,7 +36,7 @@ let i = 0 in //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func (type 2) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -71,7 +71,7 @@ let i = 0 in //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call 2))))) -//│ (func $entry (type 5) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $i (ref null any)) //│ (local $scrut (ref null any)) //│ (local $tmp (ref null any)) @@ -205,9 +205,9 @@ tailMatchAllValue(1) //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $x (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $tailMatchAllValue (type 1) (param $x (ref null any)) (result (ref null any)) +//│ (type $tailMatchAllValue1 (func (param $x (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $tailMatchAllValue (type $tailMatchAllValue1) (param $x (ref null any)) (result (ref null any)) //│ (local $matchRes (ref null any)) //│ (block (result (ref null any)) //│ (block $match @@ -243,11 +243,11 @@ tailMatchAllValue(1) //│ (local.get $matchRes))) //│ (export "tailMatchAllValue" (func $tailMatchAllValue)) //│ (elem declare func $tailMatchAllValue) -//│ (func $entry (type 2) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $tailMatchAllValue (ref null any)) //│ (block (result (ref null any)) //│ (nop) -//│ (call_ref 1 +//│ (call_ref $tailMatchAllValue1 //│ (ref.i31 //│ (i32.const 1)) //│ (ref.func $tailMatchAllValue)))) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls b/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls index 6547e2fe10..6a5a62d1ff 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls @@ -9,11 +9,11 @@ throw 1 //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (tag $mlx_exn (type 1)) +//│ (type $mlx_exn (func (param (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (tag $mlx_exn (type $mlx_exn)) //│ (export "mlx_exn" (tag $mlx_exn)) -//│ (func $entry (type 2) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (throw $mlx_exn //│ (ref.i31 //│ (i32.const 1)))) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls index 5d96862b9b..f29e4217ea 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls @@ -46,10 +46,10 @@ if Bar(true) is //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Bar (sub $Object (struct (field $$tag (mut i32)) (field $y (mut (ref null any)))))) //│ (type $Baz (sub $Object (struct (field $$tag (mut i32)) (field $z (mut (ref null any)))))) -//│ (type (func (param $y (ref null any)) (result (ref null any)))) -//│ (type (func (param $z (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $Bar (type 3) (param $y (ref null any)) (result (ref null any)) +//│ (type $Bar_ctor (func (param $y (ref null any)) (result (ref null any)))) +//│ (type $Baz_ctor (func (param $z (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $Bar (type $Bar_ctor) (param $y (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -68,7 +68,7 @@ if Bar(true) is //│ (local.get $this)))) //│ (export "Bar" (func $Bar)) //│ (elem declare func $Bar) -//│ (func $Baz (type 4) (param $z (ref null any)) (result (ref null any)) +//│ (func $Baz (type $Baz_ctor) (param $z (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -87,7 +87,7 @@ if Bar(true) is //│ (local.get $this)))) //│ (export "Baz" (func $Baz)) //│ (elem declare func $Baz) -//│ (func $entry (type 5) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Bar (ref null any)) //│ (local $Baz (ref null any)) //│ (local $scrut (ref null any)) @@ -103,7 +103,7 @@ if Bar(true) is //│ (nop) //│ (block (result (ref null any)) //│ (local.set $scrut -//│ (call_ref 3 +//│ (call_ref $Bar_ctor //│ (ref.i31 //│ (i32.const 1)) //│ (ref.func $Bar))) diff --git a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls index 5b82a49b94..305e9d1cf2 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls @@ -10,9 +10,9 @@ topA + topB //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func (type 1) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -30,7 +30,7 @@ topA + topB //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func $entry (type 2) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $topA (ref null any)) //│ (local $topB (ref null any)) //│ (block (result (ref null any)) @@ -59,10 +59,10 @@ chain(1) //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) -//│ (type (func (param $x (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func (type 1) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) +//│ (type $chain1 (func (param $x (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -80,7 +80,7 @@ chain(1) //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func $chain (type 2) (param $x (ref null any)) (result (ref null any)) +//│ (func $chain (type $chain1) (param $x (ref null any)) (result (ref null any)) //│ (local $a (ref null any)) //│ (local $b (ref null any)) //│ (block (result (ref null any)) @@ -101,11 +101,11 @@ chain(1) //│ (local.get $b)))))) //│ (export "chain" (func $chain)) //│ (elem declare func $chain) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $chain (ref null any)) //│ (block (result (ref null any)) //│ (nop) -//│ (call_ref 2 +//│ (call_ref $chain1 //│ (ref.i31 //│ (i32.const 1)) //│ (ref.func $chain)))) @@ -169,9 +169,9 @@ class Foo(val a, val b) //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $a (mut (ref null any))) (field $b (mut (ref null any)))))) -//│ (type (func (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $Foo (type 2) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)) +//│ (type $Foo_ctor (func (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $Foo (type $Foo_ctor) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -195,7 +195,7 @@ class Foo(val a, val b) //│ (local.get $this)))) //│ (export "Foo" (func $Foo)) //│ (elem declare func $Foo) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Foo (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls index 7538ec47ea..b4ed1cf248 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls @@ -9,7 +9,7 @@ //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Unit (sub $Object (struct (field $$tag (mut i32))))) //│ (type $Unit_ctor (func (result (ref null any)))) -//│ (type (func (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Unit$inst (mut (ref null $Unit)) (ref.null $Unit)) //│ (start 1) @@ -30,7 +30,7 @@ //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call 0))))) -//│ (func $entry (type 3) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (global.get $Unit$inst)) //│ (export "entry" (func $entry)) //│ (elem declare func $entry)) @@ -74,8 +74,8 @@ x //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (result (ref null any)))) -//│ (func $entry (type 1) (result (ref null any)) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $x (ref null any)) //│ (block (result (ref null any)) //│ (local.set $x diff --git a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls index ec60b20877..d0459ea7f7 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls @@ -16,7 +16,7 @@ Bar.y //│ (type $Bar (sub $Object (struct (field $$tag (mut i32)) (field $y (mut (ref null any)))))) //│ (type $Foo_ctor (func (result (ref null any)))) //│ (type $Bar_ctor (func (result (ref null any)))) -//│ (type (func (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Foo$inst (mut (ref null $Foo)) (ref.null $Foo)) //│ (global $Bar$inst (mut (ref null $Bar)) (ref.null $Bar)) @@ -65,7 +65,7 @@ Bar.y //│ (global.set $Bar$inst //│ (ref.cast (ref null $Bar) //│ (call 1))))) -//│ (func $entry (type 5) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Bar (ref null any)) //│ (local $Foo (ref null any)) //│ (block (result (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Strings.mls b/hkmc2/shared/src/test/mlscript/wasm/Strings.mls index 03ad153054..d3620df5bb 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Strings.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Strings.mls @@ -17,14 +17,14 @@ let dup2 = "dup" //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) -//│ (type (func (param (ref null any)) (param (ref null any)) (result (ref null any)))) +//│ (type $mlx_str_from_utf16 (func (param (ref null any)) (param (ref null any)) (result (ref null any)))) //│ (type $TupleArray (array (ref null any))) //│ (type $TupleArrayMut (array (mut (ref null any)))) -//│ (type (func (result (ref null any)))) +//│ (type $entry1 (func (result (ref null any)))) //│ (import "system" "mem" (memory 1)) -//│ (import "system" "mlx_str_from_utf16" (func $mlx_str_from_utf16 (type 1))) +//│ (import "system" "mlx_str_from_utf16" (func $mlx_str_from_utf16 (type $mlx_str_from_utf16))) //│ (data (i32.const 0) "\64\00\75\00\70\00") -//│ (func $entry (type 4) (result (ref null any)) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $dup1 (ref null any)) //│ (local $dup2 (ref null any)) //│ (local $tmp (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls b/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls index 0636d511d2..46025df1cf 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls @@ -54,8 +54,8 @@ c.[3] //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $TupleArray (array (ref null any))) //│ (type $TupleArrayMut (array (mut (ref null any)))) -//│ (type (func (result (ref null any)))) -//│ (func $entry (type 3) (result (ref null any)) +//│ (type $entry1 (func (result (ref null any)))) +//│ (func $entry (type $entry1) (result (ref null any)) //│ (local $c (ref null any)) //│ (local $tuple (ref null any)) //│ (block (result (ref null any)) From 747f64949dc08843399db60bad8d163e78f36d5c Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 01:06:30 +0800 Subject: [PATCH 06/16] wasm: Make symbolic index for functions mandatory --- .../scala/hkmc2/codegen/wasm/text/Ctx.scala | 75 ++++++++++++++----- .../hkmc2/codegen/wasm/text/WatBuilder.scala | 31 +++++--- .../shared/src/test/mlscript/wasm/Basics.mls | 29 +++---- .../test/mlscript/wasm/BuiltinOperators.mls | 15 ++-- .../src/test/mlscript/wasm/ControlFlow.mls | 18 +++-- .../src/test/mlscript/wasm/Matching.mls | 14 ++-- .../src/test/mlscript/wasm/ScopedLocals.mls | 22 +++--- .../src/test/mlscript/wasm/SingletonUnit.mls | 8 +- .../src/test/mlscript/wasm/Singletons.mls | 11 ++- 9 files changed, 145 insertions(+), 78 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index df9280ab60..5524c4ac6e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -30,14 +30,17 @@ import scala.collection.mutable.{ArrayBuffer as ArrayBuf, Map as MutMap} * [[Seq]] of local variables (excluding parameters) and their names. * @param body * The expression of the function body. + * @param exports + * [[Seq]] of export names for the function. */ class FuncInfo( - val id: Opt[SymIdx], + val id: SymIdx, val typeIdx: TypeIdx, params: Seq[Local -> Str], nResults: Int, locals: Seq[Local -> Str], val body: Expr, + val exports: Seq[Str], ) extends ToWat: /** @param sym @@ -60,13 +63,33 @@ class FuncInfo( nResults: Int, locals: Seq[Local -> Str], body: Expr, - ) = this( - sym.optionIf(_.nameIsMeaningful).map(sym => SymIdx(sym.nme)), + )(using Raise, Scope) = this( + SymIdx(sym.optionIf(_.nameIsMeaningful).fold(summon[Scope].allocateName(sym))(_.nme)), + typeIdx, + params, + nResults, + locals, + body, + sym.optionIf(_.nameIsMeaningful).map(_.nme).toSeq, + ) + + @deprecated("Consider providing a symbolic identifier by using `Scope.allocateName` with a `TempSymbol`.") + def this( + id: Opt[SymIdx], + typeIdx: TypeIdx, + params: Seq[Local -> Str], + nResults: Int, + locals: Seq[Local -> Str], + body: Expr, + exports: Seq[Str], + )(using Raise, Scope, State) = this( + id.getOrElse(SymIdx(summon[Scope].allocateName(TempSymbol(N, "")))), typeIdx, params, nResults, locals, body, + exports, ) /** Returns the type of this function as a [[SignatureType]]. */ @@ -76,16 +99,19 @@ class FuncInfo( ) def toWat: Document = - doc"""(func ${id.fold(doc"")(_.toWat)} (type ${typeIdx.toWat})${ + // TODO(Derppening): Make exports configurable + doc"""(func ${id.toWat} (type ${typeIdx.toWat})${ getSignatureType.toWat.surroundUnlessEmpty(doc" ") } #{ ${ locals.map: p => doc"(local $$${p._2} ${RefType.anyref.toWat})" .mkDocument(doc" # ").surroundUnlessEmpty(doc" # ") } # ${body.toWat} #} )${ - id.fold(doc""): id => - doc""" # (export "${id.id}" (func ${id.toWat})) # (elem declare func ${id.toWat})""" - }""" + exports + .map: e => + doc""" # (export "${e}" (func ${id.toWat}))""" + .mkDocument(doc"") + } # (elem declare func ${id.toWat})""" end FuncInfo /** A Wasm global and its associated information. @@ -337,7 +363,7 @@ class Ctx( funcInfosByIndex(numIdx) = funcInfo sym.foreach: namedFuncs(_) = numIdx - FuncIdx(funcInfo.id.getOrElse(NumIdx(numIdx))) + FuncIdx(funcInfo.id) /** Adds a function import into this context. * @@ -387,29 +413,42 @@ class Ctx( tags += tagInfo TagIdx(tagInfo.id) + @deprecated("Use the overload without `resolveSymIdx` instead.") + def getFunc(funcref: FuncIdx | Symbol, resolveSymIdx: Bool): Opt[FuncIdx] = + if resolveSymIdx then + funcref match + case FuncIdx(SymIdx(nme)) if resolveSymIdx => + namedFuncs.find(_._1.nme == nme).map(f => FuncIdx(NumIdx(f._2))) + case funcidx: FuncIdx => S(funcidx) + case sym: Symbol => namedFuncs.get(sym).map(idx => FuncIdx(NumIdx(idx))) + else getFunc(funcref) + /** Returns the [[FuncIdx]] of the given `funcref`, optionally resolving the symbolic index into a numeric index. */ - def getFunc(funcref: FuncIdx | Symbol, resolveSymIdx: Bool = false): Opt[FuncIdx] = funcref match - case FuncIdx(SymIdx(nme)) if resolveSymIdx => - namedFuncs.find(_._1.nme == nme).map(f => FuncIdx(NumIdx(f._2))) + def getFunc(funcref: FuncIdx | Symbol): Opt[FuncIdx] = funcref match case funcidx: FuncIdx => S(funcidx) - case sym: Symbol if resolveSymIdx => namedFuncs.get(sym).map(idx => FuncIdx(NumIdx(idx))) - case sym: Symbol => - getFunc(sym, resolveSymIdx = true).map: numIdx => - getFuncInfo(numIdx).flatMap(_.id).fold(numIdx)(FuncIdx(_)) + case sym: Symbol => getFuncInfo(funcref).map(fi => FuncIdx(fi.id)) - /** Same as [[getFunc]] but throws an exception when the `funcref` is not found. */ - def getFunc_!(funcref: FuncIdx | Symbol, resolveSymIdx: Bool = false): FuncIdx = + @deprecated("Use the overload without `resolveSymIdx` instead.") + def getFunc_!(funcref: FuncIdx | Symbol, resolveSymIdx: Bool): FuncIdx = getFunc(funcref, resolveSymIdx).getOrElse: lastWords(s"Missing function definition for ${funcref.prettyString}") + /** Same as [[getFunc]] but throws an exception when the `funcref` is not found. */ + def getFunc_!(funcref: FuncIdx | Symbol): FuncIdx = + getFunc(funcref).getOrElse: + lastWords(s"Missing function definition for ${funcref.prettyString}") + /** Returns the [[FuncInfo]] instance associated with the given `funcref`. */ def getFuncInfo(funcref: FuncIdx | Symbol): Opt[FuncInfo] = funcref match case FuncIdx(NumIdx(idx)) => funcInfosByIndex.get(idx).orElse: val localIdx = idx.toInt - functionImports.size if localIdx < 0 then N else funcs.unapply(localIdx) - case funcref => getFunc(funcref, resolveSymIdx = true).flatMap(getFuncInfo(_)) + case FuncIdx(SymIdx(nme)) => + // TODO(Derppening): Consider adding a `Map[SymIdx, TypeInfo]` for faster lookup + funcs.find(_.id.id == nme) + case funcref: Symbol => namedFuncs.get(funcref).map(idx => funcs(idx)) /** Same as [[getFuncInfo]] but throws an exception when the `funcref` is not found. */ def getFuncInfo_!(funcref: FuncIdx | Symbol): FuncInfo = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 4142324682..22fe140438 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -786,12 +786,13 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ), ) val funcInfo = FuncInfo( - id = N, + id = SymIdx(scope.allocateName(TempSymbol(N, name))), typeIdx = funcTy, params = params, nResults = 1, locals = Seq.empty, body = body, + exports = Seq.empty, ) ctx.addFunc(N, funcInfo) end createIntrinsicFunc @@ -1117,9 +1118,13 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: else break(errUnimplExpr("newCtorAuxParams.nonEmpty")) - val funcTyId = - if isSingletonObj then s"${clsLikeDefn.sym.nme}_ctor" - else scope.allocateName(TempSymbol(N, s"${clsLikeDefn.sym.nme}_ctor")) + val funcTyId = clsLikeDefn.sym + .optionIf: sym => + !isSingletonObj && sym.nameIsMeaningful + .map: sym => + s"${sym.nme}_ctor" + .getOrElse: + scope.allocateName(TempSymbol(N, s"${clsLikeDefn.sym.nme}_ctor")) val funcTy = ctx.addType( sym = N, TypeInfo( @@ -1131,18 +1136,22 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ), ) - val ctorId = - if isSingletonObj then N - else clsLikeDefn.sym.optionIf(_.nameIsMeaningful).map(sym => SymIdx(sym.nme)) + val ctorId = clsLikeDefn.sym + .optionIf: sym => + !isSingletonObj && sym.nameIsMeaningful + .map: sym => + s"${sym.nme}_ctor" ctx.addFunc( S(clsLikeDefn.sym), FuncInfo( - id = ctorId, + id = + SymIdx(ctorId.getOrElse(scope.allocateName(TempSymbol(N, s"${clsLikeDefn.sym.nme}_ctor")))), typeIdx = funcTy, params = ctorParams, nResults = ctorCode.resultTypes.length, locals = ctorLocals, body = ctorAux, + exports = ctorId.toSeq, ), ) if isSingletonObj then @@ -1517,13 +1526,14 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ), ) val entryFnInfo = FuncInfo( - id = S(SymIdx(entryNme)), + id = SymIdx(entryNme), typeIdx = entryFnTy, params = Seq.empty, nResults = 1, // TODO(Derppening): Should we place top-level scope variables in the global section? locals = (entryFnLocals ++ entryExtraLocals).map(l => l -> scope.allocateOrGetName(l)), body = entryFnExpr, + exports = Seq(entryNme), ) ctx.popLocal() @@ -1549,12 +1559,13 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: val initFn = ctx.addFunc( sym = N, FuncInfo( - id = N, + id = SymIdx(scope.allocateName(TempSymbol(N, "start"))), typeIdx = initTy, params = Seq.empty, nResults = 0, locals = Seq.empty, body = initBody, + exports = Seq.empty, ), ) ctx.setStartFunc(initFn) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls index c449e8a9ac..f859d76b9b 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls @@ -58,7 +58,7 @@ foo() + foo() //│ (i32.const 42)))) //│ (export "foo" (func $foo)) //│ (elem declare func $foo) -//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func $plus_impl1 (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -76,6 +76,7 @@ foo() + foo() //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) +//│ (elem declare func $plus_impl1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $foo (ref null any)) //│ (local $tmp (ref null any)) @@ -90,7 +91,7 @@ foo() + foo() //│ (local.set $tmp1 //│ (call_ref $foo1 //│ (ref.func $foo))) -//│ (call 1 +//│ (call $plus_impl1 //│ (local.get $tmp) //│ (local.get $tmp1)))))) //│ (export "entry" (func $entry)) @@ -120,7 +121,7 @@ class Foo(val a) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $a (mut (ref null any)))))) //│ (type $Foo_ctor (func (param $a (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func $Foo (type $Foo_ctor) (param $a (ref null any)) (result (ref null any)) +//│ (func $Foo_ctor (type $Foo_ctor) (param $a (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -137,8 +138,8 @@ class Foo(val a) //│ (nop)) //│ (return //│ (local.get $this)))) -//│ (export "Foo" (func $Foo)) -//│ (elem declare func $Foo) +//│ (export "Foo_ctor" (func $Foo_ctor)) +//│ (elem declare func $Foo_ctor) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Foo (ref null any)) //│ (local $tmp (ref null any)) @@ -146,7 +147,7 @@ class Foo(val a) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp -//│ (call $Foo +//│ (call $Foo_ctor //│ (ref.i31 //│ (i32.const 42)))) //│ (struct.get $Foo $a @@ -174,7 +175,7 @@ class Foo(val x) with //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any))) (field $y (mut (ref null any)))))) //│ (type $Foo_ctor (func (param $x (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func $Foo (type $Foo_ctor) (param $x (ref null any)) (result (ref null any)) +//│ (func $Foo_ctor (type $Foo_ctor) (param $x (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -198,8 +199,8 @@ class Foo(val x) with //│ (nop))) //│ (return //│ (local.get $this)))) -//│ (export "Foo" (func $Foo)) -//│ (elem declare func $Foo) +//│ (export "Foo_ctor" (func $Foo_ctor)) +//│ (elem declare func $Foo_ctor) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Foo (ref null any)) //│ (local $tmp (ref null any)) @@ -207,7 +208,7 @@ class Foo(val x) with //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp -//│ (call $Foo +//│ (call $Foo_ctor //│ (ref.i31 //│ (i32.const 42)))) //│ (struct.get $Foo $y @@ -232,8 +233,8 @@ O.y //│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $O$inst (mut (ref null $O)) (ref.null $O)) -//│ (start 1) -//│ (func (type $O_ctor) (result (ref null any)) +//│ (start $start1) +//│ (func $O_ctor1 (type $O_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -258,11 +259,13 @@ O.y //│ (nop))) //│ (return //│ (local.get $this)))) -//│ (func (type $start) +//│ (elem declare func $O_ctor1) +//│ (func $start1 (type $start) //│ (block //│ (global.set $O$inst //│ (ref.cast (ref null $O) //│ (call 0))))) +//│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $O (ref null any)) //│ (block (result (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls index 21e9c29e75..cce0c48ed4 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls @@ -9,7 +9,7 @@ //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func $plus_impl1 (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -27,8 +27,9 @@ //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) +//│ (elem declare func $plus_impl1) //│ (func $entry (type $entry1) (result (ref null any)) -//│ (call 0 +//│ (call $plus_impl1 //│ (ref.i31 //│ (i32.const 1)) //│ (ref.i31 @@ -45,7 +46,7 @@ //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $neg_impl (func (param $arg (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func (type $neg_impl) (param $arg (ref null any)) (result (ref null any)) +//│ (func $neg_impl1 (type $neg_impl) (param $arg (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (ref.test (ref null i31) //│ (local.get $arg)) @@ -58,8 +59,9 @@ //│ (local.get $arg)))))) //│ (else //│ (unreachable)))) +//│ (elem declare func $neg_impl1) //│ (func $entry (type $entry1) (result (ref null any)) -//│ (call 0 +//│ (call $neg_impl1 //│ (ref.i31 //│ (i32.const 2)))) //│ (export "entry" (func $entry)) @@ -94,7 +96,7 @@ not true //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $eq_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func (type $eq_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func $eq_impl1 (type $eq_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -112,8 +114,9 @@ not true //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) +//│ (elem declare func $eq_impl1) //│ (func $entry (type $entry1) (result (ref null any)) -//│ (call 0 +//│ (call $eq_impl1 //│ (ref.i31 //│ (i32.const 1)) //│ (ref.i31 diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index cc38e40a2f..5256d15409 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -17,8 +17,8 @@ let i = 0 in //│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Unit$inst (mut (ref null $Unit)) (ref.null $Unit)) -//│ (start 3) -//│ (func (type $lt_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (start $start1) +//│ (func $lt_impl1 (type $lt_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -36,7 +36,8 @@ let i = 0 in //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (elem declare func $lt_impl1) +//│ (func $plus_impl1 (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -54,7 +55,8 @@ let i = 0 in //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) -//│ (func (type $Unit_ctor) (result (ref null any)) +//│ (elem declare func $plus_impl1) +//│ (func $Unit_ctor1 (type $Unit_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -66,11 +68,13 @@ let i = 0 in //│ (nop) //│ (return //│ (local.get $this)))) -//│ (func (type $start) +//│ (elem declare func $Unit_ctor1) +//│ (func $start1 (type $start) //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call 2))))) +//│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $i (ref null any)) //│ (local $scrut (ref null any)) @@ -86,7 +90,7 @@ let i = 0 in //│ (drop //│ (block (result (ref null any)) //│ (local.set $scrut -//│ (call 0 +//│ (call $lt_impl1 //│ (local.get $i) //│ (ref.i31 //│ (i32.const 10)))) @@ -105,7 +109,7 @@ let i = 0 in //│ (block //│ (block //│ (local.set $tmp -//│ (call 1 +//│ (call $plus_impl1 //│ (local.get $i) //│ (ref.i31 //│ (i32.const 1)))) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls index f29e4217ea..ee364b6293 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls @@ -49,7 +49,7 @@ if Bar(true) is //│ (type $Bar_ctor (func (param $y (ref null any)) (result (ref null any)))) //│ (type $Baz_ctor (func (param $z (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func $Bar (type $Bar_ctor) (param $y (ref null any)) (result (ref null any)) +//│ (func $Bar_ctor (type $Bar_ctor) (param $y (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -66,9 +66,9 @@ if Bar(true) is //│ (nop)) //│ (return //│ (local.get $this)))) -//│ (export "Bar" (func $Bar)) -//│ (elem declare func $Bar) -//│ (func $Baz (type $Baz_ctor) (param $z (ref null any)) (result (ref null any)) +//│ (export "Bar_ctor" (func $Bar_ctor)) +//│ (elem declare func $Bar_ctor) +//│ (func $Baz_ctor (type $Baz_ctor) (param $z (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -85,8 +85,8 @@ if Bar(true) is //│ (nop)) //│ (return //│ (local.get $this)))) -//│ (export "Baz" (func $Baz)) -//│ (elem declare func $Baz) +//│ (export "Baz_ctor" (func $Baz_ctor)) +//│ (elem declare func $Baz_ctor) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Bar (ref null any)) //│ (local $Baz (ref null any)) @@ -106,7 +106,7 @@ if Bar(true) is //│ (call_ref $Bar_ctor //│ (ref.i31 //│ (i32.const 1)) -//│ (ref.func $Bar))) +//│ (ref.func $Bar_ctor))) //│ (block (result (ref null any)) //│ (block $match //│ (local.set $matchRes diff --git a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls index 305e9d1cf2..113985457a 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls @@ -12,7 +12,7 @@ topA + topB //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func $plus_impl1 (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -30,6 +30,7 @@ topA + topB //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) +//│ (elem declare func $plus_impl1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $topA (ref null any)) //│ (local $topB (ref null any)) @@ -41,7 +42,7 @@ topA + topB //│ (local.set $topB //│ (ref.i31 //│ (i32.const 2))) -//│ (call 0 +//│ (call $plus_impl1 //│ (local.get $topA) //│ (local.get $topB))))) //│ (export "entry" (func $entry)) @@ -62,7 +63,7 @@ chain(1) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $chain1 (func (param $x (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) +//│ (func $plus_impl1 (type $plus_impl) (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)) //│ (if (result (ref null any)) //│ (i32.and //│ (ref.test (ref null i31) @@ -80,23 +81,24 @@ chain(1) //│ (local.get $rhs)))))) //│ (else //│ (unreachable)))) +//│ (elem declare func $plus_impl1) //│ (func $chain (type $chain1) (param $x (ref null any)) (result (ref null any)) //│ (local $a (ref null any)) //│ (local $b (ref null any)) //│ (block (result (ref null any)) //│ (local.set $a -//│ (call 0 +//│ (call $plus_impl1 //│ (local.get $x) //│ (ref.i31 //│ (i32.const 1)))) //│ (block (result (ref null any)) //│ (local.set $b -//│ (call 0 +//│ (call $plus_impl1 //│ (local.get $a) //│ (ref.i31 //│ (i32.const 1)))) //│ (return -//│ (call 0 +//│ (call $plus_impl1 //│ (local.get $a) //│ (local.get $b)))))) //│ (export "chain" (func $chain)) @@ -171,7 +173,7 @@ class Foo(val a, val b) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $a (mut (ref null any))) (field $b (mut (ref null any)))))) //│ (type $Foo_ctor (func (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (func $Foo (type $Foo_ctor) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)) +//│ (func $Foo_ctor (type $Foo_ctor) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -193,8 +195,8 @@ class Foo(val a, val b) //│ (nop))) //│ (return //│ (local.get $this)))) -//│ (export "Foo" (func $Foo)) -//│ (elem declare func $Foo) +//│ (export "Foo_ctor" (func $Foo_ctor)) +//│ (elem declare func $Foo_ctor) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Foo (ref null any)) //│ (local $tmp (ref null any)) @@ -202,7 +204,7 @@ class Foo(val a, val b) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp -//│ (call $Foo +//│ (call $Foo_ctor //│ (ref.i31 //│ (i32.const 42)) //│ (ref.i31 diff --git a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls index b4ed1cf248..d9d83ef401 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls @@ -12,8 +12,8 @@ //│ (type $entry1 (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Unit$inst (mut (ref null $Unit)) (ref.null $Unit)) -//│ (start 1) -//│ (func (type $Unit_ctor) (result (ref null any)) +//│ (start $start1) +//│ (func $Unit_ctor1 (type $Unit_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -25,11 +25,13 @@ //│ (nop) //│ (return //│ (local.get $this)))) -//│ (func (type $start) +//│ (elem declare func $Unit_ctor1) +//│ (func $start1 (type $start) //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call 0))))) +//│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (global.get $Unit$inst)) //│ (export "entry" (func $entry)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls index d0459ea7f7..11e04a462e 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls @@ -20,8 +20,8 @@ Bar.y //│ (type $start (func)) //│ (global $Foo$inst (mut (ref null $Foo)) (ref.null $Foo)) //│ (global $Bar$inst (mut (ref null $Bar)) (ref.null $Bar)) -//│ (start 2) -//│ (func (type $Foo_ctor) (result (ref null any)) +//│ (start $start1) +//│ (func $Foo_ctor1 (type $Foo_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -39,7 +39,8 @@ Bar.y //│ (nop)) //│ (return //│ (local.get $this)))) -//│ (func (type $Bar_ctor) (result (ref null any)) +//│ (elem declare func $Foo_ctor1) +//│ (func $Bar_ctor1 (type $Bar_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this @@ -57,7 +58,8 @@ Bar.y //│ (nop)) //│ (return //│ (local.get $this)))) -//│ (func (type $start) +//│ (elem declare func $Bar_ctor1) +//│ (func $start1 (type $start) //│ (block //│ (global.set $Foo$inst //│ (ref.cast (ref null $Foo) @@ -65,6 +67,7 @@ Bar.y //│ (global.set $Bar$inst //│ (ref.cast (ref null $Bar) //│ (call 1))))) +//│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Bar (ref null any)) //│ (local $Foo (ref null any)) From 54dcfdf0c6bbc397b7d7e0895fd84c13be64dda3 Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 01:10:16 +0800 Subject: [PATCH 07/16] wasm: Emit `$$tag` instead of `FieldIdx(0)` --- .../src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala | 2 +- hkmc2/shared/src/test/mlscript/wasm/Basics.mls | 6 +++--- hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls | 2 +- hkmc2/shared/src/test/mlscript/wasm/Matching.mls | 4 ++-- hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls | 2 +- hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls | 2 +- hkmc2/shared/src/test/mlscript/wasm/Singletons.mls | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 22fe140438..e5e8672160 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -1100,7 +1100,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: Seq( local.set(thisVar, struct.new_default(typeref)), struct.set( - FieldIdx(NumIdx(0)), + FieldIdx(ctx.getTypeInfo_!(typeref).compType.asInstanceOf[StructType].fields(0)._2.id), ref.cast( local.get(thisVar, RefType.anyref), RefType(typeref, nullable = false), diff --git a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls index f859d76b9b..996f6c2304 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls @@ -126,7 +126,7 @@ class Foo(val a) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) -//│ (struct.set $Foo 0 +//│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) @@ -180,7 +180,7 @@ class Foo(val x) with //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) -//│ (struct.set $Foo 0 +//│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) @@ -239,7 +239,7 @@ O.y //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $O)) -//│ (struct.set $O 0 +//│ (struct.set $O $$tag //│ (ref.cast (ref $O) //│ (local.get $this)) //│ (i32.const 1)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index 5256d15409..0a4eff4e26 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -61,7 +61,7 @@ let i = 0 in //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Unit)) -//│ (struct.set $Unit 0 +//│ (struct.set $Unit $$tag //│ (ref.cast (ref $Unit) //│ (local.get $this)) //│ (i32.const 3)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls index ee364b6293..991291323a 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls @@ -54,7 +54,7 @@ if Bar(true) is //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Bar)) -//│ (struct.set $Bar 0 +//│ (struct.set $Bar $$tag //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (i32.const 1)) @@ -73,7 +73,7 @@ if Bar(true) is //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Baz)) -//│ (struct.set $Baz 0 +//│ (struct.set $Baz $$tag //│ (ref.cast (ref $Baz) //│ (local.get $this)) //│ (i32.const 2)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls index 113985457a..20f3f17a67 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls @@ -178,7 +178,7 @@ class Foo(val a, val b) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) -//│ (struct.set $Foo 0 +//│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls index d9d83ef401..b8241c5640 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls @@ -18,7 +18,7 @@ //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Unit)) -//│ (struct.set $Unit 0 +//│ (struct.set $Unit $$tag //│ (ref.cast (ref $Unit) //│ (local.get $this)) //│ (i32.const 1)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls index 11e04a462e..451da0c018 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls @@ -26,7 +26,7 @@ Bar.y //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) -//│ (struct.set $Foo 0 +//│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) @@ -45,7 +45,7 @@ Bar.y //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Bar)) -//│ (struct.set $Bar 0 +//│ (struct.set $Bar $$tag //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (i32.const 2)) From 7dfdf6ef9cfbcd504ad4b9c377cc88148850abde Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 16:32:22 +0800 Subject: [PATCH 08/16] wasm: Make symbolic index for function imports mandatory --- .../shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 2 +- .../src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala | 6 ++---- .../src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 5524c4ac6e..084955c029 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -374,7 +374,7 @@ class Ctx( functionImports += funcImport sym.foreach: namedFuncs(_) = numIdx - FuncIdx(funcImport.id.getOrElse(NumIdx(numIdx))) + FuncIdx(funcImport.id) /** Returns the cached function import for (`module`, `name`), creating it with `createImport` if needed. */ diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala index cc291d85b7..5bef9f69c4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala @@ -196,11 +196,9 @@ case class MemoryImport(module: Str, name: Str, minPages: Int) extends ToWat: doc"""(import "$module" "$name" (memory $minPages))""" /** A function import entry. */ -case class FuncImport(module: Str, name: Str, id: Opt[SymIdx], typeIdx: TypeIdx) extends ToWat: +case class FuncImport(module: Str, name: Str, id: SymIdx, typeIdx: TypeIdx) extends ToWat: def toWat: Document = - doc"""(import "$module" "$name" (func ${ - id.fold(doc"")(_.toWat) - } (type ${typeIdx.toWat})))""" + doc"""(import "$module" "$name" (func ${id.toWat} (type ${typeIdx.toWat})))""" /** A data segment entry. */ case class DataSegment(offsetExpr: Expr, bytes: Str) extends ToWat: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index e5e8672160..e7262aec97 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -259,7 +259,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: FuncImport( module = ExternIntrinsics.SystemModule, name = ExternIntrinsics.StringFromUtf16ImportName, - id = S(SymIdx(ExternIntrinsics.StringFromUtf16ImportName)), + id = SymIdx(ExternIntrinsics.StringFromUtf16ImportName), typeIdx = importTy, ) end getOrLoadStrCtorFunction From 17b4f56585c2bea82dbec485ee2725af14aca88c Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 16:43:10 +0800 Subject: [PATCH 09/16] wasm: Name imported memory --- .../src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 10 +++++++++- .../src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala | 4 ++-- .../scala/hkmc2/codegen/wasm/text/WatBuilder.scala | 2 +- hkmc2/shared/src/test/mlscript/wasm/Strings.mls | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 084955c029..ffba772d01 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -12,6 +12,7 @@ import semantics.*, Elaborator.State import text.Param as WasmParam import Instructions.* +import scala.annotation.nowarn import scala.collection.mutable.{ArrayBuffer as ArrayBuf, Map as MutMap} /** A Wasm function and its associated information. @@ -344,6 +345,7 @@ class Ctx( lastWords(s"Missing type definition for ${typeref.prettyString}") /** Returns the [[TypeInfo]] instance associated with the given `typeref`. */ + @nowarn("cat=deprecation") def getTypeInfo(typeref: TypeIdx | BlockMemberSymbol): Opt[TypeInfo] = typeref match case TypeIdx(NumIdx(idx)) => types.unapply(idx.toInt) case TypeIdx(SymIdx(nme)) => @@ -397,13 +399,18 @@ class Ctx( memoryImports(idx) = existing.copy(minPages = newMin) case N => val idx = memoryImports.size - memoryImports += MemoryImport(module, name, minPages) + memoryImports += MemoryImport(module, name, SymIdx(name), minPages) cachedMemoryImport(key) = idx /** Returns the minimum page requirement of memory import (`module`, `name`) if present. */ + @deprecated("Use `getMemoryImport` instead to get the full `MemoryImport` information.") def getMemoryImportMinPages(module: Str, name: Str): Opt[Int] = memoryImports.find(m => m.module === module && m.name === name).map(_.minPages) + /** Returns the memory import information for the given (`module`, `name`) tuple if present. */ + def getMemoryImport(module: Str, name: Str): Opt[MemoryImport] = + memoryImports.find(m => m.module === module && m.name === name) + /** Adds a data segment into this context. */ def addDataSegment(seg: DataSegment): Unit = dataSegments += seg @@ -440,6 +447,7 @@ class Ctx( lastWords(s"Missing function definition for ${funcref.prettyString}") /** Returns the [[FuncInfo]] instance associated with the given `funcref`. */ + @nowarn("cat=deprecation") def getFuncInfo(funcref: FuncIdx | Symbol): Opt[FuncInfo] = funcref match case FuncIdx(NumIdx(idx)) => funcInfosByIndex.get(idx).orElse: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala index 5bef9f69c4..b9290f8145 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala @@ -191,9 +191,9 @@ case class FieldIdx(idx: Index) extends CtxIdx(idx) case class TagIdx(idx: Index) extends CtxIdx(idx) /** A memory import entry. */ -case class MemoryImport(module: Str, name: Str, minPages: Int) extends ToWat: +case class MemoryImport(module: Str, name: Str, id: SymIdx, minPages: Int) extends ToWat: def toWat: Document = - doc"""(import "$module" "$name" (memory $minPages))""" + doc"""(import "$module" "$name" (memory ${id.toWat} $minPages))""" /** A function import entry. */ case class FuncImport(module: Str, name: Str, id: SymIdx, typeIdx: TypeIdx) extends ToWat: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index e7262aec97..0f4dd3bcce 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -1574,7 +1574,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ctx.addFunc(S(entrySym), entryFnInfo) val systemMemMinPages = - ctx.getMemoryImportMinPages(ExternIntrinsics.SystemModule, ExternIntrinsics.SystemMemoryImportName).getOrElse(0) + ctx.getMemoryImport(ExternIntrinsics.SystemModule, ExternIntrinsics.SystemMemoryImportName).fold(0)(_.minPages) (ctx.toWat, entryNme, systemMemMinPages) end program diff --git a/hkmc2/shared/src/test/mlscript/wasm/Strings.mls b/hkmc2/shared/src/test/mlscript/wasm/Strings.mls index d3620df5bb..768c650564 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Strings.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Strings.mls @@ -21,7 +21,7 @@ let dup2 = "dup" //│ (type $TupleArray (array (ref null any))) //│ (type $TupleArrayMut (array (mut (ref null any)))) //│ (type $entry1 (func (result (ref null any)))) -//│ (import "system" "mem" (memory 1)) +//│ (import "system" "mem" (memory $mem 1)) //│ (import "system" "mlx_str_from_utf16" (func $mlx_str_from_utf16 (type $mlx_str_from_utf16))) //│ (data (i32.const 0) "\64\00\75\00\70\00") //│ (func $entry (type $entry1) (result (ref null any)) From ed07049231b8ffbbd9b6ee080bc743fe48e5a3b3 Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 17:18:28 +0800 Subject: [PATCH 10/16] wasm: Use symbolic index for constructor invocation --- .../src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala | 2 +- hkmc2/shared/src/test/mlscript/wasm/Basics.mls | 2 +- hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls | 2 +- hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls | 2 +- hkmc2/shared/src/test/mlscript/wasm/Singletons.mls | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 0f4dd3bcce..5df299f29f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -136,7 +136,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: ) val ctorCall = call( - funcidx = ctx.getFunc_!(clsLikeDefn.sym, resolveSymIdx = true), + funcidx = ctx.getFunc_!(clsLikeDefn.sym), operands = Seq.empty, returnTypes = Seq(Result(RefType.anyref)), ) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls index 996f6c2304..080f8d0469 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls @@ -264,7 +264,7 @@ O.y //│ (block //│ (global.set $O$inst //│ (ref.cast (ref null $O) -//│ (call 0))))) +//│ (call $O_ctor1))))) //│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $O (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index 0a4eff4e26..5ccab493d6 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -73,7 +73,7 @@ let i = 0 in //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) -//│ (call 2))))) +//│ (call $Unit_ctor1))))) //│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $i (ref null any)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls index b8241c5640..df849c91bb 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls @@ -30,7 +30,7 @@ //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) -//│ (call 0))))) +//│ (call $Unit_ctor1))))) //│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (global.get $Unit$inst)) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls index 451da0c018..a072393320 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls @@ -63,10 +63,10 @@ Bar.y //│ (block //│ (global.set $Foo$inst //│ (ref.cast (ref null $Foo) -//│ (call 0))) +//│ (call $Foo_ctor1))) //│ (global.set $Bar$inst //│ (ref.cast (ref null $Bar) -//│ (call 1))))) +//│ (call $Bar_ctor1))))) //│ (elem declare func $start1) //│ (func $entry (type $entry1) (result (ref null any)) //│ (local $Bar (ref null any)) From 778b6bae81e2a0385e849d6b72f6d6b8e4573ae0 Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 17:20:03 +0800 Subject: [PATCH 11/16] wasm: Use monotonically increasing counter for object tags --- .../scala/hkmc2/codegen/wasm/text/Ctx.scala | 18 ++++++++-- .../hkmc2/codegen/wasm/text/WatBuilder.scala | 35 ++++++++++++------- .../src/test/mlscript/wasm/ControlFlow.mls | 2 +- .../src/test/mlscript/wasm/Matching.mls | 4 +-- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index ffba772d01..59cc596013 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -148,22 +148,25 @@ end GlobalInfo * Symbolic identifier for the function, or `N` if the function is anonymous. * @param compType * The composite type this type definition represents. + * @param objectTag + * An optional object tag number associated with this type. */ -class TypeInfo(val id: SymIdx, val compType: CompType) extends ToWat: +class TypeInfo(val id: SymIdx, val compType: CompType, val objectTag: Opt[Int]) extends ToWat: /** @param sym * The source [[BlockMemberSymbol]] which this type is generated from. * @param compType * The composite type this type definition represents. */ - def this(sym: BlockMemberSymbol, compType: CompType)(using Raise, Scope) = this( + def this(sym: BlockMemberSymbol, compType: CompType, objectTag: Opt[Int])(using Raise, Scope) = this( SymIdx(sym.optionIf(_.nameIsMeaningful).fold(summon[Scope].allocateName(sym))(_.nme)), compType, + objectTag, ) @deprecated("Consider providing a symbolic identifier by using `Scope.allocateName` with a `TempSymbol`.") def this(id: Opt[SymIdx], compType: CompType)(using Raise, Scope, State) = - this(id.getOrElse(SymIdx(summon[Scope].allocateName(TempSymbol(N, "")))), compType) + this(id.getOrElse(SymIdx(summon[Scope].allocateName(TempSymbol(N, "")))), compType, N) def toWat: Document = compType match case struct: StructType if struct.isSubtype => @@ -286,6 +289,9 @@ class Ctx( import Ctx.prettyString + /** Monotonically increasing counter for generating object tags. */ + private var objectTagNum = 0 + private val wasmIntrinsicFuncs: MutMap[Str, FuncIdx] = MutMap.empty private val wasmIntrinsicTypes: MutMap[WasmIntrinsicType, TypeIdx] = MutMap.empty private val wasmIntrinsicTags: MutMap[Str, TagIdx] = MutMap.empty @@ -310,6 +316,12 @@ class Ctx( labelTargets.collectFirst: case (sym, target) if sym eq label => target + /** Returns a new number to be used as an object tag. */ + def getFreshObjectTag(): Int = + val tag = objectTagNum + objectTagNum += 1 + tag ensuring objectTagNum > tag + /** Adds a type into this context. */ def addType(sym: Opt[BlockMemberSymbol], typeInfo: TypeInfo): TypeIdx = val numIdx = types.size diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 5df299f29f..e6b6874742 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -162,6 +162,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: typeInfo = TypeInfo( sym = defn.sym, compType = StructType(fields = allFields, parents = Seq(baseObjectTypeIdx), isSubtype = true), + objectTag = S(ctx.getFreshObjectTag()), ), ) end if @@ -203,6 +204,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: TypeInfo( id = SymIdx(symNme), FunctionType(params = Seq(WasmParam(N, RefType.anyref)), results = Seq.empty), + objectTag = S(ctx.getFreshObjectTag()), ), ), )), @@ -254,6 +256,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: params = Seq(WasmParam(N, RefType.anyref), WasmParam(N, RefType.anyref)), results = Seq(Result(RefType.anyref)), ), + objectTag = N, ), ) FuncImport( @@ -275,6 +278,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: TypeInfo( sym, ArrayType(elemType = RefType.anyref, mutable = mut), + objectTag = N, ), ) @@ -783,6 +787,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: params = params.map((_, nme) => WasmParam(S(nme), RefType.anyref)), results = Seq(Result(RefType.anyref)), ), + objectTag = N, ), ) val funcInfo = FuncInfo( @@ -1022,6 +1027,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: params = params.map(_._1), results = Seq.fill(bodyWat.resultTypes.length)(Result(RefType.anyref)), ), + objectTag = N, ), ) @@ -1081,6 +1087,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: // Use the symbolic type reference (e.g. `$Foo`) in emitted WAT for readability. // Numeric indices are only needed for `$tag` values. val typeref = ctx.getType_!(clsLikeDefn.sym) + val typeinfo = ctx.getTypeInfo_!(typeref) val (ctorParams, thisVar, ctorWat, ctorLocals) = setupCtorLocals(clsLikeDefn) @@ -1091,16 +1098,15 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: case Nil => (ctorAuxParams, Nil) case Some(_) => (ctorAuxParams, ctorParams) - val tagValue = ctx.getType_!(clsLikeDefn.sym, resolveSymIdx = true) match - case TypeIdx(NumIdx(idx)) => idx - case _ => lastWords(s"Expected numeric type index for class ${clsLikeDefn.sym}") + val tagValue = typeinfo.objectTag.getOrElse: + lastWords(s"Expected class ${clsLikeDefn.sym} to have an object tag") val ctorCode = blockInstr( label = N, Seq( local.set(thisVar, struct.new_default(typeref)), struct.set( - FieldIdx(ctx.getTypeInfo_!(typeref).compType.asInstanceOf[StructType].fields(0)._2.id), + FieldIdx(typeinfo.compType.asInstanceOf[StructType].fields(0)._2.id), ref.cast( local.get(thisVar, RefType.anyref), RefType(typeref, nullable = false), @@ -1133,6 +1139,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: params = ctorParams.map(p => WasmParam(S(p._2), RefType.anyref)), results = Seq(Result(RefType.anyref)), ), + objectTag = N, ), ) @@ -1342,14 +1349,11 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: Ls(msg"Could not resolve BlockMemberSymbol for class pattern" -> cls.toLoc), extraInfo = S(s"ClassLikeSymbol: ${cls.toString}"), )) - val clsTypeIdx = ctx.getType_!(clsBlkMemberSym, resolveSymIdx = true) + val clsTypeIdx = ctx.getType_!(clsBlkMemberSym) + val typeinfo = ctx.getTypeInfo_!(clsTypeIdx) - val expectedTag = clsTypeIdx match - case TypeIdx(NumIdx(idx)) => idx - case _ => break(errExpr( - Ls(msg"Expected numeric type index for class pattern" -> cls.toLoc), - extraInfo = S(s"TypeIdx: ${clsTypeIdx}"), - )) + val expectedTag = typeinfo.objectTag.getOrElse: + lastWords(s"Expected class $clsBlkMemberSym to have an object tag") val scrutExpr = getScrutExpr val isStructCompatible = ref.test(scrutExpr, baseObjectRefType(nullable = true)) @@ -1361,7 +1365,11 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: // Safe to cast and extract tag since ref.test passed val scrutAsObject = ref.cast(scrutExpr, baseObjectRefType(nullable = false)) - val scrutTag = struct.get(FieldIdx(NumIdx(0)), scrutAsObject, I32Type) + val scrutTag = struct.get( + FieldIdx(typeinfo.compType.asInstanceOf[StructType].fields(0)._2.id), + scrutAsObject, + I32Type, + ) val tagMatches = i32.eq(scrutTag, i32.const(expectedTag)) S(`if`( @@ -1501,6 +1509,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: Seq(tagFieldSym -> Field(I32Type, mutable = true, id = SymIdx("$tag"))), isSubtype = true, ), + objectTag = S(ctx.getFreshObjectTag() ensuring (_ == 0)), ), ) @@ -1523,6 +1532,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: TypeInfo( id = SymIdx(scope.allocateName(TempSymbol(N, entryNme))), FunctionType(params = Seq.empty, results = Seq(Result(RefType.anyref))), + objectTag = N, ), ) val entryFnInfo = FuncInfo( @@ -1549,6 +1559,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: TypeInfo( id = SymIdx(scope.allocateName(TempSymbol(N, "start"))), FunctionType(params = Seq.empty, results = Seq.empty), + objectTag = N, ), ) val initBody = blockInstr( diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index 5ccab493d6..049866facb 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -64,7 +64,7 @@ let i = 0 in //│ (struct.set $Unit $$tag //│ (ref.cast (ref $Unit) //│ (local.get $this)) -//│ (i32.const 3)) +//│ (i32.const 1)) //│ (nop) //│ (return //│ (local.get $this)))) diff --git a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls index 991291323a..d784485989 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls @@ -117,7 +117,7 @@ if Bar(true) is //│ (then //│ (if //│ (i32.eq -//│ (struct.get $Object 0 +//│ (struct.get $Object $$tag //│ (ref.cast (ref $Object) //│ (local.get $scrut))) //│ (i32.const 1)) @@ -159,7 +159,7 @@ if Bar(true) is //│ (then //│ (if //│ (i32.eq -//│ (struct.get $Object 0 +//│ (struct.get $Object $$tag //│ (ref.cast (ref $Object) //│ (local.get $scrut))) //│ (i32.const 2)) From 0d69b6e64a4824fc8494b2dd7ac0c04a3e4d843c Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 18:19:02 +0800 Subject: [PATCH 12/16] wasm: Use `Opt[Str]` for function exports We don't need multiple exports for now. --- .../scala/hkmc2/codegen/wasm/text/Ctx.scala | 17 +++++++---------- .../hkmc2/codegen/wasm/text/WatBuilder.scala | 8 ++++---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 59cc596013..53c97860e6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -32,7 +32,7 @@ import scala.collection.mutable.{ArrayBuffer as ArrayBuf, Map as MutMap} * @param body * The expression of the function body. * @param exports - * [[Seq]] of export names for the function. + * Optional export name for the function. */ class FuncInfo( val id: SymIdx, @@ -41,7 +41,7 @@ class FuncInfo( nResults: Int, locals: Seq[Local -> Str], val body: Expr, - val exports: Seq[Str], + val `export`: Opt[Str], ) extends ToWat: /** @param sym @@ -71,7 +71,7 @@ class FuncInfo( nResults, locals, body, - sym.optionIf(_.nameIsMeaningful).map(_.nme).toSeq, + sym.optionIf(_.nameIsMeaningful).map(_.nme), ) @deprecated("Consider providing a symbolic identifier by using `Scope.allocateName` with a `TempSymbol`.") @@ -82,7 +82,7 @@ class FuncInfo( nResults: Int, locals: Seq[Local -> Str], body: Expr, - exports: Seq[Str], + `export`: Opt[Str], )(using Raise, Scope, State) = this( id.getOrElse(SymIdx(summon[Scope].allocateName(TempSymbol(N, "")))), typeIdx, @@ -90,7 +90,7 @@ class FuncInfo( nResults, locals, body, - exports, + `export`, ) /** Returns the type of this function as a [[SignatureType]]. */ @@ -100,7 +100,6 @@ class FuncInfo( ) def toWat: Document = - // TODO(Derppening): Make exports configurable doc"""(func ${id.toWat} (type ${typeIdx.toWat})${ getSignatureType.toWat.surroundUnlessEmpty(doc" ") } #{ ${ @@ -108,10 +107,8 @@ class FuncInfo( doc"(local $$${p._2} ${RefType.anyref.toWat})" .mkDocument(doc" # ").surroundUnlessEmpty(doc" # ") } # ${body.toWat} #} )${ - exports - .map: e => - doc""" # (export "${e}" (func ${id.toWat}))""" - .mkDocument(doc"") + `export`.fold(doc""): e => + doc""" # (export "${e}" (func ${id.toWat}))""" } # (elem declare func ${id.toWat})""" end FuncInfo diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index e6b6874742..1ee1ce4dc5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -797,7 +797,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: nResults = 1, locals = Seq.empty, body = body, - exports = Seq.empty, + `export` = N, ) ctx.addFunc(N, funcInfo) end createIntrinsicFunc @@ -1158,7 +1158,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: nResults = ctorCode.resultTypes.length, locals = ctorLocals, body = ctorAux, - exports = ctorId.toSeq, + `export` = ctorId, ), ) if isSingletonObj then @@ -1543,7 +1543,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: // TODO(Derppening): Should we place top-level scope variables in the global section? locals = (entryFnLocals ++ entryExtraLocals).map(l => l -> scope.allocateOrGetName(l)), body = entryFnExpr, - exports = Seq(entryNme), + `export` = S(entryNme), ) ctx.popLocal() @@ -1576,7 +1576,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: nResults = 0, locals = Seq.empty, body = initBody, - exports = Seq.empty, + `export` = N, ), ) ctx.setStartFunc(initFn) From d0950a37ca65adce401c9f9e489375ac1e78064c Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 18:19:41 +0800 Subject: [PATCH 13/16] Remove deprecation for `TypeInfo` constructor with optional ID --- hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 53c97860e6..2395ab5cc9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -161,7 +161,6 @@ class TypeInfo(val id: SymIdx, val compType: CompType, val objectTag: Opt[Int]) objectTag, ) - @deprecated("Consider providing a symbolic identifier by using `Scope.allocateName` with a `TempSymbol`.") def this(id: Opt[SymIdx], compType: CompType)(using Raise, Scope, State) = this(id.getOrElse(SymIdx(summon[Scope].allocateName(TempSymbol(N, "")))), compType, N) From 87097c8e106b852938fefd0d0a170d6dc3c1e86a Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 18:20:56 +0800 Subject: [PATCH 14/16] wasm: Update `Ctx` documentation --- hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index 2395ab5cc9..f863f013b4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -285,7 +285,7 @@ class Ctx( import Ctx.prettyString - /** Monotonically increasing counter for generating object tags. */ + /** Counter for generating object tags. */ private var objectTagNum = 0 private val wasmIntrinsicFuncs: MutMap[Str, FuncIdx] = MutMap.empty From 4ef7968ea6ba818132d55b913a0a02e767b7c7c8 Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 18:21:38 +0800 Subject: [PATCH 15/16] wasm: Remove redundant tag number check --- hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index f863f013b4..edaae538a0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -316,7 +316,7 @@ class Ctx( def getFreshObjectTag(): Int = val tag = objectTagNum objectTagNum += 1 - tag ensuring objectTagNum > tag + tag /** Adds a type into this context. */ def addType(sym: Opt[BlockMemberSymbol], typeInfo: TypeInfo): TypeIdx = From b1185fc771143f16548ad426d8ad049caae01c8f Mon Sep 17 00:00:00 2001 From: David Mak Date: Sat, 14 Mar 2026 18:30:01 +0800 Subject: [PATCH 16/16] wasm: Fix typo --- hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index edaae538a0..bac8355032 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -462,7 +462,7 @@ class Ctx( val localIdx = idx.toInt - functionImports.size if localIdx < 0 then N else funcs.unapply(localIdx) case FuncIdx(SymIdx(nme)) => - // TODO(Derppening): Consider adding a `Map[SymIdx, TypeInfo]` for faster lookup + // TODO(Derppening): Consider adding a `Map[SymIdx, FuncInfo]` for faster lookup funcs.find(_.id.id == nme) case funcref: Symbol => namedFuncs.get(funcref).map(idx => funcs(idx))