10000 Merge pull request #5149 from sjrd/wasm-fmod-in-scala · scala-js/scala-js@95d04c6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 95d04c6

Browse files
authored
Merge pull request #5149 from sjrd/wasm-fmod-in-scala
Wasm: Implement fmod on the Scala side.
2 parents bb4d753 + b5e2700 commit 95d04c6

File tree

10 files changed

+561
-38
lines changed

10 files changed

+561
-38
lines changed

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

Lines changed: 455 additions & 0 deletions
Large diffs are not rendered by default.

linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ object PrivateLibHolder {
2727
"org/scalajs/linker/runtime/RuntimeLong.sjsir",
2828
"org/scalajs/linker/runtime/RuntimeLong$.sjsir",
2929
"org/scalajs/linker/runtime/UndefinedBehaviorError.sjsir",
30+
"org/scalajs/linker/runtime/WasmRuntime.sjsir",
31+
"org/scalajs/linker/runtime/WasmRuntime$.sjsir",
3032
"scala/scalajs/js/JavaScriptException.sjsir"
3133
)
3234

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,6 @@ final class CoreWasmLib(coreSpec: CoreSpec, globalInfo: LinkedGlobalInfo) {
373373
addHelperImport(genFunctionID.uIFallback, List(anyref), List(Int32))
374374
addHelperImport(genFunctionID.typeTest(IntRef), List(anyref), List(Int32))
375375

376-
addHelperImport(genFunctionID.fmod, List(Float64, Float64), List(Float64))
377-
378376
addHelperImport(genFunctionID.jsValueToString, List(RefType.any), List(RefType.extern))
379377
addHelperImport(genFunctionID.jsValueToStringForConcat, List(anyref), List(RefType.extern))
380378
addHelperImport(genFunctionID.booleanToString, List(Int32), List(RefType.extern))

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,11 @@ object Emitter {
458458

459459
// See genIdentityHashCode in HelperFunctions
460460
callMethodStatically(BoxedDoubleClass, hashCodeMethodName),
461-
callMethodStatically(BoxedStringClass, hashCodeMethodName)
461+
callMethodStatically(BoxedStringClass, hashCodeMethodName),
462+
463+
// Implementation of Float_% and Double_%
464+
callStaticMethod(WasmRuntimeClass, fmodfMethodName),
465+
callStaticMethod(WasmRuntimeClass, fmoddMethodName)
462466
)
463467
}
464468

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

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,9 +1649,18 @@ private class FunctionEmitter private (
16491649

16501650
def genLongShiftOp(shiftInstr: wa.Instr): Type = {
16511651
genTree(lhs, LongType)
1652-
genTree(rhs, IntType)
1653-
markPosition(tree)
1654-
fb += wa.I64ExtendI32S
1652+
rhs match {
1653+
case IntLiteral(r) =>
1654+
// common case: fold the extension to 64 bits into the literal
1655+
markPosition(rhs)
1656+
fb += wa.I64Const(r.toLong)
1657+
markPosition(tree)
1658+
case _ =>
1659+
// otherwise, extend at run-time
1660+
genTree(rhs, IntType)
1661+
markPosition(tree)
1662+
fb += wa.I64ExtendI32S
1663+
}
16551664
fb += shiftInstr
16561665
LongType
16571666
}
@@ -1713,31 +1722,6 @@ private class FunctionEmitter private (
17131722
case Long_>> =>
17141723
genLongShiftOp(wa.I64ShrS)
17151724

1716-
/* Floating point remainders are specified by
1717-
* https://262.ecma-international.org/#sec-numeric-types-number-remainder
1718-
* which says that it is equivalent to the C library function `fmod`.
1719-
* For `Float`s, we promote and demote to `Double`s.
1720-
* `fmod` seems quite hard to correctly implement, so we delegate to a
1721-
* JavaScript Helper.
1722-
* (The naive function `x - trunc(x / y) * y` that we can find on the
1723-
* Web does not work.)
1724-
*/
1725-
case Float_% =>
1726-
genTree(lhs, FloatType)
1727-
fb += wa.F64PromoteF32
1728-
genTree(rhs, FloatType)
1729-
fb += wa.F64PromoteF32
1730-
markPosition(tree)
1731-
fb += wa.Call(genFunctionID.fmod)
1732-
fb += wa.F32DemoteF64
1733-
FloatType
1734-
case Double_% =>
1735-
genTree(lhs, DoubleType)
1736-
genTree(rhs, DoubleType)
1737-
markPosition(tree)
1738-
fb += wa.Call(genFunctionID.fmod)
1739-
DoubleType
1740-
17411725
case String_charAt =>
17421726
genTree(lhs, StringType)
17431727
genTree(rhs, IntType)
@@ -1876,6 +1860,9 @@ private class FunctionEmitter private (
18761860
private def getElementaryBinaryOpInstr(op: BinaryOp.Code): wa.Instr = {
18771861
import BinaryOp._
18781862

1863+
def fmodFunctionID(methodName: MethodName): wanme.FunctionID =
1864+
genFunctionID.forMethod(MemberNamespace.PublicStatic, SpecialNames.WasmRuntimeClass, methodName)
1865+
18791866
(op: @switch) match {
18801867
case Boolean_== => wa.I32Eq
18811868
case Boolean_!= => wa.I32Ne
@@ -1916,11 +1903,13 @@ private class FunctionEmitter private (
19161903
case Float_- => wa.F32Sub
19171904
case Float_* => wa.F32Mul
19181905
case Float_/ => wa.F32Div
1906+
case Float_% => wa.Call(fmodFunctionID(SpecialNames.fmodfMethodName))
19191907

19201908
case Double_+ => wa.F64Add
19211909
case Double_- => wa.F64Sub
19221910
case Double_* => wa.F64Mul
19231911
case Double_/ => wa.F64Div
1912+
case Double_% => wa.Call(fmodFunctionID(SpecialNames.fmoddMethodName))
19241913

19251914
case Double_== => wa.F64Eq
19261915
case Double_!= => wa.F64Ne

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ const scalaJSHelpers = {
100100
tF: (x) => typeof x === 'number' && (Math.fround(x) === x || x !== x),
101101
tD: (x) => typeof x === 'number',
102102

103-
// fmod, to implement Float_% and Double_% (it is apparently quite hard to implement fmod otherwise)
104-
fmod: (x, y) => x % y,
105-
106103
// Strings
107104
jsValueToString: (x) => (x === void 0) ? "undefined" : x.toString(),
108105
jsValueToStringForConcat: (x) => "" + x,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ object SpecialNames {
3737
val UndefinedBehaviorErrorClass =
3838
ClassName("org.scalajs.linker.runtime.UndefinedBehaviorError")
3939

40+
val WasmRuntimeClass =
41+
ClassName("org.scalajs.linker.runtime.WasmRuntime")
42+
4043
// Field names
4144

4245
val valueFieldSimpleName = SimpleFieldName("value")
@@ -52,6 +55,9 @@ object SpecialNames {
5255

5356
val hashCodeMethodName = MethodName("hashCode", Nil, IntRef)
5457

58+
val fmodfMethodName = MethodName("fmodf", List(FloatRef, FloatRef), FloatRef)
59+
val fmoddMethodName = MethodName("fmodd", List(DoubleRef, DoubleRef), DoubleRef)
60+
5561
/** A unique simple method name to map all method *signatures* into `MethodName`s. */
5662
val normalizedSimpleMethodName = SimpleMethodName("m")
5763
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,6 @@ object VarGen {
130130
case object bIFallback extends JSHelperFunctionID
131131
case object uIFallback extends JSHelperFunctionID
132132

133-
case object fmod extends JSHelperFunctionID
134-
135133
case object jsValueToString extends JSHelperFunctionID // for actual toString() call
136134
case object jsValueToStringForConcat extends JSHelperFunctionID
137135
case object booleanToString extends JSHelperFunctionID

test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DoubleTest.scala

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class DoubleTest {
221221
test(Double.NaN, Double.NaN, 2.1)
222222
test(Double.NaN, Double.NaN, 5.5)
223223
test(Double.NaN, Double.NaN, -151.189)
224+
test(Double.NaN, Double.NaN, 3.123e-320)
225+
test(Double.NaN, Double.NaN, 2.253547e-318)
224226

225227
// If d is NaN, return NaN
226228
test(Double.NaN, Double.NaN, Double.NaN)
@@ -231,6 +233,8 @@ class DoubleTest {
231233
test(Double.NaN, 2.1, Double.NaN)
232234
test(Double.NaN, 5.5, Double.NaN)
233235
test(Double.NaN, -151.189, Double.NaN)
236+
test(Double.NaN, 3.123e-320, Double.NaN)
237+
test(Double.NaN, 2.253547e-318, Double.NaN)
234238

235239
// If n is PositiveInfinity, return NaN
236240
test(Double.NaN, Double.PositiveInfinity, Double.PositiveInfinity)
@@ -240,6 +244,8 @@ class DoubleTest {
240244
test(Double.NaN, Double.PositiveInfinity, 2.1)
241245
test(Double.NaN, Double.PositiveInfinity, 5.5)
242246
test(Double.NaN, Double.PositiveInfinity, -151.189)
247+
test(Double.NaN, Double.PositiveInfinity, 3.123e-320)
248+
test(Double.NaN, Double.PositiveInfinity, 2.253547e-318)
243249

244250
// If n is NegativeInfinity, return NaN
245251
test(Double.NaN, Double.NegativeInfinity, Double.PositiveInfinity)
@@ -249,56 +255,87 @@ class DoubleTest {
249255
test(Double.NaN, Double.NegativeInfinity, 2.1)
250256
test(Double.NaN, Double.NegativeInfinity, 5.5)
251257
test(Double.NaN, Double.NegativeInfinity, -151.189)
258+
test(Double.NaN, Double.NegativeInfinity, 3.123e-320)
259+
test(Double.NaN, Double.NegativeInfinity, 2.253547e-318)
252260

253261
// If d is PositiveInfinity, return n
254262
test(+0.0, +0.0, Double.PositiveInfinity)
255263
test(-0.0, -0.0, Double.PositiveInfinity)
256264
test(2.1, 2.1, Double.PositiveInfinity)
257265
test(5.5, 5.5, Double.PositiveInfinity)
258266
test(-151.189, -151.189, Double.PositiveInfinity)
267+
test(3.123e-320, 3.123e-320, Double.PositiveInfinity)
268+
test(2.253547e-318, 2.253547e-318, Double.PositiveInfinity)
259269

260270
// If d is NegativeInfinity, return n
261271
test(+0.0, +0.0, Double.NegativeInfinity)
262272
test(-0.0, -0.0, Double.NegativeInfinity)
263273
test(2.1, 2.1, Double.NegativeInfinity)
264274
test(5.5, 5.5, Double.NegativeInfinity)
265275
test(-151.189, -151.189, Double.NegativeInfinity)
276+
test(3.123e-320, 3.123e-320, Double.NegativeInfinity)
277+
test(2.253547e-318, 2.253547e-318, Double.NegativeInfinity)
266278

267279
// If d is +0.0, return NaN
268280
test(Double.NaN, +0.0, +0.0)
269281
test(Double.NaN, -0.0, +0.0)
270282
test(Double.NaN, 2.1, +0.0)
271283
test(Double.NaN, 5.5, +0.0)
272284
test(Double.NaN, -151.189, +0.0)
285+
test(Double.NaN, 3.123e-320, +0.0)
286+
test(Double.NaN, 2.253547e-318, +0.0)
273287

274288
// If d is -0.0, return NaN
275289
test(Double.NaN, +0.0, -0.0)
276290
test(Double.NaN, -0.0, -0.0)
277291
test(Double.NaN, 2.1, -0.0)
278292
test(Double.NaN, 5.5, -0.0)
279293
test(Double.NaN, -151.189, -0.0)
294+
test(Double.NaN, 3.123e-320, -0.0)
295+
test(Double.NaN, 2.253547e-318, -0.0)
280296

281297
// If n is +0.0, return n
282298
test(+0.0, +0.0, 2.1)
283299
test(+0.0, +0.0, 5.5)
284300
test(+0.0, +0.0, -151.189)
301+
test(+0.0, +0.0, 3.123e-320)
302+
test(+0.0, +0.0, 2.253547e-318)
285303

286304
// If n is -0.0, retu 470D rn n
287305
test(-0.0, -0.0, 2.1)
288306
test(-0.0, -0.0, 5.5)
289307
test(-0.0, -0.0, -151.189)
308+
test(-0.0, -0.0, 3.123e-320)
309+
test(-0.0, -0.0, 2.253547e-318)
290310

291311
// Non-special values
292-
// { val l = List(2.1, 5.5, -151.189); for (n <- l; d <- l) println(s" test(${n % d}, $n, $d)") }
312+
// { val l = List(2.1, 5.5, -151.189, 3.123e-320, 2.253547e-318);
313+
// for (n <- l; d <- l) println(s" test(${n % d}, $n, $d)".toLowerCase()) }
293314
test(0.0, 2.1, 2.1)
294315
test(2.1, 2.1, 5.5)
295316
test(2.1, 2.1, -151.189)
317+
test(2.0696e-320, 2.1, 3.123e-320)
318+
test(1.772535e-318, 2.1, 2.253547e-318)
296319
test(1.2999999999999998, 5.5, 2.1)
297320
test(0.0, 5.5, 5.5)
298321
test(5.5, 5.5, -151.189)
322+
test(3.607e-321, 5.5, 3.123e-320)
323+
test(6.8103e-319, 5.5, 2.253547e-318)
299324
test(-2.0889999999999866, -151.189, 2.1)
300325
test(-2.688999999999993, -151.189, 5.5)
301326
test(-0.0, -151.189, -151.189)
327+
test(-1.94e-320, -151.189, 3.123e-320)
328+
test(-4.1349e-319, -151.189, 2.253547e-318)
329+
test(3.123e-320, 3.123e-320, 2.1)
330+
test(3.123e-320, 3.123e-320, 5.5)
331+
test(3.123e-320, 3.123e-320, -151.189)
332+
test(0.0, 3.123e-320, 3.123e-320)
333+
test(3.123e-320, 3.123e-320, 2.253547e-318)
334+
test(2.253547e-318, 2.253547e-318, 2.1)
335+
test(2.253547e-318, 2.253547e-318, 5.5)
336+
test(2.253547e-318, 2.253547e-318, -151.189)
337+
test(4.995e-321, 2.253547e-318, 3.123e-320)
338+
test(0.0, 2.253547e-318, 2.253547e-318)
302339
}
303340

304341
@Test

test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/FloatTest.scala

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class FloatTest {
7373
test(Float.NaN, Float.NaN, 2.1f)
7474
test(Float.NaN, Float.NaN, 5.5f)
7575
test(Float.NaN, Float.NaN, -151.189f)
76+
test(Float.NaN, Float.NaN, 8.858e-42f)
77+
test(Float.NaN, Float.NaN, 6.39164e-40f)
7678

7779
// If d is NaN, return NaN
7880
test(Float.NaN, Float.NaN, Float.NaN)
@@ -83,6 +85,8 @@ class FloatTest {
8385
test(Float.NaN, 2.1f, Float.NaN)
8486
test(Float.NaN, 5.5f, Float.NaN)
8587
test(Float.NaN, -151.189f, Float.NaN)
88+
test(Float.NaN, 8.858e-42f, Float.NaN)
89+
test(Float.NaN, 6.39164e-40f, Float.NaN)
8690

8791
// If n is PositiveInfinity, return NaN
8892
test(Float.NaN, Float.PositiveInfinity, Float.PositiveInfinity)
@@ -92,6 +96,8 @@ class FloatTest {
9296
test(Float.NaN, Float.PositiveInfinity, 2.1f)
9397
test(Float.NaN, Float.PositiveInfinity, 5.5f)
9498
test(Float.NaN, Float.PositiveInfinity, -151.189f)
99+
test(Float.NaN, Float.PositiveInfinity, 8.858e-42f)
100+
test(Float.NaN, Float.PositiveInfinity, 6.39164e-40f)
95101

96102
// If n is NegativeInfinity, return NaN
97103
test(Float.NaN, Float.NegativeInfinity, Float.PositiveInfinity)
@@ -101,56 +107,87 @@ class FloatTest {
101107
test(Float.NaN, Float.NegativeInfinity, 2.1f)
102108
test(Float.NaN, Float.NegativeInfinity, 5.5f)
103109
test(Float.NaN, Float.NegativeInfinity, -151.189f)
110+
test(Float.NaN, Float.NegativeInfinity, 8.858e-42f)
111+
test(Float.NaN, Float.NegativeInfinity, 6.39164e-40f)
104112

105113
// If d is PositiveInfinity, return n
106114
test(+0.0f, +0.0f, Float.PositiveInfinity)
107115
test(-0.0f, -0.0f, Float.PositiveInfinity)
108116
test(2.1f, 2.1f, Float.PositiveInfinity)
109117
test(5.5f, 5.5f, Float.PositiveInfinity)
110118
test(-151.189f, -151.189f, Float.PositiveInfinity)
119+
test(8.858e-42f, 8.858e-42f, Float.PositiveInfinity)
120+
test(6.39164e-40f, 6.39164e-40f, Float.PositiveInfinity)
111121

112122
// If d is NegativeInfinity, return n
113123
test(+0.0f, +0.0f, Float.NegativeInfinity)
114124
test(-0.0f, -0.0f, Float.NegativeInfinity)
115125
test(2.1f, 2.1f, Float.NegativeInfinity)
116126
test(5.5f, 5.5f, Float.NegativeInfinity)
117127
test(-151.189f, -151.189f, Float.NegativeInfinity)
128+
test(8.858e-42f, 8.858e-42f, Float.NegativeInfinity)
129+
test(6.39164e-40f, 6.39164e-40f, Float.NegativeInfinity)
118130

119131
// If d is +0.0, return NaN
120132
test(Float.NaN, +0.0f, +0.0f)
121133
test(Float.NaN, -0.0f, +0.0f)
122134
test(Float.NaN, 2.1f, +0.0f)
123135
test(Float.NaN, 5.5f, +0.0f)
124136
test(Float.NaN, -151.189f, +0.0f)
137+
test(Float.NaN, 8.858e-42f, +0.0f)
138+
test(Float.NaN, 6.39164e-40f, +0.0f)
125139

126140
// If d is -0.0, return NaN
127141
test(Float.NaN, +0.0f, -0.0f)
128142
test(Float.NaN, -0.0f, -0.0f)
129143
test(Float.NaN, 2.1f, -0.0f)
130144
test(Float.NaN, 5.5f, -0.0f)
131145
test(Float.NaN, -151.189f, -0.0f)
146+
test(Float.NaN, 8.858e-42f, -0.0f)
147+
test(Float.NaN, 6.39164e-40f, -0.0f)
132148

133149
// If n is +0.0, return n
134150
test(+0.0f, +0.0f, 2.1f)
135151
test(+0.0f, +0.0f, 5.5f)
136152
test(+0.0f, +0.0f, -151.189f)
153+
test(+0.0f, +0.0f, 8.858e-42f)
154+
test(+0.0f, +0.0f, 6.39164e-40f)
137155

138156
// If n is -0.0, return n
139157
test(-0.0f, -0.0f, 2.1f)
140158
test(-0.0f, -0.0f, 5.5f)
141159
test(-0.0f, -0.0f, -151.189f)
160+
test(-0.0f, -0.0f, 8.858e-42f)
161+
test(-0.0f, -0.0f, 6.39164e-40f)
142162

143163
// Non-special values
144-
// { val l = List(2.1f, 5.5f, -151.189f); for (n <- l; d <- l) println(s" test(${n % d}f, ${n}f, ${d}f)") }
164+
// { val l = List(2.1f, 5.5f, -151.189f, 8.858e-42f, 6.39164e-40f);
165+
// for (n <- l; d <- l) println(s" test(${n % d}f, ${n}f, ${d}f)".toLowerCase()) }
145166
test(0.0f, 2.1f, 2.1f)
146167
test(2.1f, 2.1f, 5.5f)
147168
test(2.1f, 2.1f, -151.189f)
169+
test(8.085e-42f, 2.1f, 8.858e-42f)
170+
test(6.1636e-40f, 2.1f, 6.39164e-40f)
148171
test(1.3000002f, 5.5f, 2.1f)
149172
test(0.0f, 5.5f, 5.5f)
150173
test(5.5f, 5.5f, -151.189f)
174+
test(5.11e-43f, 5.5f, 8.858e-42f)
175+
test(4.77036e-40f, 5.5f, 6.39164e-40f)
151176
test(-2.0890021f, -151.189f, 2.1f)
152177
test(-2.6889954f, -151.189f, 5.5f)
153178
test(-0.0f, -151.189f, -151.189f)
179+
test(-1.139e-42f, -151.189f, 8.858e-42f)
180+
test(-5.64734e-40f, -151.189f, 6.39164e-40f)
181+
test(8.858e-42f, 8.858e-42f, 2.1f)
182+
test(8.858e-42f, 8.858e-42f, 5.5f)
183+
test(8.858e-42f, 8.858e-42f, -151.189f)
184+
test(0.0f, 8.858e-42f, 8.858e-42f)
185+
test(8.858e-42f, 8.858e-42f, 6.39164e-40f)
186+
test(6.39164e-40f, 6.39164e-40f, 2.1f)
187+
test(6.39164e-40f, 6.39164e-40f, 5.5f)
188+
test(6.39164e-40f, 6.39164e-40f, -151.189f)
189+
test(1.417e-42f, 6.39164e-40f, 8.858e-42f)
190+
test(0.0f, 6.39164e-40f, 6.39164e-40f)
154191
}
155192

156193
@Test

0 commit comments

Comments
 (0)
0