-
Notifications
You must be signed in to change notification settings - Fork 396
Merge the itables into the corresponding vtables. #5154
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the design decision in this PR that runtime value memory footprint optimization is more important than type metadata memory footprint optimization.
(in a memory heavy program, I'd expect #values >> #types).
@@ -229,12 +228,14 @@ we get | |||
```wat | |||
(type $v.A (sub $v.java.lang.Object (struct | |||
;; ... class metadata | |||
;; ... itable slots |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to check my understanding: The itable slots must be before the method dispatch references for the vtable subtyping relaionship to hold.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's right. It's stronger than the Wasm subtyping, though. Even if we were targeting native code, where subtyping is not a thing, we would have to do it this way. All classes have the same number of itable slots, but they have a different number of vtable method pointers. I we put method pointers first, either the itable slots would not be at a constant offset, or we would have to pad all method pointer tables to have the same (maximum) length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One concern is that we might want to change an algorithm for compressing the itables to something that have different slots for classes (something like scala-native does scala-native/scala-native#4085). But we can just revert this change if it's a problem 👍
linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/README.md
Outdated
Show resolved
Hide resolved
4f2b39c
to
ce5577c
Compare
This is a trade-off on memory and code size. The big advantage is that run-time instances of our classes use one fewer word each, since we do not have an `itables` field anymore. There could be some execution time improvements as a side effect, since we have one fewer word to initialize. There are two disadvantages, but IMO they are comparatively minor: * We cannot use the "one empty itables to rule them all" trick anymore. Classes that implement no interfaces need to list their N `null` values. This has both a code size impact and a constant run-time memory cost. * We cannot share the itables of array classes either. This has no impact on code size (there is a single place in the code where we initialize those fields), but it does add run-time footprint to the vtables of each array type that gets created. Interface method calls are not impacted, neither in the code we generate, nor in their run-time performance. Virtual method calls are only impacted insofar as the index of virtual method pointers is bumped up by the number of itable slots. This can add one more byte to some calls, due to the var-length encoding. If we get very unlucky, it could also push some vtable method pointers past a pre-fetch boundary, slowing down the calls at run-time. But I don't really think that's plausible.
ce5577c
to
1b989f2
Compare
@@ -229,12 +228,14 @@ we get | |||
```wat | |||
(type $v.A (sub $v.java.lang.Object (struct | |||
;; ... class metadata | |||
;; ... itable slots |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One concern is that we might want to change an algorithm for compressing the itables to something that have different slots for classes (something like scala-native does scala-native/scala-native#4085). But we can just revert this change if it's a problem 👍
This is a trade-off on memory and code size.
The big advantage is that run-time instances of our classes use one fewer word each, since we do not have an
itables
field anymore. There could be some execution time improvements as a side effect, since we have one fewer word to initialize.There are two disadvantages, but IMO they are comparatively minor:
null
values. This has both a code size impact and a constant run-time memory cost.Interface method calls are not impacted, neither in the code we generate, nor in their run-time performance. Virtual method calls are only impacted insofar as the index of virtual method pointers is bumped up by the number of itable slots. This can add one more byte to some calls, due to the var-length encoding. If we get very unlucky, it could also push some vtable method pointers past a pre-fetch boundary, slowing down the calls at run-time. But I don't really think that's plausible.
Review by @gzm0 and @tanishiking.