10000 Merge pull request #5154 from sjrd/fuse-itables-vtable · scala-js/scala-js@e507300 · GitHub
[go: up one dir, main page]

Skip to content

Commit e507300

Browse files
authored
Merge pull request #5154 from sjrd/fuse-itables-vtable
Merge the itables into the corresponding vtables.
2 parents 95d04c6 + 1b989f2 commit e507300

File tree

9 files changed

+97
-183
lines changed

9 files changed

+97
-183
lines changed

linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala

Lines changed: 53 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
4949
if (classInfo.hasRuntimeTypeInfo && !(clazz.kind.isClass && clazz.hasDirectInstances)) {
5050
// Gen typeData -- for concrete Scala classes, we do it as part of the vtable generation instead
5151
val typeDataFieldValues = genTypeDataFieldValues(clazz, Nil)
52-
genTypeDataGlobal(clazz.className, genTypeID.typeData, typeDataFieldValues, Nil)
52+
genTypeDataGlobal(clazz.className, genTypeID.typeData, typeDataFieldValues, Nil, Nil)
5353
}
5454

5555
// Declare static fields
@@ -138,15 +138,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
138138
}
139139
}
140140

141-
/** Generate common itable global for all array classes. */
142-
def genGlobalArrayClassItable()(implicit ctx: WasmContext): Unit = {
143-
genGlobalClassItable(
144-
genGlobalID.arrayClassITable, ctx.getClassInfo(ObjectClass),
145-
List(SerializableClass, CloneableClass),
146-
OriginalName(genGlobalID.arrayClassITable.toString())
147-
)
148-
}
149-
150141
private def genIsJSClassInstanceFunction(clazz: LinkedClass)(
151142
implicit ctx: WasmContext): Option[wanme.FunctionID] = {
152143
implicit val noPos: Position = Position.NoPosition
@@ -330,10 +321,11 @@ class ClassEmitter(coreSpec: CoreSpec) {
330321
}
331322

332323
private def genTypeDataGlobal(className: ClassName, typeDataTypeID: wanme.TypeID,
333-
typeDataFieldValues: List[wa.Instr], vtableElems: List[wa.RefFunc])(
324+
typeDataFieldValues: List[wa.Instr], itableSlots: List[wa.Instr],
325+
vtableElems: List[wa.RefFunc])(
334326
implicit ctx: WasmContext): Unit = {
335327
val instrs: List[wa.Instr] =
336-
typeDataFieldValues ::: vtableElems ::: wa.StructNew(typeDataTypeID) :: Nil
328+
typeDataFieldValues ::: itableSlots ::: vtableElems ::: wa.StructNew(typeDataTypeID) :: Nil
337329
ctx.addGlobal(
338330
wamod.Global(
339331
genGlobalID.forVTable(className),
@@ -356,19 +348,17 @@ class ClassEmitter(coreSpec: CoreSpec) {
356348

357349
val isAbstractClass = !clazz.hasDirectInstances
358350

359-
// Generate the vtable and itable for concrete classes
351+
// Generate the vtable for concrete classes
360352
if (!isAbstractClass) {
361353
// Generate an actual vtable, which we integrate into the typeData
362354
val reflectiveProxies =
363355
classInfo.resolvedMethodInfos.valuesIterator.filter(_.methodName.isReflectiveProxy).toList
364356
val typeDataFieldValues = genTypeDataFieldValues(clazz, reflectiveProxies)
357+
val itableSlots = genItableSlots(classInfo, clazz.ancestors)
365358
val vtableElems = classInfo.tableEntries.map { methodName =>
366359
wa.RefFunc(classInfo.resolvedMethodInfos(methodName).tableEntryID)
367360
}
368-
genTypeDataGlobal(className, vtableTypeID, typeDataFieldValues, vtableElems)
369-
370-
// Generate the itable
371-
genGlobalClassItable(clazz)
361+
genTypeDataGlobal(className, vtableTypeID, typeDataFieldValues, itableSlots, vtableElems)
372362
}
373363

374364
// Declare the struct type for the class
@@ -378,12 +368,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
378368
watpe.RefType(vtableTypeID),
379369
isMutable = false
380370
)
381-
val itablesField = watpe.StructField(
382-
genFieldID.objStruct.itables,
383-
itablesOriginalName,
384-
watpe.RefType(genTypeID.itables),
385-
isMutable = false
386-
)
387371
val fields = classInfo.allFieldDefs.map { field =>
388372
watpe.StructField(
389373
genFieldID.forClassInstanceField(field.name.name),
@@ -405,7 +389,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
405389
}
406390
val structTypeID = genTypeID.forClass(className)
407391
val superType = clazz.superClass.map(s => genTypeID.forClass(s.name))
408-
val structType = watpe.StructType(vtableField :: itablesField :: fields ::: jlClassDataField)
392+
val structType = watpe.StructType(vtableField :: fields ::: jlClassDataField)
409393
val subType = watpe.SubType(
410394
structTypeID,
411395
makeDebugName(ns.ClassInstance, className),
@@ -461,6 +445,14 @@ class ClassEmitter(coreSpec: CoreSpec) {
461445
implicit ctx: WasmContext): wanme.TypeID = {
462446
val className = classInfo.name
463447
val typeID = genTypeID.forVTable(className)
448+
val itableSlotFields = (0 until ctx.itablesLength).map { i =>
449+
watpe.StructField(
450+
genFieldID.vtableStruct.itableSlot(i),
451+
OriginalName.NoOriginalName,
452+
watpe.RefType.nullable(watpe.HeapType.Struct),
453+
isMutable = false
454+
)
455+
}.toList
464456
val vtableFields =
465457
classInfo.tableEntries.map { methodName =>
466458
watpe.StructField(
@@ -474,7 +466,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
474466
case None => genTypeID.typeData
475467
case Some(s) => genTypeID.forVTable(s.name)
476468
}
477-
val structType = watpe.StructType(ctx.coreLib.typeDataStructFields ::: vtableFields)
469+
val structType = watpe.StructType(ctx.coreLib.typeDataStructFields ::: itableSlotFields ::: vtableFields)
478470
val subType = watpe.SubType(
479471
typeID,
480472
makeDebugName(ns.VTable, className),
@@ -525,10 +517,10 @@ class ClassEmitter(coreSpec: CoreSpec) {
525517
/* Test whether the itable at the target interface's slot is indeed an
526518
* instance of that interface's itable struct type.
527519
*/
528-
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.itables)
520+
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.vtable)
529521
fb += wa.StructGet(
530-
genTypeID.itables,
531-
genFieldID.itablesStruct.itableSlot(classInfo.itableIdx)
522+
genTypeID.ObjectVTable,
523+
genFieldID.vtableStruct.itableSlot(classInfo.itableIdx)
532524
)
533525
fb += wa.RefTest(watpe.RefType(genTypeID.forITable(className)))
534526
fb += wa.Return
@@ -642,12 +634,6 @@ class ClassEmitter(coreSpec: CoreSpec) {
642634
fb.setResultType(watpe.RefType(structTypeID))
643635

644636
fb += wa.GlobalGet(genGlobalID.forVTable(className))
645-
646-
if (classInfo.classImplementsAnyInterface)
647-
fb += wa.GlobalGet(genGlobalID.forITable(className))
648-
else
649-
fb += wa.GlobalGet(genGlobalID.emptyITable)
650-
651637
classInfo.allFieldDefs.foreach { f =>
652638
fb += genZeroOf(f.ftpe)
653639
}
@@ -689,9 +675,8 @@ class ClassEmitter(coreSpec: CoreSpec) {
689675
fb += wa.RefCast(structRefType)
690676
fb += wa.LocalSet(fromTypedLocal)
691677

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

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

825-
/** Generates the global instance of the class itable.
826-
*
827-
* If the class implements no interface at all, we skip this step. Instead,
828-
* we will use the unique `emptyITable` as itable for this class.
829-
*/
830-
private def genGlobalClassItable(clazz: LinkedClass)(implicit ctx: WasmContext): Unit = {
831-
val className = clazz.className
832-
val classInfo = ctx.getClassInfo(className)
833-
if (classInfo.classImplementsAnyInterface) {
834-
genGlobalClassItable(
835-
genGlobalID.forITable(className),
836-
classInfo,
837-
clazz.ancestors,
838-
makeDebugName(ns.ITable, classInfo.name)
839-
)
840-
}
841-
}
842-
843-
private def genGlobalClassItable(classITableGlobalID: wanme.GlobalID,
844-
classInfoForResolving: WasmContext.ClassInfo, ancestors: List[ClassName],
845-
originalName: OriginalName)(
846-
implicit ctx: WasmContext): Unit = {
847-
val itablesInit = Array.fill[List[wa.Instr]](ctx.itablesLength) {
848-
List(wa.RefNull(watpe.HeapType.Struct))
849-
}
850-
val resolvedMethodInfos = classInfoForResolving.resolvedMethodInfos
851-
852-
for {
853-
ancestor <- ancestors
854-
// Use getClassInfoOption in case the reachability analysis got rid of those interfaces
855-
interfaceInfo <- ctx.getClassInfoOption(ancestor)
856-
if interfaceInfo.isInterface
857-
} {
858-
val init = interfaceInfo.tableEntries.map { method =>
859-
wa.RefFunc(resolvedMethodInfos(method).tableEntryID)
860-
} :+ wa.StructNew(genTypeID.forITable(ancestor))
861-
itablesInit(interfaceInfo.itableIdx) = init
862-
}
863-
864-
val global = wamod.Global(
865-
classITableGlobalID,
866-
originalName,
867-
isMutable = false,
868-
watpe.RefType(genTypeID.itables),
869-
wa.Expr(itablesInit.flatten.toList :+ wa.StructNew(genTypeID.itables))
870-
)
871-
ctx.addGlobal(global)
872-
}
873-
874810
private def genInterface(clazz: LinkedClass)(implicit ctx: WasmContext): Unit = {
875811
assert(clazz.kind == ClassKind.Interface)
876812
// gen itable type
@@ -1563,5 +1499,36 @@ object ClassEmitter {
15631499

15641500
private val thisOriginalName: OriginalName = OriginalName("this")
15651501
private val vtableOriginalName: OriginalName = OriginalName("vtable")
1566-
private val itablesOriginalName: OriginalName = OriginalName("itables")
1502+
1503+
/** Generates the itable slots of a class.
1504+
*
1505+
* @param classInfoForResolving
1506+
* The `ClassInfo` from which to resolve methods. This is normally the
1507+
* class info of the class for which we are generating the itable slots.
1508+
* For the itable slots of array classes, it must be the info of `jl.Object`.
1509+
* @param ancestors
1510+
* The list of ancestors of the target class.
1511+
*/
1512+
def genItableSlots(classInfoForResolving: WasmContext.ClassInfo,
1513+
ancestors: List[ClassName])(
1514+
implicit ctx: WasmContext): List[wa.Instr] = {
1515+
val itablesInit = Array.fill[List[wa.Instr]](ctx.itablesLength) {
1516+
List(wa.RefNull(watpe.HeapType.Struct))
1517+
}
1518+
val resolvedMethodInfos = classInfoForResolving.resolvedMethodInfos
1519+
1520+
for {
1521+
ancestor <- ancestors
1522+
// Use getClassInfoOption in case the reachability analysis got rid of those interfaces
1523+
interfaceInfo <- ctx.getClassInfoOption(ancestor)
1524+
if interfaceInfo.isInterface
1525+
} {
1526+
val init = interfaceInfo.tableEntries.map { method =>
1527+
wa.RefFunc(resolvedMethodInfos(method).tableEntryID)
1528+
} :+ wa.StructNew(genTypeID.forITable(ancestor))
1529+
itablesInit(interfaceInfo.itableIdx) = init
1530+
}
1531+
1532+
itablesInit.flatten.toList
1533+
}
15671534
}

linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
117117

118118
genImports()
119119

120-
genEmptyITable()
121120
genPrimitiveTypeDataGlobals()
122121

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

178-
genCoreType(
179-
genTypeID.itables,
180-
StructType(
181-
(0 until ctx.itablesLength).map { i =>
182-
StructField(
183-
genFieldID.itablesStruct.itableSlot(i),
184-
OriginalName.NoOriginalName,
185-
RefType.nullable(HeapType.Struct),
186-
isMutable = false
187-
)
188-
}.toList
189-
)
190-
)
191-
192177
genCoreType(
193178
genTypeID.reflectiveProxies,
194179
ArrayType(FieldType(RefType(genTypeID.reflectiveProxy), isMutable = false))
@@ -234,12 +219,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
234219
RefType(vtableTypeID),
235220
isMutable = false
236221
)
237-
val itablesField = StructField(
238-
genFieldID.objStruct.itables,
239-
OriginalName(genFieldID.objStruct.itables.toString()),
240-
RefType(genTypeID.itables),
241-
isMutable = false
242-
)
243222

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

267246
val superType = genTypeID.ObjectStruct
268247
val structType = StructType(
269-
List(vtableField, itablesField, underlyingArrayField)
248+
List(vtableField, underlyingArrayField)
270249
)
271250
val subType = SubType(structTypeID, origName, isFinal = true, Some(superType), structType)
272251
ctx.mainRecType.addSubType(subType)
@@ -429,18 +408,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
429408

430409
// --- Global definitions ---
431410

432-
private def genEmptyITable()(implicit ctx: WasmContext): Unit = {
433-
ctx.addGlobal(
434-
Global(
435-
genGlobalID.emptyITable,
436-
OriginalName(genGlobalID.emptyITable.toString()),
437-
isMutable = false,
438-
RefType(genTypeID.itables),
439-
Expr(List(StructNewDefault(genTypeID.itables)))
440-
)
441-
)
442-
}
443-
444411
private def genPrimitiveTypeDataGlobals()(implicit ctx: WasmContext): Unit = {
445412
import genFieldID.typeData._
446413

@@ -496,7 +463,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
496463
val boxStruct = genTypeID.forClass(boxClassName)
497464
val instrs: List[Instr] = List(
498465
GlobalGet(genGlobalID.forVTable(boxClassName)),
499-
GlobalGet(genGlobalID.forITable(boxClassName)),
500466
zeroValueInstr,
501467
StructNew(boxStruct)
502468
)
@@ -1564,7 +1530,11 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
15641530
// reflectiveProxies, empty since all methods of array classes exist in jl.Object
15651531
fb += ArrayNewFixed(genTypeID.reflectiveProxies, 0)
15661532

1533+
// itable slots
15671534
val objectClassInfo = ctx.getClassInfo(ObjectClass)
1535+
fb ++= ClassEmitter.genItableSlots(objectClassInfo, List(SerializableClass, CloneableClass))
1536+
1537+
// vtable items
15681538
fb ++= objectClassInfo.tableEntries.map { methodName =>
15691539
ctx.refFuncWithDeclaration(objectClassInfo.resolvedMethodInfos(methodName).tableEntryID)
15701540
}
@@ -2254,17 +2224,16 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
22542224
fb += StructGet(genTypeID.ClassStruct, genFieldID.classData)
22552225
fb += LocalTee(componentTypeDataLocal)
22562226

2257-
// Load the vtable and itables of the ArrayClass instance we will create
2227+
// Load the vtable of the ArrayClass instance we will create
22582228
fb += I32Const(1)
2259-
fb += Call(genFunctionID.arrayTypeData) // vtable
2260-
fb += GlobalGet(genGlobalID.arrayClassITable) // itables
2229+
fb += Call(genFunctionID.arrayTypeData)
22612230

22622231
// Load the length
22632232
fb += LocalGet(lengthParam)
22642233

22652234
// switch (componentTypeData.kind)
22662235
val switchClauseSig = FunctionType(
2267-
List(arrayTypeDataType, RefType(genTypeID.itables), Int32),
2236+
List(arrayTypeDataType, Int32),
22682237
List(RefType(genTypeID.ObjectStruct))
22692238
)
22702239
fb.switch(switchClauseSig) { () =>
@@ -2805,7 +2774,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
28052774
// Build the result arrayStruct
28062775
fb += LocalGet(fromLocal)
28072776
fb += StructGet(arrayStructTypeID, genFieldID.objStruct.vtable) // vtable
2808-
fb += GlobalGet(genGlobalID.arrayClassITable) // itable
28092777
fb += LocalGet(resultUnderlyingLocal)
28102778
fb += StructNew(arrayStructTypeID)
28112779

linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ final class Emitter(config: Emitter.Config) {
9090
coreLib.genPreClasses()
9191
sortedClasses.foreach(classEmitter.genClassDef(_))
9292
topLevelExports.foreach(classEmitter.genTopLevelExport(_))
93-
classEmitter.genGlobalArrayClassItable()
9493
coreLib.genPostClasses()
9594

9695
genStartFunction(sortedClasses, moduleInitializers, topLevelExports)

linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,10 +1190,10 @@ private class FunctionEmitter private (
11901190
// Generates an itable-based dispatch.
11911191
def genITableDispatch(): Unit = {
11921192
fb += wa.LocalGet(receiverLocalForDispatch)
1193-
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.itables)
1193+
fb += wa.StructGet(genTypeID.ObjectStruct, genFieldID.objStruct.vtable)
11941194
fb += wa.StructGet(
1195-
genTypeID.itables,
1196-
genFieldID.itablesStruct.itableSlot(receiverClassInfo.itableIdx)
1195+
genTypeID.ObjectVTable,
1196+
genFieldID.vtableStruct.itableSlot(receiverClassInfo.itableIdx)
11971197
)
11981198
fb += wa.RefCast(watpe.RefType(genTypeID.forITable(receiverClassInfo.name)))
11991199
fb += wa.StructGet(
@@ -2711,7 +2711,6 @@ private class FunctionEmitter private (
27112711

27122712
fb += wa.LocalSet(primLocal)
27132713
fb += wa.GlobalGet(genGlobalID.forVTable(boxClassName))
2714-
fb += wa.GlobalGet(genGlobalID.forITable(boxClassName))
27152714
fb += wa.LocalGet(primLocal)
27162715
fb += wa.StructNew(genTypeID.forClass(boxClassName))
27172716

@@ -2990,7 +2989,7 @@ private class FunctionEmitter private (
29902989

29912990
markPosition(tree)
29922991

2993-
genLoadVTableAndITableForArray(fb, arrayTypeRef)
2992+
genLoadArrayTypeData(fb, arrayTypeRef) // vtable
29942993

29952994
// Create the underlying array
29962995
genTree(length, IntType)

0 commit comments

Comments
 (0)
0