Using my asm-generator with Exypery.

bryce at kampjes.demon.co.uk bryce at kampjes.demon.co.uk
Tue May 8 20:18:56 UTC 2007


sig writes:
 > Consider following example:
 > 
 > you write the code:
 > ------
 > PrimitiveSmallIntegers>>add: a with: b
 > | result |
 >    self pragma: #inline.  "tell the parser to not generate prologue/epilogue"
 > 
 > " adding pair of smallintegers (assuming a and b is smallintegers!!) "
 > 
 > ^ ( result := a + b ) ifNotOverflow: [ result - 1 ] else: [ self call:
 > #coerceToBigIntsAndAdd:with: with: a with: b ]
 > -------
 > and then in
 > 
 > IntermediateGenerator>>generate:add:
 > ^ self parsedMethod: #add:with:  "return inlined form for adding two operands"
 > 
 > It provides an easy way for extending Exupery with all basic numerics
 > functionality (like big integers/floats), without jungles of hardly to
 > write and hardly to manage mid-level code.

Generating intermediate is not hard, and has good support for unit
testing.

For use in Exupery primitives must be implemented in the mid level
code because that exists to allow for easy optimization of waste
across bytecodes (or primitives).

For instance "a < b ifTrue: [...]"

Will remove the conversion to then from a boolean object for the
SmallInteger case. Only if a floating point < primitive was inlined
into medium level intermediate could this optimisation be done.

Which is similar to removing the temporary float created in "1.0 + 2.0
+ 3.0". 

There are several different kinds of primitive: 
* Small frequently called ones. e.g. #at: and #at:put: 
* Larger important clean primitives (possibly large integer stuff) 
* Larger or infrequent unclean primitives
* Interfacing to C 
* Performance optimizations.

The small frequently called primitives are compiled into code
customized for each receiver class. There's only a handful of them
and most of them are inlined into interpret() in the interpreter.
interpret () accounts for 70% of the time in the benchmarks I've
analyzed so performance wise it's the area to focus on.

The larger clean primitives could be quickly implemented by calling
a C helper function. These could be either compiled as part of
their methods or inlined.

The larger infrequent unclean primitives are operations that may
change the active context. Here Exupery would need to save all
the state back into the context, call the C helper function, then
load it back and potentially enter a different method. The only
overhead that can be saved is some of the message sends by using
PICs. 

Interfacing to C could use some support. I haven't looked at it.

Some of the performance optimizations should be replaced with
normal Smalltalk as Exupery get's faster. Some should stay as
tolerable interpreted performance is still nice.


To be inlined a primitive must be able to execute in both it's
senders context (in case it isn't inlined) and in it's senders
context (in case it is inlined). If you send, or GC from your
own context you must save the stack pointer and context state
while if you're in the senders context you mustn't save the
stack pointer. This is easy to implement using generated code
but not using a template.

Bryce


More information about the Exupery mailing list