-
Notifications
You must be signed in to change notification settings - Fork 396
WiP Redesign the encoding of Longs in JavaScript. #5205
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
base: main
Are you sure you want to change the base?
Conversation
7161430
to
2fe28e7
Compare
@gzm0 This is not yet ready for a code review. However, it is a good time to get your opinion on the overall new compilation scheme and architectural changes. WDYT? |
f35c5dd
to
3e51123
Compare
Some results from the
"Before" is #5204, and "After" is this PR. It's not dramatic, but it's a strong 10% performance improvement. |
My thoughts on this based on the PR description and a very brief look at the code:
|
I have considered it, and it's still in the cards. The only downside, AFAICT, is that the
These are two of the options I'm considering. We'll have to measure which is faster. |
32c14dd
to
d26f4c0
Compare
As a nice bonus, the IR checker now passes with `RuntimeLong`.
Sadly, that doesn't fix the GCC issue. It may have performance, especially in multi-module outputs, as we don't need to mutate a foreign var.
Overall, we make the flat representation of longs as a pair of
(lo, hi)
the representation, at the Emitter level. There are no more instances ofRuntimeLong
. The emitter flattens out all theLong
s as follows:long
becomes two local variables of typeint
.long
becomes two fields of typeint
.Array[Long]
is stored as anInt32Array
with twice as many elements, alternatinglo
andhi
words.long
are expanded as two parameters of typeint
.For the result of method parameters, there is a trick. That one is the most "debatable", in that I think there are contending alternatives that may be faster. When a method returns a
Long
, it stores thehi
word in a global variable$resHi
, then returns thelo
word. At call site, we read back the$resHi
global. We used a similar trick for non-inlined methods ofRuntimeLong
, with avar resultHi
field ofobject RuntimeLong
. Now this is moved to the emitter to take care of.All the methods of
RuntimeLong
explicitly take "expanded" versions of their parameters:abs
takes two parameters of typeInt
;add
takes 4. Shifts take 3 parameters of typeInt
: thelo
, thehi
, and the shift amount. The result, however, is aLong
.In order to allow them to construct
Long
results from their words, we introduce a magic methodRuntimeLong.pack(lo, hi)
, whose body is filled in by theDesugarer
with a specialTransient(PackLong(lo, hi))
. It cannot be the compiler, because we cannot serialize transients. And it cannot wait for the emitter, because the optimizer definitely wants to see thePackLong
s to unpack them. An alternative would be to introduce a newBinaryOp
, but I think that's worse because it bakes an implementation detail of the emitter into the IR. The fact that we can even do this PR is a testament to the current abstraction level of our IR.These changes significantly speed up even the SHA512 benchmark, even though it performs most computations on stack already anyway (the improvements must come from the arrays, in this case). I haven't measured benchmarks that extensively use
Long
fields yet, but I expect them to get dramatic speedups.For the optimizer, this makes things a lot simpler. Instead of having special-cases for
RuntimeLong
everywhere, we basically have a uniquewithSplitLong
method to deal with them. That method can split onePreTransform
of typelong
into twoPreTransform
s of typeint
s, so that they can be given to the inlined methods ofRuntimeLong
. We introduce a newLongPairReplacement
forLocalDef
s that aggregate a pair of(lo, hi)
LocalDef
s (typically the result of a splitPackLong
). As a nice bonus, the IR checker now passes withRuntimeLong
after the optimizer.One big caveat for now: Closure breaks the new encoding, sometimes. I think it gets confused by the
$resHi
variable and evaluation order of function params. For example if we pass the result of aLong
method to aLong
parameter, we emitDuring
y.bar(1)
, the method modifies$resHi
. It is then read right after to be passed as second argument tox.foo
.