Skip to content

Commit 1c51097

Browse files
authored
Merge pull request tanishiking#137 from sjrd/small-codegen-improvements
Small codegen improvements.
2 parents fb3bcb2 + 0d82f14 commit 1c51097

File tree

5 files changed

+54
-99
lines changed

5 files changed

+54
-99
lines changed

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

+16-14
Original file line numberDiff line numberDiff line change
@@ -545,26 +545,28 @@ class ClassEmitter(coreSpec: CoreSpec) {
545545

546546
val instrs = fb
547547

548-
val heapType = watpe.HeapType(genTypeID.forClass(className))
548+
val structTypeID = genTypeID.forClass(className)
549+
val structRefType = watpe.RefType(structTypeID)
549550

550-
val from = fb.addLocal("fromTyped", watpe.RefType.nullable(heapType))
551-
val result = fb.addLocal("result", watpe.RefType.nullable(heapType))
551+
val fromTypedLocal = fb.addLocal("fromTyped", structRefType)
552552

553+
// Downcast fromParam to fromTyped
553554
instrs += wa.LocalGet(fromParam)
554-
instrs += wa.RefCast(watpe.RefType(heapType))
555-
instrs += wa.LocalSet(from)
555+
instrs += wa.RefCast(structRefType)
556+
instrs += wa.LocalSet(fromTypedLocal)
556557

557-
instrs += wa.Call(genFunctionID.newDefault(className))
558-
instrs += wa.LocalSet(result)
558+
// Push vtable and itables on the stack (there is at least Cloneable in the itables)
559+
instrs += wa.GlobalGet(genGlobalID.forVTable(className))
560+
instrs += wa.GlobalGet(genGlobalID.forITable(className))
561+
562+
// Push every field of `fromTyped` on the stack
559563
info.allFieldDefs.foreach { field =>
560-
val fieldIdx = genFieldID.forClassInstanceField(field.name.name)
561-
instrs += wa.LocalGet(result)
562-
instrs += wa.LocalGet(from)
563-
instrs += wa.StructGet(genTypeID.forClass(className), fieldIdx)
564-
instrs += wa.StructSet(genTypeID.forClass(className), fieldIdx)
564+
instrs += wa.LocalGet(fromTypedLocal)
565+
instrs += wa.StructGet(structTypeID, genFieldID.forClassInstanceField(field.name.name))
565566
}
566-
instrs += wa.LocalGet(result)
567-
instrs += wa.RefAsNotNull
567+
568+
// Create the result
569+
instrs += wa.StructNew(structTypeID)
568570

569571
fb.buildAndAddToModule()
570572
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,6 @@ object CoreWasmLib {
388388
}
389389
addHelperImport(genFunctionID.box(primRef), List(wasmType), List(anyref))
390390
addHelperImport(genFunctionID.unbox(primRef), List(anyref), List(wasmType))
391-
addHelperImport(genFunctionID.unboxOrNull(primRef), List(anyref), List(anyref))
392391
addHelperImport(genFunctionID.typeTest(primRef), List(anyref), List(Int32))
393392
}
394393

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

+38-71
Original file line numberDiff line numberDiff line change
@@ -1731,64 +1731,44 @@ private class FunctionEmitter private (
17311731
val sourceTpe = tree.expr.tpe
17321732
val targetTpe = tree.tpe
17331733

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

1741-
markPosition(tree)
1741+
val sourceWasmType = TypeTransformer.transformType(sourceTpe)(ctx)
1742+
val targetWasmType = TypeTransformer.transformType(targetTpe)(ctx)
17421743

1743-
def genAsPrimType(targetTpe: PrimType): Unit = {
1744-
// TODO We could do something better for things like double.asInstanceOf[int]
1745-
genUnbox(targetTpe)(tree.pos)
1746-
}
1744+
if (sourceWasmType == targetWasmType) {
1745+
/* Common case where no cast is necessary at the Wasm level.
1746+
* Note that this is not *obviously* correct. It is only correct
1747+
* because, under our choices of representation and type translation
1748+
* rules, there is no pair `(sourceTpe, targetTpe)` for which the Wasm
1749+
* types are equal but a valid cast would require a *conversion*.
1750+
*/
1751+
genTreeAuto(tree.expr)
1752+
} else {
1753+
genTree(tree.expr, AnyType)
17471754

1748-
targetTpe match {
1749-
case targetTpe: PrimType =>
1750-
genAsPrimType(targetTpe)
1755+
markPosition(tree)
17511756

1752-
case AnyType =>
1753-
()
1757+
targetTpe match {
1758+
case targetTpe: PrimType =>
1759+
// TODO Opt: We could do something better for things like double.asInstanceOf[int]
1760+
genUnbox(targetTpe)(tree.pos)
17541761

1755-
case ClassType(targetClassName) =>
1756-
val info = ctx.getClassInfo(targetClassName)
1757-
if (info.kind == ClassKind.HijackedClass) {
1758-
BoxedClassToPrimType(targetClassName) match {
1759-
case UndefType | StringType =>
1760-
()
1761-
case primType: PrimTypeWithRef =>
1762-
primType match {
1763-
case CharType =>
1764-
val structTypeName = genTypeID.forClass(SpecialNames.CharBoxClass)
1765-
instrs += wa.RefCast(watpe.RefType.nullable(structTypeName))
1766-
case LongType =>
1767-
val structTypeName = genTypeID.forClass(SpecialNames.LongBoxClass)
1768-
instrs += wa.RefCast(watpe.RefType.nullable(structTypeName))
1769-
case NoType | NothingType | NullType =>
1770-
throw new AssertionError(s"Unexpected prim type $primType for $targetClassName")
1771-
case _ =>
1772-
instrs += wa.Call(genFunctionID.unboxOrNull(primType.primRef))
1773-
}
1762+
case _ =>
1763+
targetWasmType match {
1764+
case watpe.RefType(true, watpe.HeapType.Any) =>
1765+
() // nothing to do
1766+
case targetWasmType: watpe.RefType =>
1767+
instrs += wa.RefCast(targetWasmType)
1768+
case _ =>
1769+
throw new AssertionError(s"Unexpected type in AsInstanceOf: $targetTpe")
17741770
}
1775-
} else if (info.isAncestorOfHijackedClass) {
1776-
// Nothing to do; the translation is `anyref`
1777-
()
1778-
} else if (info.kind.isClass) {
1779-
instrs += wa.RefCast(
1780-
watpe.RefType.nullable(genTypeID.forClass(targetClassName))
1781-
)
1782-
} else if (info.isInterface) {
1783-
instrs += wa.RefCast(watpe.RefType.nullable(genTypeID.ObjectStruct))
1784-
}
1785-
1786-
case ArrayType(arrayTypeRef) =>
1787-
val structTypeName = genTypeID.forArrayClass(arrayTypeRef)
1788-
instrs += wa.RefCast(watpe.RefType.nullable(structTypeName))
1789-
1790-
case targetTpe: RecordType =>
1791-
throw new AssertionError(s"Illegal type in AsInstanceOf: $targetTpe")
1771+
}
17921772
}
17931773

17941774
targetTpe
@@ -1842,9 +1822,6 @@ private class FunctionEmitter private (
18421822
}
18431823
}
18441824

1845-
private def isSubclass(subClass: ClassName, superClass: ClassName): Boolean =
1846-
ctx.getClassInfo(subClass).ancestors.contains(superClass)
1847-
18481825
private def genGetClass(tree: GetClass): Type = {
18491826
/* Unlike in `genApply` or `genStringConcat`, here we make no effort to
18501827
* optimize known-primitive receivers. In practice, such cases would be
@@ -2282,25 +2259,15 @@ private class FunctionEmitter private (
22822259
private def genLoadJSModule(tree: LoadJSModule): Type = {
22832260
markPosition(tree)
22842261

2285-
val info = ctx.getClassInfo(tree.className)
2286-
2287-
info.kind match {
2288-
case ClassKind.NativeJSModuleClass =>
2289-
val jsNativeLoadSpec = info.jsNativeLoadSpec.getOrElse {
2290-
throw new AssertionError(s"Found $tree for class without jsNativeLoadSpec at ${tree.pos}")
2291-
}
2292-
genLoadJSFromSpec(instrs, jsNativeLoadSpec)(ctx)
2293-
AnyType
2294-
2295-
case ClassKind.JSModuleClass =>
2262+
ctx.getClassInfo(tree.className).jsNativeLoadSpec match {
2263+
case Some(loadSpec) =>
2264+
genLoadJSFromSpec(instrs, loadSpec)(ctx)
2265+
case None =>
2266+
// This is a non-native JS module
22962267
instrs += wa.Call(genFunctionID.loadModule(tree.className))
2297-
AnyType
2298-
2299-
case _ =>
2300-
throw new AssertionError(
2301-
s"Invalid LoadJSModule for class ${tree.className.nameString} of kind ${info.kind}"
2302-
)
23032268
}
2269+
2270+
AnyType
23042271
}
23052272

23062273
private def genSelectJSNativeMember(tree: SelectJSNativeMember): Type = {

wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala

-8
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,6 @@ const scalaJSHelpers = {
9595
uF: (x) => Math.fround(x),
9696
uD: (x) => +x,
9797

98-
// Unboxes to primitive or null (downcast to the boxed classes)
99-
uNZ: (x) => (x !== null) ? (x | 0) : null,
100-
uNB: (x) => (x !== null) ? ((x << 24) >> 24) : null,
101-
uNS: (x) => (x !== null) ? ((x << 16) >> 16) : null,
102-
uNI: (x) => (x !== null) ? (x | 0) : null,
103-
uNF: (x) => (x !== null) ? Math.fround(x) : null,
104-
uND: (x) => (x !== null) ? +x : null,
105-
10698
// Type tests
10799
tZ: (x) => typeof x === 'boolean',
108100
tB: (x) => typeof x === 'number' && Object.is((x << 24) >> 24, x),

wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala

-5
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,12 @@ object VarGen {
154154
override def toString(): String = "u" + primRef.charCode
155155
}
156156

157-
private final case class UnboxOrNullID(primRef: PrimRef) extends JSHelperFunctionID {
158-
override def toString(): String = "uN" + primRef.charCode
159-
}
160-
161157
private final case class TypeTestID(primRef: PrimRef) extends JSHelperFunctionID {
162158
override def toString(): String = "t" + primRef.charCode
163159
}
164160

165161
def box(primRef: PrimRef): JSHelperFunctionID = BoxID(primRef)
166162
def unbox(primRef: PrimRef): JSHelperFunctionID = UnboxID(primRef)
167-
def unboxOrNull(primRef: PrimRef): JSHelperFunctionID = UnboxOrNullID(primRef)
168163
def typeTest(primRef: PrimRef): JSHelperFunctionID = TypeTestID(primRef)
169164

170165
case object fmod extends JSHelperFunctionID

0 commit comments

Comments
 (0)