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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy