[Vm-dev] Max number of method arguments

Eliot Miranda eliot.miranda at gmail.com
Tue Jan 8 22:39:42 UTC 2019


Hi Nicolas,

On Tue, Jan 8, 2019 at 2:30 PM Nicolas Cellier <
nicolas.cellier.aka.nice at gmail.com> wrote:

>
> Le mar. 8 janv. 2019 à 23:07, Nicolas Cellier <
> nicolas.cellier.aka.nice at gmail.com> a écrit :
>
>>
>> Le mar. 8 janv. 2019 à 22:51, Nicolas Cellier <
>> nicolas.cellier.aka.nice at gmail.com> a écrit :
>>
>>> Hi all,
>>> particularly Clement and Eliot,
>>>
>>> One of the most annoying limit of bytecode is the number of arguments
>>> (<16 in V3), not so much annoying for pure Smalltalk, but certainly so for
>>> FFI (FORTRAN 77 lacks structures so existing code base often have functions
>>> with many arguments).
>>> For scientific Smalltalk, some of those old FORTRAN libraries are still
>>> around nowadays (LAPACK is an example).
>>>
>>> I patched the old Squeak compiler in Smallapack to workaround this
>>> limitation (it was easy enough to pass a single Array, and invoke FFI with
>>> many args).
>>> In modern Pharo flavour, this is more involved with the new OpalCompiler
>>> (iit does not seem to be designed for extensibility as it seems necessary
>>> to patch many pieces/subclasses for a single feature change...).
>>>
>>> But we now have Sista V1 bytecodes which removed a lot of limitations (#
>>> inst vars, #literals, max jump offset ...). Alas I don't see a modified
>>> limit for number of arguments (source:
>>> https://hal.inria.fr/hal-01088801/document a bytecode set for adaptive
>>> optimization): there is still a limit of 4 reserved bits in compiled method
>>> header documented in link above.
>>> Though, there is an adjacent unused bit now...
>>> In Squeak,/Pharo, EncoderForSistaV1>>genSend:numArgs: suggests that the
>>> limit is 31 (sic)
>>>
>>>     (nArgs < 0 or: [nArgs > 31]) ifTrue:
>>>         [^self outOfRangeError: 'numArgs' index: nArgs range: 0 to: 31
>>> "!!"].
>>>
>>> or at least 2047 if we believe code below:
>>>
>>>     "234        11101010    i i i i i j j j    Send Literal Selector
>>> #iiiii (+ Extend A * 32) with jjj (+ Extend B * 8) Arguments"
>>>
>>>
>>> https://github.com/pharo-project/pharo/blob/50992c3e5fed790b7e660954aee983f4681da658/src/Kernel-BytecodeEncoders/EncoderForSistaV1.class.st
>>>
>>> Pharo also limit the numArgs to 15 whatever the encoding in
>>> CompiledMethod>>newBytes:trailerBytes:nArgs:nTemps:nStack:nLits:
>>> primitive:
>>>
>>> https://github.com/pharo-project/pharo/blob/50992c3e5fed790b7e660954aee983f4681da658/src/Kernel/CompiledMethod.class.st
>>>
>>> But Squeak does not limit nArgs at all in
>>>
>>> EncoderForSistaV1>>computeMethodHeaderForNumArgs:numTemps:numLits:primitive:
>>>
>>> So my questions:
>>> - is that doc up-to-date?
>>> - if so, couldn't we expand the limit to 31 args by using the unused bit?
>>>
>>> Note: there is another unused bit in V3 (not adjacent), and the double
>>> extended (send) byte code has room for 31 args in V3 too, since only the
>>> first 3 bits of second byte encode the type of operation...
>>>
>>
>> Confirmed in VMMaker: max num args is still 15 in CompiledMethod header
>>
>> StackInterpreter>>argumentCountOfMethodHeader: header
>>     <api>
>>     ^header >> MethodHeaderArgCountShift bitAnd: 16rF
>>
>> I see that this decoding is shared whatever byte-code alternative...
>>
>
> And the other unused bit (flag, position 30, 0-based) is not always
> unused... See maybeFlagMethodAsInterpreted:
>
>          realHeader := realHeader bitOr: (objectMemory integerObjectOf: 1
> << MethodHeaderFlagBitPosition).
>
> Why do we flag the interpreted methods?
>

This is for me for profiling.  Because Cog is a hybrid interpreter+JIT it
can use the policy of avoiding JITting a method until it is found in the
method lookup cache, or (to make sure that doits run at full speed)
evaluated by one of the withArgs:executeMethod: primitives.  This means
that the JIT doesn't waste time JITing huge methods that are only evaluated
one e on system startup.  JITting is like a very slow interpretation, so it
doesn't make sense to JIT a method that is simply a long sequence of
statements if it is only used once; it will be much quicker and use much
less space to simply interpret it once, instead of JITing it and then
evaluating it once.  Further, the VM won't JIOT anything with a certain
number of literals anyway.  This is a startup parameter and defaults to 60
literals.  Any method with more than 60 literals won't be JITted.

But I wanted to be sure that this policy was not affecting any methods that
are performance critical.  So I added the facility top flag interpreted
methods as I can see which methods are executed that the VM chooses not to
JIT.

Back in 2009/2010 I did indeed use the facility to check and found that my
intuitions were correct and that the policy is a good one (although our
including of the selectors for inlined messages such as ifTrue:ifFaklse: et
al might mean we want to increase the limit on number fo literals for
JITing as little, 65 perhaps?).

I had the impression that the offset was off by 1
>
>     MethodHeaderFlagBitPosition := 28 + tagBits.
>
> but we are tricky, since integerObjectOf: will perform the missing 1-bit
> shift...
> This way, Newspeak that is using both bits 29-30 has the right offset.
> Ouch.
> I now see no room left in that header...
>

_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20190108/9099a430/attachment-0001.html>


More information about the Vm-dev mailing list