OpenSmalltalk / opensmalltalk-vm

Cross-platform virtual machine for Squeak, Pharo, Cuis, and Newspeak.
http://opensmalltalk.org/
Other
557 stars 111 forks source link

Mixed-arithmetic handling only works for float+int, not int+float #669

Open marceltaeumel opened 10 months ago

marceltaeumel commented 10 months ago

I played around with VM parameter 75. It does indeed show a change for the float-based primitives (i.e., 541, 542, ...) but not the integer-based primitives (i.e., 1, 2, 3, ...).

Is this expected behavior? In the VMMaker sources, it looks like it should work at least for comparisons (i.e., 3 to 8):

genPrimitiveLessThan
    ^coInterpreter primitiveDoMixedArithmetic
        ifTrue: [self
                genSmallIntegerComparison: JumpLess
                orDoubleComparison: #JumpFPGreater:
                invert: true]
        ifFalse: [self genSmallIntegerComparison: JumpLess]

Is genPrimitiveLessThan not the one in charge here? Hm....

So, debugging 3+4.0 shows that the primitive will fail, which it should not when mixed-arithmetic would work as expected.

nicolas-cellier-aka-nice commented 10 months ago

I don't remember, there are many possible path, what if not Jitted ? Certainly a case for exercising VM Simulator skills, or learn them...

nicolas-cellier-aka-nice commented 10 months ago

Note that the primitiveLessThan should fail: it only handle SmallInteger argument (and receiver).
It's bytecodePrimLessThan that eventually handle the automatic int -> float conversion.
Remember that we (the stack interpreter) short-circuit some message sends for some specialSelectors when we can recognize known operand types.
Would it be possible that the debugger fails to emulate the special send machinery?

nicolas-cellier-aka-nice commented 9 months ago

It appears that the JIT does thing differently than the (stack) interpreter.

The interpreter will try things in this order when interpreting the send of a special selector for arithmetic or comparison - for example in bytecodePrimAdd

In this scheme, if we add handling of conversions in SmallInteger primitives, we will just duplicate the work done in the 2nd stage of bytecodePrimAdd for no added value, but just a few more wasted cycles.

The JIT will only try to handle the case of both SmallIntegers

Then it will fallback to a message send which goes through the primitives.
and the integer primitives effectively do not handle conversion as we saw above.

The other way around, Float op: Integer, the float primitives do handle the conversion, and the JIT appears to be faster than Integer op: Float. The irony is that Jitted Integer op: Float is even slower than interpreted code.

I let Eliot explain while the second stage was skipped, but it might be that it consumes two many instructions: remember that the purpose is to inline the specialSelectors send, so we might end up with lot of instructions... If it doesn't pay statistically (except in micro benchmarks), then it's not worth it, because it will exhaust the space devoted to jitted code more rapidly (and remember, specialSelectors are sent from many places !).

We saw that adding conversions in integer primitives is not ideal, at least for the interpreted code.
So welcome to brighter ideas for having your cake and eat it too...