10000 WiP Redesign the encoding of Longs in JavaScript. · scala-js/scala-js@781fc3c · GitHub
[go: up one dir, main page]

Skip to content

Commit 781fc3c

Browse files
committed
WiP Redesign the encoding of Longs in JavaScript.
As a nice bonus, the IR checker now passes with `RuntimeLong`.
1 parent 331baa8 commit 781fc3c

File tree

17 files changed

+1408
-997
lines changed

17 files changed

+1408
-997
lines changed

linker-private-library/src/main/scala/org/scalajs/linker/runtime/RuntimeLong.scala

Lines changed: 191 additions & 350 deletions
Large diffs are not rendered by default.

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,17 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
371371
} yield {
372372
val field = anyField.asInstanceOf[FieldDef]
373373
implicit val pos = field.pos
374-
js.Assign(genSelectForDef(js.This(), field.name, field.originalName),
375-
genZeroOf(field.ftpe))
374+
field.ftp 341A e match {
375+
case LongType if !useBigIntForLongs =>
376+
val (lo, hi) = genSelectLongForDef(js.This(), field.name, field.originalName)
377+
js.Block(
378+
js.Assign(lo, js.IntLiteral(0)),
379+
js.Assign(hi, js.IntLiteral(0))
380+
)
381+
case _ =>
382+
js.Assign(genSelectForDef(js.This(), field.name, field.originalName),
383+
genZeroOf(field.ftpe))
384+
}
376385
}
377386
}
378387

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala

Lines changed: 191 additions & 40 deletions
Large diffs are not rendered by default.

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,12 +1434,7 @@ object Emitter {
14341434
callMethod(BoxedStringClass, hashCodeMethodName),
14351435

14361436
cond(!config.coreSpec.esFeatures.allowBigIntsForLongs) {
1437-
multiple(
1438-
instanceTests(LongImpl.RuntimeLongClass),
1439-
instantiateClass(LongImpl.RuntimeLongClass, LongImpl.AllConstructors.toList),
1440-
callMethods(LongImpl.RuntimeLongClass, LongImpl.BoxedLongMethods.toList),
1441-
callStaticMethods(LongImpl.RuntimeLongClass, LongImpl.OperatorMethods.toList)
1442-
)
1437+
callStaticMethods(LongImpl.RuntimeLongClass, LongImpl.OperatorMethods.toList)
14431438
},
14441439

14451440
cond(config.coreSpec.esFeatures.esVersion < ESVersion.ES2015) {

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

Lines changed: 639 additions & 228 deletions
Large diffs are not rendered by default.

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/LongImpl.scala

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import org.scalajs.ir.WellKnownNames._
1818

1919
private[linker] object LongImpl {
2020
final val RuntimeLongClass = ClassName("org.scalajs.linker.runtime.RuntimeLong")
21+
final val RuntimeLongModClass = ClassName("org.scalajs.linker.runtime.RuntimeLong$")
2122

2223
final val lo = MethodName("lo", Nil, IntRef)
2324
final val hi = MethodName("hi", Nil, IntRef)
@@ -26,35 +27,23 @@ private[linker] object LongImpl {
2627
private final val OneRTLongRef = RTLongRef :: Nil
2728
private final val TwoRTLongRefs = RTLongRef :: OneRTLongRef
2829

30+
private final val TwoIntRefs = IntRef :: IntRef :: Nil
31+
private final val ThreeIntRefs = IntRef :: TwoIntRefs
32+
private final val FourIntRefs = IntRef :: ThreeIntRefs
33+
34+
final val pack = MethodName("pack", TwoIntRefs, LongRef)
35+
2936
def unaryOp(name: String): MethodName =
30-
MethodName(name, OneRTLongRef, RTLongRef)
37+
MethodName(name, TwoIntRefs, LongRef)
3138

3239
def binaryOp(name: String): MethodName =
33-
MethodName(name, TwoRTLongRefs, RTLongRef)
40+
MethodName(name, FourIntRefs, LongRef)
3441

3542
def shiftOp(name: String): MethodName =
36-
MethodName(name, List(RTLongRef, IntRef), RTLongRef)
43+
MethodName(name, ThreeIntRefs, LongRef)
3744

3845
def compareOp(name: String): MethodName =
39-
MethodName(name, TwoRTLongRefs, BooleanRef)
40-
41-
// Instance methods that we need to reach as part of the jl.Long boxing
42-
43-
private final val byteValue = MethodName("byteValue", Nil, ByteRef)
44-
private final val shortValue = MethodName("shortValue", Nil, ShortRef)
45-
private final val intValue = MethodName("intValue", Nil, IntRef)
46-
private final val longValue = MethodName("longValue", Nil, LongRef)
47-
private final val floatValue = MethodName("floatValue", Nil, FloatRef)
48-
private final val doubleValue = MethodName("doubleValue", Nil, DoubleRef)
49-
50-
private final val equalsO = MethodName("equals", List(ClassRef(ObjectClass)), BooleanRef)
51-
private final val hashCode_ = MethodName("hashCode", Nil, IntRef)
52-
private final val compareTo = MethodName("compareTo", List(ClassRef(BoxedLongClass)), IntRef)
53-
private final val compareToO = MethodName("compareTo", List(ClassRef(ObjectClass)), IntRef)
54-
55-
val BoxedLongMethods = Set(
56-
byteValue, shortValue, intValue, longValue, floatValue, doubleValue,
57-
equalsO, hashCode_, compareTo, compareToO)
46+
MethodName(name, FourIntRefs, BooleanRef)
5847

5948
// Operator methods
6049

@@ -86,49 +75,42 @@ private[linker] object LongImpl {
8675
final val gtu = compareOp("gtu")
8776
final val geu = compareOp("geu")
8877

89-
final val toInt = MethodName("toInt", OneRTLongRef, IntRef)
90-
final val toFloat = MethodName("toFloat", OneRTLongRef, FloatRef)
91-
final val toDouble = MethodName("toDouble", OneRTLongRef, DoubleRef)
92-
final val bitsToDouble = MethodName("bitsToDouble", List(RTLongRef, ObjectRef), DoubleRef)
93-
final val clz = MethodName("clz", OneRTLongRef, IntRef)
78+
final val toInt = MethodName("toInt", TwoIntRefs, IntRef)
79+
final val toFloat = MethodName("toFloat", TwoIntRefs, FloatRef)
80+
final val toDouble = MethodName("toDouble", TwoIntRefs, DoubleRef)
81+
final val bitsToDouble = MethodName("bitsToDouble", List(IntRef, IntRef, ObjectRef), DoubleRef)
82+
final val clz = MethodName("clz", TwoIntRefs, IntRef)
9483

95-
final val fromInt = MethodName("fromInt", List(IntRef), RTLongRef)
96-
final val fromUnsignedInt = MethodName("fromUnsignedInt", List(IntRef), RTLongRef)
97-
final val fromDouble = MethodName("fromDouble", List(DoubleRef), RTLongRef)
98-
final val fromDoubleBits = MethodName("fromDoubleBits", List(DoubleRef, ObjectRef), RTLongRef)
84+
final val fromInt = MethodName("fromInt", List(IntRef), LongRef)
85+
final val fromUnsignedInt = MethodName("fromUnsignedInt", List(IntRef), LongRef)
86+
final val fromDouble = MethodName("fromDouble", List(DoubleRef), LongRef)
87+
final val fromDoubleBits = MethodName("fromDoubleBits", List(DoubleRef, ObjectRef), LongRef)
88+
89+
final val toString_ = MethodName("toString", TwoIntRefs, ClassRef(BoxedStringClass))
9990

10091
val OperatorMethods = Set(
10192
add, sub, mul,
10293
divide, remainder, divideUnsigned, remainderUnsigned,
10394
or, and, xor, shl, shr, sar,
10495
equals_, notEquals, lt, le, gt, ge, ltu, leu, gtu, geu,
10596
toInt, toFloat, toDouble, bitsToDouble, clz,
106-
fromInt, fromUnsignedInt, fromDouble, fromDoubleBits
97+
fromInt, fromUnsignedInt, fromDouble, fromDoubleBits,
98+
toString_
10799
)
108100

109101
// Methods used for intrinsics
110102

111-
final val toString_ = MethodName("toString", OneRTLongRef, ClassRef(BoxedStringClass))
112-
113-
final val compare = MethodName("compare", TwoRTLongRefs, IntRef)
103+
final val compare = MethodName("compare", FourIntRefs, IntRef)
114104

115-
final val abs = MethodName("abs", OneRTLongRef, RTLongRef)
116-
final val multiplyFull = MethodName("multiplyFull", List(IntRef, IntRef), RTLongRef)
105+
final val abs = MethodName("abs", TwoIntRefs, LongRef)
106+
final val multiplyFull = MethodName("multiplyFull", TwoIntRefs, LongRef)
117107

118108
val AllIntrinsicMethods = Set(
119-
toString_,
120109
compare,
121110
abs,
122111
multiplyFull
123112
)
124113

125-
// Constructors
126-
127-
final val initFromParts = MethodName.constructor(List(IntRef, IntRef))
128-
129-
val AllConstructors = Set(
130-
initFromParts)
131-
132114
// Extract the parts to give to the initFromParts constructor
133115

134116
def extractParts(value: Long): (Int, Int) =

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ private[emitter] final class SJSGen(
5757
/** `Char.c`: the int value of the character. */
5858
val c = "c"
5959

60+
/** `Long.l`: the lo word of the long. */
61+
val lo = "l"
62+
63+
/** `Long.h`: the hi word of the long. */
64+
val hi = "h"
65+
6066
// --- TypeData fields ---
6167

6268
/** `TypeData.constr`: the run-time constructor of the class. */
@@ -222,23 +228,18 @@ private[emitter] final class SJSGen(
222228
def genLongZero()(
223229
implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
224230
pos: Position): Tree = {
225-
if (useBigIntForLongs)
226-
BigIntLiteral(0L)
227-
else
228-
globalVar(VarField.L0, CoreVar)
231+
assert(useBigIntForLongs, s"cannot generate a zero value for primitive long at $pos")
232+
BigIntLiteral(0L)
229233
}
230234

231235
def genBoxedZeroOf(tpe: Type)(
232236
implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
233237
pos: Position): Tree = {
234-
if (tpe == CharType) genBoxedCharZero()
235-
else genZeroOf(tpe)
236-
}
237-
238-
def genBoxedCharZero()(
239-
implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
240-
pos: Position): Tree = {
241-
globalVar(VarField.bC0, CoreVar)
238+
tpe match {
239+
case CharType => globalVar(VarField.bC0, CoreVar)
240+
case LongType if !useBigIntForLongs => globalVar(VarField.bL0, CoreVar)
241+
case _ => genZeroOf(tpe)
242+
}
242243
}
243244

244245
def genLongApplyStatic(methodName: MethodName, args: Tree*)(
@@ -278,7 +279,11 @@ private[emitter] final class SJSGen(
278279
case FloatRef => some("Float32Array")
279280
case DoubleRef => some("Float64Array")
280281

281-
case LongRef if useBigIntForLongs => some("BigInt64Array")
282+
case LongRef =>
283+
if (useBigIntForLongs)
284+
some("BigInt64Array")
285+
else
286+
some("Int32Array") // where elements are spread over two slots
282287

283288
case _ => None
284289
}
@@ -289,12 +294,28 @@ private[emitter] final class SJSGen(
289294
DotSelect(receiver, genFieldIdent(field.name)(field.pos))
290295
}
291296

297+
def genSelectLong(receiver: Tree, field: irt.FieldIdent)(
298+
implicit pos: Position): (Tree, Tree) = {
299+
// TODO Name compressor
300+
val baseName = genName(field.name)
301+
val loIdent = Ident(baseName + "_$lo")(field.pos)
302+
val hiIdent = Ident(baseName + "_$hi")(field.pos)
303+
(DotSelect(receiver, loIdent), DotSelect(receiver, hiIdent))
304+
}
305+
292306
def genSelectForDef(receiver: Tree, field: irt.FieldIdent,
293307
originalName: OriginalName)(
294308
implicit pos: Position): Tree = {
295309
DotSelect(receiver, genFieldIdentForDef(field.name, originalName)(field.pos))
296310
}
297311

312+
def genSelectLongForDef(receiver: Tree, field: irt.FieldIdent,
313+
originalName: OriginalName)(
314+
implicit pos: Position): (Tree, Tree) = {
315+
// TODO Name compressor; originalName
316+
genSelectLong(receiver, field)
317+
}
318+
298319
private def genFieldIdent(fieldName: FieldName)(
299320
implicit pos: Position): MaybeDelayedIdent = {
300321
nameCompressor match {
@@ -488,7 +509,7 @@ private[emitter] final class SJSGen(
488509
import TreeDSL._
489510

490511
if (useBigIntForLongs) genCallHelper(VarField.isLong, expr)
491-
else expr instanceof globalVar(VarField.c, LongImpl.RuntimeLongClass)
512+
else expr instanceof globalVar(VarField.Long, CoreVar)
492513
}
493514

494515
def genAsInstanceOf(expr: Tree, tpe: Type)(

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,47 @@ import org.scalajs.ir.Types._
2121

2222
object Transients {
2323

24+
/** Packs a `long` value from its `lo` and `hi` words. */
25+
final case class PackLong(lo: Tree, hi: Tree) extends Transient.Value {
26+
val tpe = LongType
27+
28+
def traverse(traverser: Traverser): Unit = {
29+
traverser.traverse(lo)
30+
traverser.traverse(hi)
31+
}
32+
33+
def transform(transformer: Transformer)(implicit pos: Position): Tree =
34+
Transient(PackLong(transformer.transform(lo), transformer.transform(hi)))
35+
36+
def printIR(out: IRTreePrinter): Unit = {
37+
out.print("<packLong>(")
38+
out.print(lo)
39+
out.print(", ")
40+
out.print(hi)
41+
out.print(")")
42+
}
43+
}
44+
45+
/** Extracts the `hi` word of a `long` value.
46+
*
47+
* To extract the `lo` word, use a `UnaryOp.LongToInt`.
48+
*/
49+
final case class ExtractLongHi(value: Tree) extends Transient.Value {
50+
val tpe = IntType
51+
52+
def traverse(traverser: Traverser): Unit =
53+
traverser.traverse(value)
54+
55+
def transform(transformer: Transformer)(implicit pos: Position): Tree =
56+
Transient(ExtractLongHi(transformer.transform(value)))
57+
58+
def printIR(out: IRTreePrinter): Unit = {
59+
out.print("<hi>(")
60+
out.print(value)
61+
out.print(")")
62+
}
63+
}
64+
2465
/** Casts `expr` to the given `tpe`, without any check.
2566
*
2667
* This operation is only valid if we know that `expr` is indeed a value of

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/VarField.scala

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ private[emitter] object VarField {
5858
/** Static fields. */
5959
final val t = mk("$t")
6060

61+
/** Static fields, hi word of a long. */
62+
final val thi = mk("$thi")
63+
6164
/** Scala module accessor. */
6265
final val m = mk("$m")
6366

@@ -123,12 +126,18 @@ private[emitter] object VarField {
123126
/** Local field for class captures. */
124127
final val cc = mk("$cc")
125128

129+
/** Local field for the hi word of a long class capture. */
130+
final val cchi = mk("$cchi")
131+
126132
/** Local field for super class. */
127133
final val superClass = mk("$superClass")
128134

129135
/** Local field for this replacement. */
130136
final val thiz = mk("$thiz")
131137

138+
/** Local field for the hi word of a this replacement. */
139+
final val thizhi = mk("$thizhi")
140+
132141
/** Local field for dynamic imports. */
133142
final val module = mk("$module")
134143

@@ -143,8 +152,11 @@ private[emitter] object VarField {
143152
/** The TypeData class. */
144153
final val TypeData = mk("$TypeData")
145154

146-
/** Long zero. */
147-
final val L0 = mk("$L0")
155+
/** Hi word of a long result. */
156+
final val resHi = mk("$resHi")
157+
158+
/** Setter for resHi (for ES Modules). */
159+
final val setResHi = mk("$setResHi")
148160

149161
/** DataView for floating point bit manipulation. */
150162
final val fpBitsDataView = mk("$fpBitsDataView")
@@ -167,6 +179,17 @@ private[emitter] object VarField {
167179

168180
final val charAt = mk("$charAt")
169181

182+
// Long
183+
184+
/** The Long class. */
185+
final val Long = mk("$Long")
186+
187+
/** Box long. */
188+
final val bL = mk("$bL")
189+
190+
/** Boxed Long zero. */
191+
final val bL0 = mk("$bL0")
192+
170193
// Object helpers
171194

172195
final val objectClone = mk("$objectClone")

linker/shared/src/main/scala/org/scalajs/linker/checker/FeatureSet.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ private[checker] object FeatureSet {
6060
/** Records and record types. */
6161
val Records = new FeatureSet(1 << 7)
6262

63+
val PackLong = new FeatureSet(1 << 8)
64+
6365
/** Relaxed constructor discipline.
6466
*
6567
* - Optional super/delegate constructor call.
@@ -88,7 +90,7 @@ private[checker] object FeatureSet {
8890

8991
/** IR that is only the result of desugaring (currently empty). */
9092
private val Desugared =
91-
Empty
93+
PackLong
9294

9395
/** IR that is only the result of optimizations. */
9496
private val Optimized =

0 commit comments

Comments
 (0)
0