-
Notifications
You must be signed in to change notification settings - Fork 398
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
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
1c41a25
Add CoreSpec.targetIsWebAssembly.
sjrd 7430166
Add LinkingInfo.isWebAssembly to the library.
sjrd f0a23d3
Wasm: Assert that we never write to `VarStorage.StructField`.
sjrd 3aebb38
Wasm: Handle RecordType's in the backend.
sjrd 265d283
Wasm: Handle Cast nodes coming from the optimizer.
sjrd 6be639a
Opt/Wasm: Take into account that Wasm never uses `RuntimeLong`s.
sjrd 78d12c4
Opt/Wasm: Do not destroy the only shape of ForIn that Wasm can handle.
sjrd 7bb17f1
Opt/Wasm: Disable intrinsics in Wasm mode for now.
sjrd b94ee99
Enable the optimizer with WebAssembly.
sjrd bc26f0f
Wasm: Use the original ArrayBuilder implementations.
sjrd ad162ac
Opt/Wasm: Enable the intrinsics that make sense for Wasm.
sjrd 7276564
Opt/Wasm: Add a number of Wasm-specific intrinsics and transients.
sjrd 08d7f97
Wasm: Dedicated implementation of `Double.hashCode`.
sjrd b7af05f
Opt/Wasm: Replace `Apply` by `ApplyStatically` when possible.
sjrd bae4c82
Wasm: Use `ApplyStatically` in the derived classes.
sjrd 2cbe367
Wasm: Remove the isEffectivelyFinal analysis and its use for `Apply`.
sjrd 828f90b
Wasm: Compute tableEntries in Preprocessor.
sjrd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Opt/Wasm: Add a number of Wasm-specific intrinsics and transients.
The motivation is mainly to get intrinsics for the bit-conversions between integers and floating point numbers, as well as for `numberLeadingZeros`. These are building blocks for many other low-level operations, and their JS-builtin-based implementation is really bad on Wasm for those use cases. Once we have the infrastructure for those as transients in the Wasm backend, we also take the opportunity to add a series of other methods that have a direct Wasm opcode equivalent.
- Loading branch information
commit 7276564fb8277de3e22b961232d76006ed6c96a6
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
209 changes: 209 additions & 0 deletions
209
linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmTransients.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
/* | ||
* Scala.js (https://www.scala-js.org/) | ||
* | ||
* Copyright EPFL. | ||
* | ||
* Licensed under Apache License 2.0 | ||
* (https://www.apache.org/licenses/LICENSE-2.0). | ||
* | ||
* See the NOTICE file distributed with this work for | ||
* additional information regarding copyright ownership. | ||
*/ | ||
|
||
package org.scalajs.linker.backend.wasmemitter | ||
|
||
import scala.annotation.switch | ||
|
||
import org.scalajs.ir.Position | ||
import org.scalajs.ir.Printers._ | ||
import org.scalajs.ir.Transformers._ | ||
import org.scalajs.ir.Traversers._ | ||
import org.scalajs.ir.Trees._ | ||
import org.scalajs.ir.Types._ | ||
|
||
import org.scalajs.linker.backend.webassembly.{Instructions => wa} | ||
|
||
/** Transients generated by the optimizer that only makes sense in Wasm. */ | ||
object WasmTransients { | ||
|
||
/** Wasm unary op. | ||
* | ||
* Wasm features a number of dedicated opcodes for operations that are not | ||
* in the IR, but only implemented in user space. We can see `WasmUnaryOp` | ||
* as an extension of `ir.Trees.UnaryOp` that covers those. | ||
* | ||
* Wasm unary ops always preserve pureness. | ||
*/ | ||
final case class WasmUnaryOp(op: WasmUnaryOp.Code, lhs: Tree) | ||
extends Transient.Value { | ||
import WasmUnaryOp._ | ||
|
||
val tpe: Type = resultTypeOf(op) | ||
|
||
def traverse(traverser: Traverser): Unit = | ||
traverser.traverse(lhs) | ||
|
||
def transform(transformer: Transformer, isStat: Boolean)( | ||
implicit pos: Position): Tree = { | ||
Transient(WasmUnaryOp(op, transformer.transformExpr(lhs))) | ||
} | ||
|
||
def wasmInstr: wa.SimpleInstr = (op: @switch) match { | ||
case I32Clz => wa.I32Clz | ||
case I32Ctz => wa.I32Ctz | ||
case I32Popcnt => wa.I32Popcnt | ||
|
||
case I64Clz => wa.I64Clz | ||
case I64Ctz => wa.I64Ctz | ||
case I64Popcnt => wa.I64Popcnt | ||
|
||
case F32Abs => wa.F32Abs | ||
|
||
case F64Abs => wa.F64Abs | ||
case F64Ceil => wa.F64Ceil | ||
case F64Floor => wa.F64Floor | ||
case F64Nearest => wa.F64Nearest | ||
case F64Sqrt => wa.F64Sqrt | ||
|
||
case I32ReinterpretF32 => wa.I32ReinterpretF32 | ||
case I64ReinterpretF64 => wa.I64ReinterpretF64 | ||
case F32ReinterpretI32 => wa.F32ReinterpretI32 | ||
case F64ReinterpretI64 => wa.F64ReinterpretI64 | ||
} | ||
|
||
def printIR(out: IRTreePrinter): Unit = { | ||
out.print("$") | ||
out.print(wasmInstr.mnemonic) | ||
out.printArgs(List(lhs)) | ||
} | ||
} | ||
|
||
object WasmUnaryOp { | ||
/** Codes are raw Ints to be able to write switch matches on them. */ | ||
type Code = Int | ||
|
||
final val I32Clz = 1 | ||
final val I32Ctz = 2 | ||
final val I32Popcnt = 3 | ||
|
||
final val I64Clz = 4 | ||
final val I64Ctz = 5 | ||
final val I64Popcnt = 6 | ||
|
||
final val F32Abs = 7 | ||
|
||
final val F64Abs = 8 | ||
final val F64Ceil = 9 | ||
final val F64Floor = 10 | ||
final val F64Nearest = 11 | ||
final val F64Sqrt = 12 | ||
|
||
final val I32ReinterpretF32 = 13 | ||
final val I64ReinterpretF64 = 14 | ||
final val F32ReinterpretI32 = 15 | ||
final val F64ReinterpretI64 = 16 | ||
|
||
def resultTypeOf(op: Code): Type = (op: @switch) match { | ||
case I32Clz | I32Ctz | I32Popcnt | I32ReinterpretF32 => | ||
IntType | ||
|
||
case I64Clz | I64Ctz | I64Popcnt | I64ReinterpretF64 => | ||
LongType | ||
|
||
case F32Abs | F32ReinterpretI32 => | ||
FloatType | ||
|
||
case F64Abs | F64Ceil | F64Floor | F64Nearest | F64Sqrt | F64ReinterpretI64 => | ||
DoubleType | ||
} | ||
} | ||
|
||
/** Wasm binary op. | ||
* | ||
* Wasm features a number of dedicated opcodes for operations that are not | ||
* in the IR, but only implemented in user space. We can see `WasmBinaryOp` | ||
* as an extension of `ir.Trees.BinaryOp` that covers those. | ||
* | ||
* Unsigned divisions and remainders exhibit always-unchecked undefined | ||
* behavior when their rhs is 0. It is up to code generating those transient | ||
* nodes to check for 0 themselves if necessary. | ||
* | ||
* All other Wasm binary ops preserve pureness. | ||
*/ | ||
final case class WasmBinaryOp(op: WasmBinaryOp.Code, lhs: Tree, rhs: Tree) | ||
extends Transient.Value { | ||
import WasmBinaryOp._ | ||
|
||
val tpe: Type = resultTypeOf(op) | ||
|
||
def traverse(traverser: Traverser): Unit = { | ||
traverser.traverse(lhs) | ||
traverser.traverse(rhs) | ||
} | ||
|
||
def transform(transformer: Transformer, isStat: Boolean)( | ||
implicit pos: Position): Tree = { | ||
Transient(WasmBinaryOp(op, transformer.transformExpr(lhs), | ||
transformer.transformExpr(rhs))) | ||
} | ||
|
||
def wasmInstr: wa.SimpleInstr = (op: @switch) match { | ||
case I32DivU => wa.I32DivU | ||
case I32RemU => wa.I32RemU | ||
case I32Rotl => wa.I32Rotl | ||
case I32Rotr => wa.I32Rotr | ||
|
||
case I64DivU => wa.I64DivU | ||
case I64RemU => wa.I64RemU | ||
case I64Rotl => wa.I64Rotl | ||
case I64Rotr => wa.I64Rotr | ||
|
||
case F32Min => wa.F32Min | ||
case F32Max => wa.F32Max | ||
|
||
case F64Min => wa.F64Min | ||
case F64Max => wa.F64Max | ||
} | ||
|
||
def printIR(out: IRTreePrinter): Unit = { | ||
out.print("$") | ||
out.print(wasmInstr.mnemonic) | ||
out.printArgs(List(lhs, rhs)) | ||
} | ||
} | ||
|
||
object WasmBinaryOp { | ||
/** Codes are raw Ints to be able to write switch matches on them. */ | ||
type Code = Int | ||
|
||
final val I32DivU = 1 | ||
final val I32RemU = 2 | ||
final val I32Rotl = 3 | ||
final val I32Rotr = 4 | ||
|
||
final val I64DivU = 5 | ||
final val I64RemU = 6 | ||
final val I64Rotl = 7 | ||
final val I64Rotr = 8 | ||
|
||
final val F32Min = 9 | ||
final val F32Max = 10 | ||
|
||
final val F64Min = 11 | ||
final val F64Max = 12 | ||
gzm0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def resultTypeOf(op: Code): Type = (op: @switch) match { | ||
case I32DivU | I32RemU | I32Rotl | I32Rotr => | ||
IntType | ||
|
||
case I64DivU | I64RemU | I64Rotl | I64Rotr => | ||
LongType | ||
|
||
case F32Min | F32Max => | ||
FloatType | ||
|
||
case F64Min | F64Max => | ||
DoubleType | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.