Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Small codegen improvements. #137

Merged
merged 3 commits into from
May 21, 2024
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 @@ -545,26 +545,28 @@ class ClassEmitter(coreSpec: CoreSpec) {

val instrs = fb

val heapType = watpe.HeapType(genTypeID.forClass(className))
val structTypeID = genTypeID.forClass(className)
val structRefType = watpe.RefType(structTypeID)

val from = fb.addLocal("fromTyped", watpe.RefType.nullable(heapType))
val result = fb.addLocal("result", watpe.RefType.nullable(heapType))
val fromTypedLocal = fb.addLocal("fromTyped", structRefType)

// Downcast fromParam to fromTyped
instrs += wa.LocalGet(fromParam)
instrs += wa.RefCast(watpe.RefType(heapType))
instrs += wa.LocalSet(from)
instrs += wa.RefCast(structRefType)
instrs += wa.LocalSet(fromTypedLocal)

instrs += wa.Call(genFunctionID.newDefault(className))
instrs += wa.LocalSet(result)
// Push vtable and itables on the stack (there is at least Cloneable in the itables)
instrs += wa.GlobalGet(genGlobalID.forVTable(className))
instrs += wa.GlobalGet(genGlobalID.forITable(className))

// Push every field of `fromTyped` on the stack
info.allFieldDefs.foreach { field =>
val fieldIdx = genFieldID.forClassInstanceField(field.name.name)
instrs += wa.LocalGet(result)
instrs += wa.LocalGet(from)
instrs += wa.StructGet(genTypeID.forClass(className), fieldIdx)
instrs += wa.StructSet(genTypeID.forClass(className), fieldIdx)
instrs += wa.LocalGet(fromTypedLocal)
instrs += wa.StructGet(structTypeID, genFieldID.forClassInstanceField(field.name.name))
}
instrs += wa.LocalGet(result)
instrs += wa.RefAsNotNull

// Create the result
instrs += wa.StructNew(structTypeID)

fb.buildAndAddToModule()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ object CoreWasmLib {
}
addHelperImport(genFunctionID.box(primRef), List(wasmType), List(anyref))
addHelperImport(genFunctionID.unbox(primRef), List(anyref), List(wasmType))
addHelperImport(genFunctionID.unboxOrNull(primRef), List(anyref), List(anyref))
addHelperImport(genFunctionID.typeTest(primRef), List(anyref), List(Int32))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1731,64 +1731,44 @@ private class FunctionEmitter private (
val sourceTpe = tree.expr.tpe
val targetTpe = tree.tpe

if (isSubtype(sourceTpe, targetTpe)(isSubclass(_, _))) {
// Common case where no cast is necessary
genTreeAuto(tree.expr)
sourceTpe
if (sourceTpe == NothingType) {
// We cannot call transformType for NothingType, so we have to handle this case separately.
genTree(tree.expr, NothingType)
NothingType
} else {
genTree(tree.expr, AnyType)
// By IR checker rules, targetTpe is none of NothingType, NullType, NoType or RecordType

markPosition(tree)
val sourceWasmType = TypeTransformer.transformType(sourceTpe)(ctx)
val targetWasmType = TypeTransformer.transformType(targetTpe)(ctx)

def genAsPrimType(targetTpe: PrimType): Unit = {
// TODO We could do something better for things like double.asInstanceOf[int]
genUnbox(targetTpe)(tree.pos)
}
if (sourceWasmType == targetWasmType) {
/* Common case where no cast is necessary at the Wasm level.
* Note that this is not *obviously* correct. It is only correct
* because, under our choices of representation and type translation
* rules, there is no pair `(sourceTpe, targetTpe)` for which the Wasm
* types are equal but a valid cast would require a *conversion*.
Comment on lines +1748 to +1749
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

*/
genTreeAuto(tree.expr)
} else {
genTree(tree.expr, AnyType)

targetTpe match {
case targetTpe: PrimType =>
genAsPrimType(targetTpe)
markPosition(tree)

case AnyType =>
()
targetTpe match {
case targetTpe: PrimType =>
// TODO Opt: We could do something better for things like double.asInstanceOf[int]
genUnbox(targetTpe)(tree.pos)

case ClassType(targetClassName) =>
val info = ctx.getClassInfo(targetClassName)
if (info.kind == ClassKind.HijackedClass) {
BoxedClassToPrimType(targetClassName) match {
case UndefType | StringType =>
()
case primType: PrimTypeWithRef =>
primType match {
case CharType =>
val structTypeName = genTypeID.forClass(SpecialNames.CharBoxClass)
instrs += wa.RefCast(watpe.RefType.nullable(structTypeName))
case LongType =>
val structTypeName = genTypeID.forClass(SpecialNames.LongBoxClass)
instrs += wa.RefCast(watpe.RefType.nullable(structTypeName))
case NoType | NothingType | NullType =>
throw new AssertionError(s"Unexpected prim type $primType for $targetClassName")
case _ =>
instrs += wa.Call(genFunctionID.unboxOrNull(primType.primRef))
}
case _ =>
targetWasmType match {
case watpe.RefType(true, watpe.HeapType.Any) =>
() // nothing to do
case targetWasmType: watpe.RefType =>
instrs += wa.RefCast(targetWasmType)
case _ =>
throw new AssertionError(s"Unexpected type in AsInstanceOf: $targetTpe")
}
} else if (info.isAncestorOfHijackedClass) {
// Nothing to do; the translation is `anyref`
()
} else if (info.kind.isClass) {
instrs += wa.RefCast(
watpe.RefType.nullable(genTypeID.forClass(targetClassName))
)
} else if (info.isInterface) {
instrs += wa.RefCast(watpe.RefType.nullable(genTypeID.ObjectStruct))
}

case ArrayType(arrayTypeRef) =>
val structTypeName = genTypeID.forArrayClass(arrayTypeRef)
instrs += wa.RefCast(watpe.RefType.nullable(structTypeName))

case targetTpe: RecordType =>
throw new AssertionError(s"Illegal type in AsInstanceOf: $targetTpe")
}
}

targetTpe
Expand Down Expand Up @@ -1842,9 +1822,6 @@ private class FunctionEmitter private (
}
}

private def isSubclass(subClass: ClassName, superClass: ClassName): Boolean =
ctx.getClassInfo(subClass).ancestors.contains(superClass)

private def genGetClass(tree: GetClass): Type = {
/* Unlike in `genApply` or `genStringConcat`, here we make no effort to
* optimize known-primitive receivers. In practice, such cases would be
Expand Down Expand Up @@ -2282,25 +2259,15 @@ private class FunctionEmitter private (
private def genLoadJSModule(tree: LoadJSModule): Type = {
markPosition(tree)

val info = ctx.getClassInfo(tree.className)

info.kind match {
case ClassKind.NativeJSModuleClass =>
val jsNativeLoadSpec = info.jsNativeLoadSpec.getOrElse {
throw new AssertionError(s"Found $tree for class without jsNativeLoadSpec at ${tree.pos}")
}
genLoadJSFromSpec(instrs, jsNativeLoadSpec)(ctx)
AnyType

case ClassKind.JSModuleClass =>
ctx.getClassInfo(tree.className).jsNativeLoadSpec match {
case Some(loadSpec) =>
genLoadJSFromSpec(instrs, loadSpec)(ctx)
case None =>
// This is a non-native JS module
instrs += wa.Call(genFunctionID.loadModule(tree.className))
AnyType

case _ =>
throw new AssertionError(
s"Invalid LoadJSModule for class ${tree.className.nameString} of kind ${info.kind}"
)
}

AnyType
}

private def genSelectJSNativeMember(tree: SelectJSNativeMember): Type = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,6 @@ const scalaJSHelpers = {
uF: (x) => Math.fround(x),
uD: (x) => +x,

// Unboxes to primitive or null (downcast to the boxed classes)
uNZ: (x) => (x !== null) ? (x | 0) : null,
uNB: (x) => (x !== null) ? ((x << 24) >> 24) : null,
uNS: (x) => (x !== null) ? ((x << 16) >> 16) : null,
uNI: (x) => (x !== null) ? (x | 0) : null,
uNF: (x) => (x !== null) ? Math.fround(x) : null,
uND: (x) => (x !== null) ? +x : null,

// Type tests
tZ: (x) => typeof x === 'boolean',
tB: (x) => typeof x === 'number' && Object.is((x << 24) >> 24, x),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,12 @@ object VarGen {
override def toString(): String = "u" + primRef.charCode
}

private final case class UnboxOrNullID(primRef: PrimRef) extends JSHelperFunctionID {
override def toString(): String = "uN" + primRef.charCode
}

private final case class TypeTestID(primRef: PrimRef) extends JSHelperFunctionID {
override def toString(): String = "t" + primRef.charCode
}

def box(primRef: PrimRef): JSHelperFunctionID = BoxID(primRef)
def unbox(primRef: PrimRef): JSHelperFunctionID = UnboxID(primRef)
def unboxOrNull(primRef: PrimRef): JSHelperFunctionID = UnboxOrNullID(primRef)
def typeTest(primRef: PrimRef): JSHelperFunctionID = TypeTestID(primRef)

case object fmod extends JSHelperFunctionID
Expand Down