Kotlin/Wasm の interface dispatch に何で ref.cast いるんですか?
質問したけど、よく考えたらそれはそうって感じだった
Rikito Taniguchi
1 day ago
Hi, I'm delving into Kotlin/Wasm internals out of curiosity, really a great work! (I also watched/read the presentation on the introduction to Kotlin/Wasm at Wasm/IO, which greatly helped me understand the internals :slightly_smiling_face: https://seb.deleuze.fr/introducing-kotlin-wasm/).
I have a question regarding interface dispatching.
When we have a class like class Derived: Base , it's vtable's type is sth like (field (ref $Derived.vtable___type_30))
.
On the other hand, the itable's type is always (field (ref null struct))
and an interface dispatch always requires ref.cast
at the call site, as mentioned in the presentation.
My question is: Why don't we define a class with a concrete itable type, and remove the ref.cast at call site?
Can we replace val classITableRefGcType = WasmRefNullType(WasmHeapType.Simple.Struct)
with val classITableRefGcType = WasmRefNullType(WasmHeapType.Type(context.referenceClassITableGcType(symbol)))
in https://github.com/JetBrains/kotlin/blob/c4fc0b919dfe9b746f9ecfcc08668fa05b839064/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt#L376 ?
Slava Kuzmich [JetBrains]
:webassembly: 1 day ago
Hi! We currently lower all static interface types to kotlin.Any , requiring at least one cast at the call site.
In cases where we know concrete static class type, we bypass the itable and generate either direct (final) or vtable (open) call. (edited)
:+1:
1
Slava Kuzmich [JetBrains]
:webassembly: 1 day ago
The idea of having different wasm type for interfaces is indeed interesting, but requires bigger changes to the backend.
Rikito Taniguchi
5 minutes ago
Thank you for your response! Oh yeah, that's right, I realize I may not have fully grasped the concept of interface dispatch :sweat_smile:
I was curious it might be possible to do that because J2CL-generated wasm has concrete itable types
For example, for class and interface like
interface Animal {
public void sound();
}
class Cat implements Animal {
public void sound() {}
}
Cat 's type and itable are like
(type $com.google.j2cl.samples.wasm.Cat (sub $java.lang.Object (struct
(field $vtable (ref $com.google.j2cl.samples.wasm.Cat.vtable))
(field $itable (ref $com.google.j2cl.samples.wasm.Cat.itable))
(field $$systemIdentityHashCode@java.lang.Object (mut i32))
))
)
(type $com.google.j2cl.samples.wasm.Cat.itable (sub $itable (struct
(field $slot0 (ref $com.google.j2cl.samples.wasm.Animal.vtable))
(field $slot1 (ref null struct))
(field $slot2 (ref null struct))
(field $slot3 (ref null struct))
(field $slot4 (ref null struct))
(field $slot5 (ref null struct))
(field $slot6 (ref null struct))
)))
Rikito Taniguchi
5 minutes ago
However, it turned out J2CL still requires casting at call-site. :+1:
As you mentioned, the static interfaces are represented as java.lang.Object (like kotlin.Any for Kotlin). And not sure which virtual methods are available from it's vtable at interface call site.
Rikito Taniguchi
4 minutes ago
For a method like
private static void doSound(Animal animal) {
animal.sound();
}
it's gonna be compiled to like
(func $m_doSound__com_google_j2cl_samples_wasm_Animal__void@com.google.j2cl.samples.wasm.HelloWorld
(param $animal (ref null $java.lang.Object))
(call_ref $function.m_sound__void
(ref.as_non_null (local.get $animal))
(struct.get
$com.google.j2cl.samples.wasm.Animal.vtable
$m_sound__void
(ref.cast
(ref $com.google.j2cl.samples.wasm.Animal.vtable)
(struct.get
$itable
$slot0
(struct.get
$java.lang.Object
$itable
(local.get $animal)
)
)
)
)
)
)