Imperative compiler

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Imperative compiler

Herby Vojčík
I am playing with different Compiler... the present one is nicely
functional, but it can't inline `true ifTrue: [^42]`, producing
unnecessary try/catch.

Though ditching "functional" and bringing in "imperative" is probably
not quite beuatiful, maybe it can produce more efficient code (trying to
use if/then/else and assign to variable instead of IIFEs).

For the time being, it is able to compile

foo: x
|y|
        y:=1.
        ^[y<10] whileTrue: [y:=(y<3) ifTrue: [y*3] ifFalse: [^y*2]]

to

function(x){var self=this;
        var $3,$8;
        var $early={}; try{
        var y=nil;
        y=(1);
        for(;;){var $2=nil;
        if(y.klass === smalltalk.Number) {$3=(y<(10));
        } else {$3=smalltalk.send(y, "__lt", [(10)]);
        }
        if (!($3)) {return $2;
        break}
        if(y.klass === smalltalk.Number) {$8=(y<(3));
        } else {$8=smalltalk.send(y, "__lt", [(3)]);
        }
        if($8.klass === smalltalk.Boolean) if($8) {if(y.klass ===
smalltalk.Number) {y=(y*(3));
        } else {y=smalltalk.send(y, "__star", [(3)]);
        }
        } else {if(y.klass === smalltalk.Number) {return (y*(2));
        } else {return smalltalk.send(y, "__star", [(2)]);
        }
        } else {y=smalltalk.send($8, "_ifTrue_ifFalse_",
[(function(){if(y.klass === smalltalk.Number) {return (y*(3));
                } else {return smalltalk.send(y, "__star", [(3)]);
                }
                }), (function(){if(y.klass === smalltalk.Number) {throw $early=[(y*(2))];
                } else {throw $early=[smalltalk.send(y, "__star", [(2)])];
                }
                })]);
        }
        $2=y;
        }
        } catch(e) {if(e===$early) return e[0]; throw e}}

(BTW, shouldn't whileTrue: return value (of the last body block
evaulated or nil) or I am just making things up? In the present one, the
while just quit without passing any value (that is, it returns undefined))

I don't know this alternative compiler will be of any value at all, not
even if it would be faster (paradigmatic change, it can only be tested
after finished), but I'm interested if someone would be willing to look
over it and test it to find some bugs... I'll upload it to github once
finished (it's just a matter of a little more work, from the principles
point of view all is clear).

Herby

P.S.: I checked the above thing compiled with old and new and new is two
times faster (Chrome18, Win7)... but it is probably not the
representative pattern, if has lots of ifTrues and whileTrues and other
inlineable code...

P.P.S.: Of course, old compiler after the fix of GH-178, otherwise it
fails to compile.
Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

Nicolas Petton

On Apr 28, 2012, at 2:24 PM, Herby Vojčík wrote:

(BTW, shouldn't whileTrue: return value (of the last body block evaulated or nil) or I am just making things up? In the present one, the while just quit without passing any value (that is, it returns undefined))

Nope, I think it's implemented the same way in Pharo (but now that you mention it, I'll check anyway)

Nico


Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

Nicolas Petton
In reply to this post by Herby Vojčík
Hi!

That's indeed very interesting. It is true that early returns in Amber are expensive.  
I'll be happy to follow your progress here to see where your compiler is going :)

Nico

On Apr 28, 2012, at 2:24 PM, Herby Vojčík wrote:

I am playing with different Compiler... the present one is nicely functional, but it can't inline `true ifTrue: [^42]`, producing unnecessary try/catch.

Though ditching "functional" and bringing in "imperative" is probably not quite beuatiful, maybe it can produce more efficient code (trying to use if/then/else and assign to variable instead of IIFEs).

For the time being, it is able to compile

foo: x
|y|
y:=1.
^[y<10] whileTrue: [y:=(y<3) ifTrue: [y*3] ifFalse: [^y*2]]

to

function(x){var self=this;
var $3,$8;
var $early={}; try{
var y=nil;
y=(1);
for(;;){var $2=nil;
if(y.klass === smalltalk.Number) {$3=(y<(10));
} else {$3=smalltalk.send(y, "__lt", [(10)]);
}
if (!($3)) {return $2;
break}
if(y.klass === smalltalk.Number) {$8=(y<(3));
} else {$8=smalltalk.send(y, "__lt", [(3)]);
}
if($8.klass === smalltalk.Boolean) if($8) {if(y.klass === smalltalk.Number) {y=(y*(3));
} else {y=smalltalk.send(y, "__star", [(3)]);
}
} else {if(y.klass === smalltalk.Number) {return (y*(2));
} else {return smalltalk.send(y, "__star", [(2)]);
}
} else {y=smalltalk.send($8, "_ifTrue_ifFalse_", [(function(){if(y.klass === smalltalk.Number) {return (y*(3));
} else {return smalltalk.send(y, "__star", [(3)]);
}
}), (function(){if(y.klass === smalltalk.Number) {throw $early=[(y*(2))];
} else {throw $early=[smalltalk.send(y, "__star", [(2)])];
}
})]);
}
$2=y;
}
} catch(e) {if(e===$early) return e[0]; throw e}}

(BTW, shouldn't whileTrue: return value (of the last body block evaulated or nil) or I am just making things up? In the present one, the while just quit without passing any value (that is, it returns undefined))

I don't know this alternative compiler will be of any value at all, not even if it would be faster (paradigmatic change, it can only be tested after finished), but I'm interested if someone would be willing to look over it and test it to find some bugs... I'll upload it to github once finished (it's just a matter of a little more work, from the principles point of view all is clear).

Herby

P.S.: I checked the above thing compiled with old and new and new is two times faster (Chrome18, Win7)... but it is probably not the representative pattern, if has lots of ifTrues and whileTrues and other inlineable code...

P.P.S.: Of course, old compiler after the fix of GH-178, otherwise it fails to compile.


Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

gokr
Great work again! So nice to see people getting into the deeper end of the pool, that is what is needed to gain evolution speed.

Also, Nicolas, perhaps we could open up to some more people with key access to your repo? Not sure, pullreqs are of course also simple.

regards, Göran

-- Sent from my Palm Pre 2, wohoo!


On Apr 28, 2012 15:41, Nicolas Petton <[hidden email]> wrote:

Hi!

That's indeed very interesting. It is true that early returns in Amber are expensive.  
I'll be happy to follow your progress here to see where your compiler is going :)

Nico

On Apr 28, 2012, at 2:24 PM, Herby Vojčík wrote:

I am playing with different Compiler... the present one is nicely functional, but it can't inline `true ifTrue: [^42]`, producing unnecessary try/catch.

Though ditching "functional" and bringing in "imperative" is probably not quite beuatiful, maybe it can produce more efficient code (trying to use if/then/else and assign to variable instead of IIFEs).

For the time being, it is able to compile

foo: x
|y|
y:=1.
^[y<10] whileTrue: [y:=(y<3) ifTrue: [y*3] ifFalse: [^y*2]]

to

function(x){var self=this;
var $3,$8;
var $early={}; try{
var y=nil;
y=(1);
for(;;){var $2=nil;
if(y.klass === smalltalk.Number) {$3=(y<(10));
} else {$3=smalltalk.send(y, "__lt", [(10)]);
}
if (!($3)) {return $2;
break}
if(y.klass === smalltalk.Number) {$8=(y<(3));
} else {$8=smalltalk.send(y, "__lt", [(3)]);
}
if($8.klass === smalltalk.Boolean) if($8) {if(y.klass === smalltalk.Number) {y=(y*(3));
} else {y=smalltalk.send(y, "__star", [(3)]);
}
} else {if(y.klass === smalltalk.Number) {return (y*(2));
} else {return smalltalk.send(y, "__star", [(2)]);
}
} else {y=smalltalk.send($8, "_ifTrue_ifFalse_", [(function(){if(y.klass === smalltalk.Number) {return (y*(3));
} else {return smalltalk.send(y, "__star", [(3)]);
}
}), (function(){if(y.klass === smalltalk.Number) {throw $early=[(y*(2))];
} else {throw $early=[smalltalk.send(y, "__star", [(2)])];
}
})]);
}
$2=y;
}
} catch(e) {if(e===$early) return e[0]; throw e}}

(BTW, shouldn't whileTrue: return value (of the last body block evaulated or nil) or I am just making things up? In the present one, the while just quit without passing any value (that is, it returns undefined))

I don't know this alternative compiler will be of any value at all, not even if it would be faster (paradigmatic change, it can only be tested after finished), but I'm interested if someone would be willing to look over it and test it to find some bugs... I'll upload it to github once finished (it's just a matter of a little more work, from the principles point of view all is clear).

Herby

P.S.: I checked the above thing compiled with old and new and new is two times faster (Chrome18, Win7)... but it is probably not the representative pattern, if has lots of ifTrues and whileTrues and other inlineable code...

P.P.S.: Of course, old compiler after the fix of GH-178, otherwise it fails to compile.


Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

bobcalco


2012/4/29 Göran Krampe <[hidden email]>
Great work again! So nice to see people getting into the deeper end of the pool, that is what is needed to gain evolution speed.

Also, Nicolas, perhaps we could open up to some more people with key access to your repo? Not sure, pullreqs are of course also simple.

I for one really want to understand how the compiler works, and would be interested in contributing over time to the project. I think Amber -- like CoffeeScript and Objective-J -- is a very forward-looking technology, treating JavaScript as a compiler target and making it possible to express higher-level semantics for web applications. 

I believe the further back one can look, the further ahead one can gaze, and so Amber's Smalltalk heritage suggests a very long view forward. But the reality of JavaScript and the browser as a target forces a lot of both interesting and frustrating constraints, I imagine, for a Smalltalker.

Could you write something, Nico, about how the compiler works, for someone wanting to tinker with it, and what its various runtime and compile-time and use-case assumptions are? Or point us to something you have already published that explains, as it were, the implementation decisions you made? I would find that very helpful in diving into the code.

- Bob 

regards, Göran

-- Sent from my Palm Pre 2, wohoo!


On Apr 28, 2012 15:41, Nicolas Petton <[hidden email]> wrote:

Hi!

That's indeed very interesting. It is true that early returns in Amber are expensive.  
I'll be happy to follow your progress here to see where your compiler is going :)

Nico

On Apr 28, 2012, at 2:24 PM, Herby Vojčík wrote:

I am playing with different Compiler... the present one is nicely functional, but it can't inline `true ifTrue: [^42]`, producing unnecessary try/catch.

Though ditching "functional" and bringing in "imperative" is probably not quite beuatiful, maybe it can produce more efficient code (trying to use if/then/else and assign to variable instead of IIFEs).

For the time being, it is able to compile

foo: x
|y|
y:=1.
^[y<10] whileTrue: [y:=(y<3) ifTrue: [y*3] ifFalse: [^y*2]]

to

function(x){var self=this;
var $3,$8;
var $early={}; try{
var y=nil;
y=(1);
for(;;){var $2=nil;
if(y.klass === smalltalk.Number) {$3=(y<(10));
} else {$3=smalltalk.send(y, "__lt", [(10)]);
}
if (!($3)) {return $2;
break}
if(y.klass === smalltalk.Number) {$8=(y<(3));
} else {$8=smalltalk.send(y, "__lt", [(3)]);
}
if($8.klass === smalltalk.Boolean) if($8) {if(y.klass === smalltalk.Number) {y=(y*(3));
} else {y=smalltalk.send(y, "__star", [(3)]);
}
} else {if(y.klass === smalltalk.Number) {return (y*(2));
} else {return smalltalk.send(y, "__star", [(2)]);
}
} else {y=smalltalk.send($8, "_ifTrue_ifFalse_", [(function(){if(y.klass === smalltalk.Number) {return (y*(3));
} else {return smalltalk.send(y, "__star", [(3)]);
}
}), (function(){if(y.klass === smalltalk.Number) {throw $early=[(y*(2))];
} else {throw $early=[smalltalk.send(y, "__star", [(2)])];
}
})]);
}
$2=y;
}
} catch(e) {if(e===$early) return e[0]; throw e}}

(BTW, shouldn't whileTrue: return value (of the last body block evaulated or nil) or I am just making things up? In the present one, the while just quit without passing any value (that is, it returns undefined))

I don't know this alternative compiler will be of any value at all, not even if it would be faster (paradigmatic change, it can only be tested after finished), but I'm interested if someone would be willing to look over it and test it to find some bugs... I'll upload it to github once finished (it's just a matter of a little more work, from the principles point of view all is clear).

Herby

P.S.: I checked the above thing compiled with old and new and new is two times faster (Chrome18, Win7)... but it is probably not the representative pattern, if has lots of ifTrues and whileTrues and other inlineable code...

P.P.S.: Of course, old compiler after the fix of GH-178, otherwise it fails to compile.



Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

Herby Vojčík


Robert Calco wrote:

> I for one really want to understand how the compiler works, and would be
> interested in contributing over time to the project. I think Amber --
> like CoffeeScript and Objective-J -- is a very forward-looking
> technology, treating JavaScript as a compiler target and making it
> possible to express higher-level semantics for web applications.
>
> I believe the further back one can look, the further ahead one can gaze,
> and so Amber's Smalltalk heritage suggests a very long view forward. But
> the reality of JavaScript and the browser as a target forces a lot of
> both interesting and frustrating constraints, I imagine, for a Smalltalker.
>
> Could you write something, Nico, about how the compiler works, for
> someone wanting to tinker with it, and what its various runtime and
> compile-time and use-case assumptions are? Or point us to something you
> have already published that explains, as it were, the implementation
> decisions you made? I would find that very helpful in diving into the code.

For me, I ignored the parsing completely (I treat it as "batteries
included") and then look at the functions it produces and look over
methods visitXxxNode: of Compiler, where basically everything is done
and I think the code is readable.

In Workspace, I just print-it

Compiler new compile: '

...
source of the sample method
...

' forClass: 'String'

(I use NewCompiler in the snipper above to test my own class which I
simply copied from Compiler; it is advised you also create a copy and
play with it because if you break Compiler, you have broken a tool with
which you could fix it; and of course you can use any other class than
String in producing the output; and last but not least, the snippet
above just produces the output, does not install the method, so you are
safe to play with it)

Herby

P.S.: Of course, it precludes that you are familiar with Visitor
pattern, if not, just google "Visitor pattern" or visit
http://www.c2.com/ and find it there, c2 is valuable site for anything
involving high quality programming :-)
Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

Herby Vojčík
Herby Vojčík wrote:
> In Workspace, I just print-it
>
> Compiler new compile: '
>
> ...
> source of the sample method
> ...
>
> ' forClass: 'String'

fix:
' forClass: String
(the class should be there as object, not as string)
Reply | Threaded
Open this post in threaded view
|

Re: Imperative compiler

Herby Vojčík
In reply to this post by Nicolas Petton


Nicolas Petton wrote:
> Hi!
>
> That's indeed very interesting. It is true that early returns in Amber
> are expensive.
> I'll be happy to follow your progress here to see where your compiler is
> going :)

The crude version of the compiler is done!
(https://github.com/herby/amber/tree/newcompiler)

Known bugs:
- when inlining, temps from block collide with temps from method / upper
blocks
- some objects (mainly function () {...} for blocks but also some
inlined Number operators) are evaluated more times, not saved into a
variable.
- super is not treated right when inlining (also error of existing
compiler).


I'd like anyone who is willing to look either at the code or trying to
use the new compiler at the various wild sample codes to tell me if you
find any other bugs / imporovements, God forbid even crashes ;-) !

Herby

P.S.: It is not integrated into the system, it is external for the
moment, but you can use it manually from workspace or in any other way
programmatically - an example of workspace use was shown in this thread
a few posts back.