-
Notifications
You must be signed in to change notification settings - Fork 401
Description
Currently, all class fields are mutable at the Wasm level: the genFunctionID.newDefault function creates a class instance with fields initialized to their zero values, and then the constructor initializes them (by Assign at the IR level).
scala-js/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala
Lines 351 to 358 in 4c9494e
| val fields = classInfo.allFieldDefs.map { field => | |
| watpe.StructField( | |
| genFieldID.forClassInstanceField(field.name.name), | |
| makeDebugName(ns.InstanceField, field.name.name), | |
| transformFieldType(field.ftpe), | |
| isMutable = true // initialized by the constructors, so always mutable at the Wasm level | |
| ) | |
| } |
Fields being mutable means that accessing a field is an impure operation at the Wasm level, causing us to miss some optimization opportunities.
In fact, making itables immutable has improved performance in some benchmarks, as seen in #5038.
We may want to "merge" the genFunctionID.newDefault function with the constructor: The fused constructor would:
- Set up the vtable (virtual method table) and itable (interface table)
- Initialize the fields
- Instead of filling fields with zero values, the
wasmemitterwould transform the RHS of fieldAssignnodes to become the initial values of the fields.
- Instead of filling fields with zero values, the
- Execute
struct.newfor instantiation (asnewDefaultcurrently does)
Alternatively, should we modify the IR structure somehow? It appears that these Assign operations in the constructor originate from Scala compiler, not from GenJSCode🤷. In this case, making changes at the IR level seems introduce more complications than it solves.