Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Enable the optimizer with WebAssembly. #4993

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Aug 25, 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
9 changes: 9 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,15 @@ def Tasks = [
'set Global/enableWasmEverywhere := true' \
'set scalaJSStage in Global := FullOptStage' \
$testSuite$v/test &&
sbtretry ++$scala \
'set Global/enableWasmEverywhere := true' \
'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withOptimizer(false))' \
$testSuite$v/test &&
sbtretry ++$scala \
'set Global/enableWasmEverywhere := true' \
'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withOptimizer(false))' \
'set scalaJSStage in Global := FullOptStage' \
$testSuite$v/test &&
sbtretry ++$scala \
'set Global/enableWasmEverywhere := true' \
testingExample$v/testHtml &&
Expand Down
22 changes: 20 additions & 2 deletions javalib/src/main/scala/java/lang/Double.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package java.lang
import java.lang.constant.{Constable, ConstantDesc}

import scala.scalajs.js
import scala.scalajs.runtime.linkingInfo

import Utils._

Expand Down Expand Up @@ -363,12 +364,29 @@ object Double {
@inline def isFinite(d: scala.Double): scala.Boolean =
!isNaN(d) && !isInfinite(d)

@inline def hashCode(value: scala.Double): Int =
FloatingPointBits.numberHashCode(value)
@inline def hashCode(value: scala.Double): Int = {
if (linkingInfo.isWebAssembly)
hashCodeForWasm(value)
else
FloatingPointBits.numberHashCode(value)
}

// See FloatingPointBits for the spec of this computation
@inline
private def hashCodeForWasm(value: scala.Double): Int = {
val bits = doubleToLongBits(value)
val valueInt = value.toInt
if (doubleToLongBits(valueInt.toDouble) == bits)
valueInt
else
Long.hashCode(bits)
}

// Wasm intrinsic
@inline def longBitsToDouble(bits: scala.Long): scala.Double =
FloatingPointBits.longBitsToDouble(bits)

// Wasm intrinsic
@inline def doubleToLongBits(value: scala.Double): scala.Long =
FloatingPointBits.doubleToLongBits(value)

Expand Down
4 changes: 3 additions & 1 deletion javalib/src/main/scala/java/lang/Float.scala
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,13 @@ object Float {
!isNaN(f) && !isInfinite(f)

@inline def hashCode(value: scala.Float): Int =
FloatingPointBits.numberHashCode(value)
Double.hashCode(value.toDouble)

// Wasm intrinsic
@inline def intBitsToFloat(bits: scala.Int): scala.Float =
FloatingPointBits.intBitsToFloat(bits)

// Wasm intrinsic
@inline def floatToIntBits(value: scala.Float): scala.Int =
FloatingPointBits.floatToIntBits(value)

Expand Down
7 changes: 7 additions & 0 deletions javalib/src/main/scala/java/lang/Integer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ object Integer {
@inline def toUnsignedLong(x: Int): scala.Long =
x.toLong & 0xffffffffL

// Wasm intrinsic
def bitCount(i: scala.Int): scala.Int = {
/* See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
*
Expand All @@ -219,10 +220,12 @@ object Integer {
(((t2 + (t2 >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24
}

// Wasm intrinsic
@inline def divideUnsigned(dividend: Int, divisor: Int): Int =
if (divisor == 0) 0 / 0
else asInt(asUint(dividend) / asUint(divisor))

// Wasm intrinsic
@inline def remainderUnsigned(dividend: Int, divisor: Int): Int =
if (divisor == 0) 0 % 0
else asInt(asUint(dividend) % asUint(divisor))
Expand Down Expand Up @@ -263,15 +266,18 @@ object Integer {
reverseBytes((k & 0x0F0F0F0F) << 4 | (k >> 4) & 0x0F0F0F0F)
}

// Wasm intrinsic
@inline def rotateLeft(i: scala.Int, distance: scala.Int): scala.Int =
(i << distance) | (i >>> -distance)

// Wasm intrinsic
@inline def rotateRight(i: scala.Int, distance: scala.Int): scala.Int =
(i >>> distance) | (i << -distance)

@inline def signum(i: scala.Int): scala.Int =
if (i == 0) 0 else if (i < 0) -1 else 1

// Intrinsic, fallback on actual code for non-literal in JS
@inline def numberOfLeadingZeros(i: scala.Int): scala.Int = {
if (linkingInfo.esVersion >= ESVersion.ES2015) js.Math.clz32(i)
else clz32Dynamic(i)
Expand All @@ -296,6 +302,7 @@ object Integer {
}
}

// Wasm intrinsic
@inline def numberOfTrailingZeros(i: scala.Int): scala.Int =
if (i == 0) 32
else 31 - numberOfLeadingZeros(i & -i)
Expand Down
9 changes: 7 additions & 2 deletions javalib/src/main/scala/java/lang/Long.scala
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,11 @@ object Long {
@inline def compareUnsigned(x: scala.Long, y: scala.Long): scala.Int =
compare(x ^ SignBit, y ^ SignBit)

// Intrinsic
// Intrinsic, except for JS when using bigint's for longs
def divideUnsigned(dividend: scala.Long, divisor: scala.Long): scala.Long =
divModUnsigned(dividend, divisor, isDivide = true)

// Intrinsic
// Intrinsic, except for JS when using bigint's for longs
def remainderUnsigned(dividend: scala.Long, divisor: scala.Long): scala.Long =
divModUnsigned(dividend, divisor, isDivide = false)

Expand Down Expand Up @@ -408,6 +408,7 @@ object Long {
if (lo != 0) 0 else Integer.lowestOneBit(hi))
}

// Wasm intrinsic
@inline
def bitCount(i: scala.Long): scala.Int = {
val lo = i.toInt
Expand Down Expand Up @@ -436,10 +437,12 @@ object Long {
private def makeLongFromLoHi(lo: Int, hi: Int): scala.Long =
(lo.toLong & 0xffffffffL) | (hi.toLong << 32)

// Wasm intrinsic
@inline
def rotateLeft(i: scala.Long, distance: scala.Int): scala.Long =
(i << distance) | (i >>> -distance)

// Wasm intrinsic
@inline
def rotateRight(i: scala.Long, distance: scala.Int): scala.Long =
(i >>> distance) | (i << -distance)
Expand All @@ -452,13 +455,15 @@ object Long {
else 1
}

// Wasm intrinsic
@inline
def numberOfLeadingZeros(l: scala.Long): Int = {
val hi = (l >>> 32).toInt
if (hi != 0) Integer.numberOfLeadingZeros(hi)
else Integer.numberOfLeadingZeros(l.toInt) + 32
}

// Wasm intrinsic
@inline
def numberOfTrailingZeros(l: scala.Long): Int = {
val lo = l.toInt
Expand Down
10 changes: 10 additions & 0 deletions javalib/src/main/scala/java/lang/Math.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,30 @@ object Math {

@inline def abs(a: scala.Int): scala.Int = if (a < 0) -a else a
@inline def abs(a: scala.Long): scala.Long = if (a < 0) -a else a

// Wasm intrinsics
@inline def abs(a: scala.Float): scala.Float = js.Math.abs(a).toFloat
@inline def abs(a: scala.Double): scala.Double = js.Math.abs(a)

@inline def max(a: scala.Int, b: scala.Int): scala.Int = if (a > b) a else b
@inline def max(a: scala.Long, b: scala.Long): scala.Long = if (a > b) a else b

// Wasm intrinsics
@inline def max(a: scala.Float, b: scala.Float): scala.Float = js.Math.max(a, b).toFloat
@inline def max(a: scala.Double, b: scala.Double): scala.Double = js.Math.max(a, b)

@inline def min(a: scala.Int, b: scala.Int): scala.Int = if (a < b) a else b
@inline def min(a: scala.Long, b: scala.Long): scala.Long = if (a < b) a else b

// Wasm intrinsics
@inline def min(a: scala.Float, b: scala.Float): scala.Float = js.Math.min(a, b).toFloat
@inline def min(a: scala.Double, b: scala.Double): scala.Double = js.Math.min(a, b)

// Wasm intrinsics
@inline def ceil(a: scala.Double): scala.Double = js.Math.ceil(a)
@inline def floor(a: scala.Double): scala.Double = js.Math.floor(a)

// Wasm intrinsic
def rint(a: scala.Double): scala.Double = {
val rounded = js.Math.round(a)
val mod = a % 1.0
Expand All @@ -60,7 +68,9 @@ object Math {
@inline def round(a: scala.Float): scala.Int = js.Math.round(a).toInt
@inline def round(a: scala.Double): scala.Long = js.Math.round(a).toLong

// Wasm intrinsic
@inline def sqrt(a: scala.Double): scala.Double = js.Math.sqrt(a)

@inline def pow(a: scala.Double, b: scala.Double): scala.Double = js.Math.pow(a, b)

@inline def exp(a: scala.Double): scala.Double = js.Math.exp(a)
Expand Down
34 changes: 34 additions & 0 deletions library/src/main/scala/scala/scalajs/LinkingInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,40 @@ object LinkingInfo {
def useECMAScript2015Semantics: Boolean =
linkingInfo.assumingES6 // name mismatch for historical reasons

/** Whether we are linking to WebAssembly.
*
* This property can be used to delegate to different code paths optimized
* for WebAssembly rather than for JavaScript.
*
* ---
*
* This ends up being constant-folded to a constant at link-time. So
* constant-folding, inlining, and other local optimizations can be
* leveraged with this "constant" to write alternatives that can be
* dead-code-eliminated.
*
* A typical usage of this method is:
* {{{
* if (isWebAssembly)
* implementationOptimizedForWebAssembly()
* else
* implementationOptimizedForJavaScript()
* }}}
*
* At link-time, `isWebAssembly` will either be a constant
* true, in which case the above snippet folds into
* {{{
* implementationOptimizedForWebAssembly()
* }}}
* or a constant false, in which case it folds into
* {{{
* implementationOptimizedForJavaScript()
* }}}
*/
@inline
def isWebAssembly: Boolean =
linkingInfo.isWebAssembly

/** Constants for the value of `esVersion`. */
object ESVersion {
/** ECMAScrîpt 5.1. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ sealed trait LinkingInfo extends js.Object {
*/
val assumingES6: Boolean

/** Whether we are linking to WebAssembly.
*
* This property can be used to delegate to different code paths optimized
* for WebAssembly rather than for JavaScript.
*/
val isWebAssembly: Boolean

/** Whether we are linking in production mode. */
val productionMode: Boolean

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config)
require(moduleKind != ModuleKind.ESModule,
s"Cannot use module kind $moduleKind with the Closure Compiler")

require(!targetIsWebAssembly,
s"A JavaScript backend cannot be used with CoreSpec targeting WebAssembly")

private[this] val emitter = {
// Note that we do not transfer `minify` -- Closure will do its own thing anyway
val emitterConfig = Emitter.Config(config.commonConfig.coreSpec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ final class BasicLinkerBackend(config: LinkerBackendImpl.Config)

import BasicLinkerBackend._

require(!coreSpec.targetIsWebAssembly,
s"A JavaScript backend cannot be used with CoreSpec targeting WebAssembly")

private[this] var totalModules = 0
private[this] val rewrittenModules = new AtomicInteger(0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ final class WebAssemblyLinkerBackend(config: LinkerBackendImpl.Config)
"The WebAssembly backend only supports strict float semantics."
)

require(coreSpec.targetIsWebAssembly,
s"A WebAssembly backend cannot be used with CoreSpec targeting JavaScript")

val loaderJSFileName = OutputPatternsImpl.jsFile(config.outputPatterns, "__loader")

private val fragmentIndex = new SourceMapWriter.Index
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ private[emitter] object CoreJSLib {
val linkingInfo = objectFreeze(ObjectConstr(List(
str("esVersion") -> int(esVersion.edition),
str("assumingES6") -> bool(useECMAScript2015Semantics), // different name for historical reasons
str("isWebAssembly") -> bool(false),
str("productionMode") -> bool(productionMode),
str("linkerVersion") -> str(ScalaJSVersions.current),
str("fileLevelThis") -> This()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
genGlobalID.forStaticField(name.name),
origName,
isMutable = true,
transformType(ftpe),
transformFieldType(ftpe),
wa.Expr(List(genZeroOf(ftpe)))
)
ctx.addGlobal(global)
Expand Down Expand Up @@ -350,7 +350,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
watpe.StructField(
genFieldID.forClassInstanceField(field.name.name),
makeDebugName(ns.InstanceField, field.name.name),
transformType(field.ftpe),
transformFieldType(field.ftpe),
isMutable = true // initialized by the constructors, so always mutable at the Wasm level
)
}
Expand Down Expand Up @@ -769,7 +769,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
clazz.pos
)
val classCaptureParams = jsClassCaptures.map { cc =>
fb.addParam("cc." + cc.name.name.nameString, transformLocalType(cc.ptpe))
fb.addParam("cc." + cc.name.name.nameString, transformParamType(cc.ptpe))
}
fb.setResultType(watpe.RefType.any)

Expand Down Expand Up @@ -1147,7 +1147,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
if (namespace.isStatic)
None
else if (isHijackedClass)
Some(transformType(BoxedClassToPrimType(className)))
Some(transformPrimType(BoxedClassToPrimType(className)))
else
Some(transformClassType(className).toNonNullable)

Expand Down Expand Up @@ -1183,7 +1183,7 @@ class ClassEmitter(coreSpec: CoreSpec) {
val receiverParam = fb.addParam(thisOriginalName, watpe.RefType.any)
val argParams = method.args.map { arg =>
val origName = arg.originalName.orElse(arg.name.name)
fb.addParam(origName, TypeTransformer.transformLocalType(arg.ptpe))
fb.addParam(origName, TypeTransformer.transformParamType(arg.ptpe))
}
fb.setResultTypes(TypeTransformer.transformResultType(method.resultType))
fb.setFunctionType(ctx.tableFunctionType(methodName))
Expand Down
Loading