8000 Merge the itables into the corresponding vtables. by sjrd · Pull Request #5154 · scala-js/scala-js · GitHub
[go: up one dir, main page]

Skip to content

Merge the itables into the corresponding vtables. #5154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
if (classInfo.hasRuntimeTypeInfo && !(clazz.kind.isClass && clazz.hasDirectInstances)) {
// Gen typeData -- for concrete Scala classes, we do it as part of the vtable generation instead
val typeDataFieldValues = genTypeDataFieldValues(clazz, Nil)
genTypeDataGlobal(clazz.className, genTypeID.typeData, typeDataFieldValues, Nil)
genTypeDataGlobal(clazz.className, genTypeID.typeData, typeDataFieldValues, Nil, Nil)
}

// Declare static fields
Expand Down Expand Up @@ -138,15 +138,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
}
}

/** Generate common itable global for all array classes. */
def genGlobalArrayClassItable()(implicit ctx: WasmContext): Unit = {
genGlobalClassItable(
genGlobalID.arrayClassITable, ctx.getClassInfo(ObjectClass),
List(SerializableClass, CloneableClass),
OriginalName(genGlobalID.arrayClassITable.toString())
)
}

private def genIsJSClassInstanceFunction(clazz: LinkedClass)(
implicit ctx: WasmContext): Option[wanme.FunctionID] = {
implicit val noPos: Position = Position.NoPosition
Expand Down Expand Up @@ -330,10 +321,11 @@ class ClassEmitter(coreSpec: CoreSpec) {
}

private def genTypeDataGlobal(className: ClassName, typeDataTypeID: wanme.TypeID,
typeDataFieldValues: List[wa.Instr], vtableElems: List[wa.RefFunc])(
typeDataFieldValues: List[wa.Instr], itableSlots: List[wa.Instr],
vtableElems: List[wa.RefFunc])(
implicit ctx: WasmContext): Unit = {
val instrs: List[wa.Instr] =
typeDataFieldValues ::: vtableElems ::: wa.StructNew(typeDataTypeID) :: Nil
typeDataFieldValues ::: itableSlots ::: vtableElems ::: wa.StructNew(typeDataTypeID) :: Nil
ctx.addGlobal(
wamod.Global(
genGlobalID.forVTable(className),
Expand All @@ -356,19 +348,17 @@ class ClassEmitter(coreSpec: CoreSpec) {

val isAbstractClass = !clazz.hasDirectInstances

// Generate the vtable and itable for concrete classes
// Generate the vtable for concrete classes
if (!isAbstractClass) {
// Generate an actual vtable, which we integrate into the typeData
val reflectiveProxies =
classInfo.resolvedMethodInfos.valuesIterator.filter(_.methodName.isReflectiveProxy).toList
val typeDataFieldValues = genTypeDataFieldValues(clazz, reflectiveProxies)
val itableSlots = genItableSlots(classInfo, clazz.ancestors)
val vtableElems = classInfo.tableEntries.map { methodName =>
wa.RefFunc(classInfo.resolvedMethodInfos(methodName).tableEntryID)
}
genTypeDataGlobal(className, vtableTypeID, typeDataFieldValues, vtableElems)

// Generate the itable
genGlobalClassItable(clazz)
genTypeDataGlobal(className, vtableTypeID, typeDataFieldValues, itableSlots, vtableElems)
}

// Declare the struct type for the class
Expand All @@ -378,12 +368,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
watpe.RefType(vtableTypeID),
isMutable = false
)
val itablesField = watpe.StructField(
genFieldID.objStruct.itables,
itablesOriginalName,
watpe.RefType(genTypeID.itables),
isMutable = false
)
val fields = classInfo.allFieldDefs.map { field =>
watpe.StructField(
genFieldID.forClassInstanceField(field.name.name),
Expand All @@ -405,7 +389,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
}
val structTypeID = genTypeID.forClass(className)
val superType = clazz.superClass.map(s => genTypeID.forClass(s.name))
val structType = watpe.StructType(vtableField :: itablesField :: fields ::: jlClassDataField)
val structType = watpe.StructType(vtableField :: fields ::: jlClassDataField)
val subType = watpe.SubType(
structTypeID,
makeDebugName(ns.ClassInstance, className),
Expand Down Expand Up @@ -461,6 +445,14 @@ class ClassEmitter(coreSpec: CoreSpec) {
implicit ctx: WasmContext): wanme.TypeID = {
val className = classInfo.name
val typeID = genTypeID.forVTable(className)
val itableSlotFields = (0 until ctx.itablesLength).map { i =>
watpe.StructField(
genFieldID.vtableStruct.itableSlot(i),
OriginalName.NoOriginalName,
watpe.RefType.nullable(watpe.HeapType.Struct),
isMutable = false
)
}.toList
val vtableFields =
classInfo.tableEntries.map { methodName =>
watpe.StructField(
Expand All @@ -474,7 +466,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
case None => genTypeID.typeData
case Some(s) => genTypeID.forVTable(s.name)
}
val structType = watpe.StructType(ctx.coreLib.typeDataStructFields ::: vtableFields)
val structType = watpe.StructType(ctx.coreLib.typeDataStructFields ::: itableSlotFields ::: vtableFields)
val subType = watpe.SubType(
typeID,
makeDebugName(ns.VTable, className),
Expand Down Expand Up @@ -525,10 +517,10 @@ class ClassEmitter(coreSpec: CoreSpec) {
/* Test whether the itable at the target interface's slot is indeed an
* instance of that interface's itable struct type.
*/
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.itables)
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.vtable)
fb += wa.StructGet(
genTypeID.itables,
genFieldID.itablesStruct.itableSlot(classInfo.itableIdx)
genTypeID.ObjectVTable,
genFieldID.vtableStruct.itableSlot(classInfo.itableIdx)
)
fb += wa.RefTest(watpe.RefType(genTypeID.forITable(className)))
fb += wa.Return
Expand Down Expand Up @@ -642,12 +634,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
fb.setResultType(watpe.RefType(structTypeID))

fb += wa.GlobalGet(genGlobalID.forVTable(className))

if (classInfo.classImplementsAnyInterface)
fb += wa.GlobalGet(genGlobalID.forITable(className))
else
fb += wa.GlobalGet(genGlobalID.emptyITable)

classInfo.allFieldDefs.foreach { f =>
fb += genZeroOf(f.ftpe)
}
Expand Down Expand Up @@ -689,9 +675,8 @@ class ClassEmitter(coreSpec: CoreSpec) {
fb += wa.RefCast(structRefType)
fb += wa.LocalSet(fromTypedLocal)

// Push vtable and itables on the stack (there is at least Cloneable in the itables)
// Push the vtable on the stack
fb += wa.GlobalGet(genGlobalID.forVTable(className))
fb += wa.GlobalGet(genGlobalID.forITable(className))

// Push every field of `fromTyped` on the stack
info.allFieldDefs.foreach { field =>
Expand Down Expand Up @@ -822,55 +807,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
fb.buildAndAddToModule()
}

/** Generates the global instance of the class itable.
*
* If the class implements no interface at all, we skip this step. Instead,
* we will use the unique `emptyITable` as itable for this class.
*/
private def genGlobalClassItable(clazz: LinkedClass)(implicit ctx: WasmContext): Unit = {
val className = clazz.className
val classInfo = ctx.getClassInfo(className)
if (classInfo.classImplementsAnyInterface) {
genGlobalClassItable(
genGlobalID.forITable(className),
classInfo,
clazz.ancestors,
makeDebugName(ns.ITable, classInfo.name)
)
}
}

private def genGlobalClassItable(classITableGlobalID: wanme.GlobalID,
classInfoForResolving: WasmContext.ClassInfo, ancestors: List[ClassName],
originalName: OriginalName)(
implicit ctx: WasmContext): Unit = {
val itablesInit = Array.fill[List[wa.Instr]](ctx.itablesLength) {
List(wa.RefNull(watpe.HeapType.Struct))
}
val resolvedMethodInfos = classInfoForResolving.resolvedMethodInfos

for {
ancestor <- ancestors
// Use getClassInfoOption in case the reachability analysis got rid of those interfaces
interfaceInfo <- ctx.getClassInfoOption(ancestor)
if interfaceInfo.isInterface
} {
val init = interfaceInfo.tableEntries.map { method =>
wa.RefFunc(resolvedMethodInfos(method).tableEntryID)
} :+ wa.StructNew(genTypeID.forITable(ancestor))
itablesInit(interfaceInfo.itableIdx) = init
}

val global = wamod.Global(
classITableGlobalID,
originalName,
isMutable = false,
watpe.RefType(genTypeID.itables),
wa.Expr(itablesInit.flatten.toList :+ wa.StructNew(genTypeID.itables))
)
ctx.addGlobal(global)
}

private def genInterface(clazz: LinkedClass)(implicit ctx: WasmContext): Unit = {
assert(clazz.kind == ClassKind.Interface)
// gen itable type
Expand Down Expand Up @@ -1563,5 +1499,36 @@ object ClassEmitter {

private val thisOriginalName: OriginalName = OriginalName("this")
private val vtableOriginalName: OriginalName = OriginalName("vtable")
private val itablesOriginalName: OriginalName = OriginalName("itables")

/** Generates the itable slots of a class.
*
* @param classInfoForResolving
* The `ClassInfo` from which to resolve methods. This is normally the
* class info of the class for which we are generating the itable slots.
* For the itable slots of array classes, it must be the info of `jl.Object`.
* @param ancestors
* The list of ancestors of the target class.
*/
def genItableSlots(classInfoForResolving: WasmContext.ClassInfo,
ancestors: List[ClassName])(
implicit ctx: WasmContext): List[wa.Instr] = {
val itablesInit = Array.fill[List[wa.Instr]](ctx.itablesLength) {
List(wa.RefNull(watpe.HeapType.Struct))
}
val resolvedMethodInfos = classInfoForResolving.resolvedMethodInfos

for {
ancestor <- ancestors
// Use getClassInfoOption in case the reachability analysis got rid of those interfaces
interfaceInfo <- ctx.getClassInfoOption(ancestor)
if interfaceInfo.isInterface
} {
val init = interfaceInfo.tableEntries.map { method =>
wa.RefFunc(resolvedMethodInfos(method).tableEntryID)
} :+ wa.StructNew(genTypeID.forITable(ancestor))
itablesInit(interfaceInfo.itableIdx) = init
}

itablesInit.flatten.toList
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {

genImports()

genEmptyITable()
genPrimitiveTypeDataGlobals()

genHelperDefinitions()
Expand Down Expand Up @@ -175,20 +174,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
ArrayType(FieldType(RefType(genTypeID.typeData), isMutable = false))
)

genCoreType(
genTypeID.itables,
StructType(
(0 until ctx.itablesLength).map { i =>
StructField(
genFieldID.itablesStruct.itableSlot(i),
OriginalName.NoOriginalName,
RefType.nullable(HeapType.Struct),
isMutable = false
)
}.toList
)
)

genCoreType(
genTypeID.reflectiveProxies,
ArrayType(FieldType(RefType(genTypeID.reflectiveProxy), isMutable = false))
Expand Down Expand Up @@ -234,12 +219,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
RefType(vtableTypeID),
isMutable = false
)
val itablesField = StructField(
genFieldID.objStruct.itables,
OriginalName(genFieldID.objStruct.itables.toString()),
RefType(genTypeID.itables),
isMutable = false
)

val typeRefsWithArrays: List[(TypeID, TypeID)] =
List(
Expand All @@ -266,7 +245,7 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {

val superType = genTypeID.ObjectStruct
val structType = StructType(
List(vtableField, itablesField, underlyingArrayField)
List(vtableField, underlyingArrayField)
)
val subType = SubType(structTypeID, origName, isFinal = true, Some(superType), structType)
ctx.mainRecType.addSubType(subType)
Expand Down Expand Up @@ -431,18 +410,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {

// --- Global definitions ---

private def genEmptyITable()(implicit ctx: WasmContext): Unit = {
ctx.addGlobal(
Global(
genGlobalID.emptyITable,
OriginalName(genGlobalID.emptyITable.toString()),
isMutable = false,
RefType(genTypeID.itables),
Expr(List(StructNewDefault(genTypeID.itables)))
)
)
}

private def genPrimitiveTypeDataGlobals()(implicit ctx: WasmContext): Unit = {
import genFieldID.typeData._

Expand Down Expand Up @@ -498,7 +465,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
val boxStruct = genTypeID.forClass(boxClassName)
val instrs: List[Instr] = List(
GlobalGet(genGlobalID.forVTable(boxClassName)),
GlobalGet(genGlobalID.forITable(boxClassName)),
zeroValueInstr,
StructNew(boxStruct)
)
Expand Down Expand Up @@ -1566,7 +1532,11 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
// reflectiveProxies, empty since all methods of array classes exist in jl.Object
fb += ArrayNewFixed(genTypeID.reflectiveProxies, 0)

// itable slots
val objectClassInfo = ctx.getClassInfo(ObjectClass)
fb ++= ClassEmitter.genItableSlots(objectClassInfo, List(SerializableClass, CloneableClass))

// vtable items
fb ++= objectClassInfo.tableEntries.map { methodName =>
ctx.refFuncWithDeclaration(objectClassInfo.resolvedMethodInfos(methodName).tableEntryID)
}
Expand Down Expand Up @@ -2256,17 +2226,16 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
fb += StructGet(genTypeID.ClassStruct, genFieldID.classData)
fb += LocalTee(componentTypeDataLocal)

// Load the vtable and itables of the ArrayClass instance we will create
// Load the vtable of the ArrayClass instance we will create
fb += I32Const(1)
fb += Call(genFunctionID.arrayTypeData) // vtable
fb += GlobalGet(genGlobalID.arrayClassITable) // itables
fb += Call(genFunctionID.arrayTypeData)

// Load the length
fb += LocalGet(lengthParam)

// switch (componentTypeData.kind)
val switchClauseSig = FunctionType(
List(arrayTypeDataType, RefType(genTypeID.itables), Int32),
List(arrayTypeDataType, Int32),
List(RefType(genTypeID.ObjectStruct))
)
fb.switch(switchClauseSig) { () =>
Expand Down Expand Up @@ -2807,7 +2776,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
// Build the result arrayStruct
fb += LocalGet(fromLocal)
fb += StructGet(arrayStructTypeID, genFieldID.objStruct.vtable) // vtable
fb += GlobalGet(genGlobalID.arrayClassITable) // itable
fb += LocalGet(resultUnderlyingLocal)
fb += StructNew(arrayStructTypeID)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ final class Emitter(config: Emitter.Config) {
coreLib.genPreClasses()
sortedClasses.foreach(classEmitter.genClassDef(_))
topLevelExports.foreach(classEmitter.genTopLevelExport(_))
classEmitter.genGlobalArrayClassItable()
coreLib.genPostClasses()

genStartFunction(sortedClasses, moduleInitializers, topLevelExports)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1190,10 +1190,10 @@ private class FunctionEmitter private (
// Generates an itable-based dispatch.
def genITableDispatch(): Unit = {
fb += wa.LocalGet(receiverLocalForDispatch)
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.itables)
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.vtable)
fb += wa.StructGet(
genTypeID.itables,
genFieldID.itablesStruct.itableSlot(receiverClassInfo.itableIdx)
genTypeID.ObjectVTable,
genFieldID.vtableStruct.itableSlot(receiverClassInfo.itableIdx)
)
fb += wa.RefCast(watpe.RefType(genTypeID.forITable(receiverClassInfo.name)))
fb += wa.StructGet(
Expand Down Expand Up @@ -2722,7 +2722,6 @@ private class FunctionEmitter private (

fb += wa.LocalSet(primLocal)
fb += wa.GlobalGet(genGlobalID.forVTable(boxClassName))
fb += wa.GlobalGet(genGlobalID.forITable(boxClassName))
fb += wa.LocalGet(primLocal)
fb += wa.StructNew(genTypeID.forClass(boxClassName))

Expand Down Expand Up @@ -3001,7 +3000,7 @@ private class FunctionEmitter private (

markPosition(tree)

genLoadVTableAndITableForArray(fb, arrayTypeRef)
genLoadArrayTypeData(fb, arrayTypeRef) // vtable

// Create the underlying array
genTree(length, IntType)
Expand Down
Loading
0