Browse Source

Compiler: push things down.

Herbert Vojčík 7 years ago
parent
commit
372dfba14c
6 changed files with 412 additions and 381 deletions
  1. 32 324
      src/Compiler-AST.js
  2. 10 57
      src/Compiler-AST.st
  3. 263 0
      src/Compiler-IR.js
  4. 53 0
      src/Compiler-IR.st
  5. 46 0
      src/Compiler-Interpreter.js
  6. 8 0
      src/Compiler-Interpreter.st

+ 32 - 324
src/Compiler-AST.js

@@ -6,7 +6,7 @@ $core.addPackage('Compiler-AST');
 $core.packages["Compiler-AST"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Compiler-AST"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
-$core.addClass('Node', $globals.Object, ['parent', 'position', 'source', 'nodes', 'shouldBeInlined', 'shouldBeAliased'], 'Compiler-AST');
+$core.addClass('Node', $globals.Object, ['parent', 'position', 'source', 'nodes', 'shouldBeAliased'], 'Compiler-AST');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.Node.comment="I am the abstract root class of the abstract syntax tree.\x0a\x0aConcrete classes should implement `#accept:` to allow visiting.\x0a\x0a`position` holds a point containing line and column number of the symbol location in the original source file.";
 //>>excludeEnd("ide");
@@ -236,29 +236,6 @@ messageSends: []
 }),
 $globals.Node);
 
-$core.addMethod(
-$core.method({
-selector: "isLastChild",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-return $recv($recv($recv(self._parent())._nodes())._last()).__eq(self);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"isLastChild",{},$globals.Node)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "isLastChild\x0a\x09^ self parent nodes last = self",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["=", "last", "nodes", "parent"]
-}),
-$globals.Node);
-
 $core.addMethod(
 $core.method({
 selector: "isNavigationNode",
@@ -295,44 +272,6 @@ messageSends: []
 }),
 $globals.Node);
 
-$core.addMethod(
-$core.method({
-selector: "isReferenced",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $3,$2,$1;
-$3=self._parent();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["parent"]=1;
-//>>excludeEnd("ctx");
-$2=$recv($3)._isSequenceNode();
-$1=$recv($2)._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(self._parent())._isAssignmentNode();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-return $recv($1)._not();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"isReferenced",{},$globals.Node)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "isReferenced\x0a\x09\x22Answer true if the receiver is referenced by other nodes.\x0a\x09Do not take sequences or assignments into account\x22\x0a\x09\x0a\x09^ (self parent isSequenceNode or: [\x0a\x09\x09self parent isAssignmentNode ]) not",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["not", "or:", "isSequenceNode", "parent", "isAssignmentNode"]
-}),
-$globals.Node);
-
 $core.addMethod(
 $core.method({
 selector: "isReturnNode",
@@ -916,54 +855,6 @@ messageSends: []
 }),
 $globals.Node);
 
-$core.addMethod(
-$core.method({
-selector: "shouldBeInlined",
-protocol: 'accessing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1,$receiver;
-$1=self["@shouldBeInlined"];
-if(($receiver = $1) == null || $receiver.isNil){
-return false;
-} else {
-return $1;
-};
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"shouldBeInlined",{},$globals.Node)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "shouldBeInlined\x0a\x09^ shouldBeInlined ifNil: [ false ]",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["ifNil:"]
-}),
-$globals.Node);
-
-$core.addMethod(
-$core.method({
-selector: "shouldBeInlined:",
-protocol: 'accessing',
-fn: function (aBoolean){
-var self=this;
-self["@shouldBeInlined"]=aBoolean;
-return self;
-
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["aBoolean"],
-source: "shouldBeInlined: aBoolean\x0a\x09shouldBeInlined := aBoolean",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: []
-}),
-$globals.Node);
-
 $core.addMethod(
 $core.method({
 selector: "size",
@@ -1035,58 +926,6 @@ messageSends: []
 }),
 $globals.Node);
 
-$core.addMethod(
-$core.method({
-selector: "subtreeNeedsAliasing",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1;
-$1=$recv($recv(self._shouldBeAliased())._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return self._shouldBeInlined();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-})))._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(self._nodes())._anySatisfy_((function(each){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx3) {
-//>>excludeEnd("ctx");
-return $recv(each)._subtreeNeedsAliasing();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx2,3)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["or:"]=1;
-//>>excludeEnd("ctx");
-return $1;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.Node)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "subtreeNeedsAliasing\x0a\x09^ (self shouldBeAliased or: [ self shouldBeInlined ]) or: [\x0a\x09\x09self nodes anySatisfy: [ :each | each subtreeNeedsAliasing ] ]",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["or:", "shouldBeAliased", "shouldBeInlined", "anySatisfy:", "nodes", "subtreeNeedsAliasing"]
-}),
-$globals.Node);
-
 
 
 $core.addClass('AssignmentNode', $globals.Node, ['left', 'right'], 'Compiler-AST');
@@ -1243,46 +1082,6 @@ messageSends: ["parent:"]
 }),
 $globals.AssignmentNode);
 
-$core.addMethod(
-$core.method({
-selector: "shouldBeAliased",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1;
-$1=(
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.supercall = true,
-//>>excludeEnd("ctx");
-($globals.AssignmentNode.superclass||$boot.nilAsClass).fn.prototype._shouldBeAliased.apply($recv(self), []));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.supercall = false;
-//>>excludeEnd("ctx");;
-return $recv($1)._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return self._isReferenced();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"shouldBeAliased",{},$globals.AssignmentNode)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "shouldBeAliased\x0a\x09^ super shouldBeAliased or: [ self isReferenced ]",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["or:", "shouldBeAliased", "isReferenced"]
-}),
-$globals.AssignmentNode);
-
 
 
 $core.addClass('BlockNode', $globals.Node, ['parameters', 'scope'], 'Compiler-AST');
@@ -1416,37 +1215,6 @@ messageSends: []
 }),
 $globals.BlockNode);
 
-$core.addMethod(
-$core.method({
-selector: "subtreeNeedsAliasing",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-return $recv(self._shouldBeAliased())._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return self._shouldBeInlined();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.BlockNode)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "subtreeNeedsAliasing\x0a\x09^ self shouldBeAliased or: [ self shouldBeInlined ]",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["or:", "shouldBeAliased", "shouldBeInlined"]
-}),
-$globals.BlockNode);
-
 
 
 $core.addClass('CascadeNode', $globals.Node, ['receiver'], 'Compiler-AST');
@@ -1531,29 +1299,6 @@ messageSends: []
 }),
 $globals.CascadeNode);
 
-$core.addMethod(
-$core.method({
-selector: "subtreeNeedsAliasing",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-return $recv($recv(self._parent())._isSequenceNode())._not();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.CascadeNode)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "subtreeNeedsAliasing\x0a\x09^ self parent isSequenceNode not",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["not", "isSequenceNode", "parent"]
-}),
-$globals.CascadeNode);
-
 
 
 $core.addClass('DynamicArrayNode', $globals.Node, [], 'Compiler-AST');
@@ -2129,7 +1874,7 @@ $globals.ReturnNode);
 
 
 
-$core.addClass('SendNode', $globals.Node, ['selector', 'arguments', 'receiver', 'index'], 'Compiler-AST');
+$core.addClass('SendNode', $globals.Node, ['selector', 'arguments', 'receiver', 'index', 'shouldBeInlined'], 'Compiler-AST');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.SendNode.comment="I represent an message send node.";
 //>>excludeEnd("ide");
@@ -2295,29 +2040,6 @@ messageSends: []
 }),
 $globals.SendNode);
 
-$core.addMethod(
-$core.method({
-selector: "isCascadeSendNode",
-protocol: 'testing',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-return $recv(self._parent())._isCascadeNode();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"isCascadeSendNode",{},$globals.SendNode)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "isCascadeSendNode\x0a\x09^ self parent isCascadeNode",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["isCascadeNode", "parent"]
-}),
-$globals.SendNode);
-
 $core.addMethod(
 $core.method({
 selector: "isNavigationNode",
@@ -2520,63 +2242,49 @@ $globals.SendNode);
 
 $core.addMethod(
 $core.method({
-selector: "shouldBeAliased",
-protocol: 'testing',
+selector: "shouldBeInlined",
+protocol: 'accessing',
 fn: function (){
 var self=this;
-var sends;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $2,$1;
-sends=$recv($recv($recv(self._method())._sendIndexes())._at_(self._selector()))._size();
-$2=(
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.supercall = true,
-//>>excludeEnd("ctx");
-($globals.SendNode.superclass||$boot.nilAsClass).fn.prototype._shouldBeAliased.apply($recv(self), []));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.supercall = false;
-//>>excludeEnd("ctx");;
-$1=$recv($2)._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(self._isReferenced())._and_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx3) {
-//>>excludeEnd("ctx");
-return $recv($recv(self._index()).__lt(sends))._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx4) {
-//>>excludeEnd("ctx");
-return self._superSend();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["or:"]=1;
-//>>excludeEnd("ctx");
+var $1,$receiver;
+$1=self["@shouldBeInlined"];
+if(($receiver = $1) == null || $receiver.isNil){
+return false;
+} else {
 return $1;
+};
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"shouldBeAliased",{sends:sends},$globals.SendNode)});
+}, function($ctx1) {$ctx1.fill(self,"shouldBeInlined",{},$globals.SendNode)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "shouldBeAliased\x0a\x09\x22Because we keep track of send indexes, some send nodes need additional care for aliasing. \x0a\x09See IRJSVisitor >> visitIRSend:\x22\x0a\x09\x0a\x09| sends |\x0a\x09\x0a\x09sends := (self method sendIndexes at: self selector) size.\x0a\x09\x0a\x09^ (super shouldBeAliased or: [\x0a\x09\x09self isReferenced and: [\x0a\x09\x09\x09self index < sends or: [\x0a\x09\x09\x09\x09self superSend ] ] ])",
+source: "shouldBeInlined\x0a\x09^ shouldBeInlined ifNil: [ false ]",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["size", "at:", "sendIndexes", "method", "selector", "or:", "shouldBeAliased", "and:", "isReferenced", "<", "index", "superSend"]
+messageSends: ["ifNil:"]
+}),
+$globals.SendNode);
+
+$core.addMethod(
+$core.method({
+selector: "shouldBeInlined:",
+protocol: 'accessing',
+fn: function (aBoolean){
+var self=this;
+self["@shouldBeInlined"]=aBoolean;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBoolean"],
+source: "shouldBeInlined: aBoolean\x0a\x09shouldBeInlined := aBoolean",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
 }),
 $globals.SendNode);
 

+ 10 - 57
src/Compiler-AST.st

@@ -1,6 +1,6 @@
 Smalltalk createPackage: 'Compiler-AST'!
 Object subclass: #Node
-	instanceVariableNames: 'parent position source nodes shouldBeInlined shouldBeAliased'
+	instanceVariableNames: 'parent position source nodes shouldBeAliased'
 	package: 'Compiler-AST'!
 !Node commentStamp!
 I am the abstract root class of the abstract syntax tree.
@@ -91,14 +91,6 @@ shouldBeAliased: aBoolean
 	shouldBeAliased := aBoolean
 !
 
-shouldBeInlined
-	^ shouldBeInlined ifNil: [ false ]
-!
-
-shouldBeInlined: aBoolean
-	shouldBeInlined := aBoolean
-!
-
 size
 	^ self source size
 !
@@ -156,10 +148,6 @@ isJSStatementNode
 	^ false
 !
 
-isLastChild
-	^ self parent nodes last = self
-!
-
 isNavigationNode
 	"Answer true if the node can be navigated to"
 	
@@ -170,14 +158,6 @@ isNode
 	^ true
 !
 
-isReferenced
-	"Answer true if the receiver is referenced by other nodes.
-	Do not take sequences or assignments into account"
-	
-	^ (self parent isSequenceNode or: [
-		self parent isAssignmentNode ]) not
-!
-
 isReturnNode
 	^ false
 !
@@ -212,11 +192,6 @@ requiresSmalltalkContext
 	^ (self nodes 
 		detect: [ :each | each requiresSmalltalkContext ]
 		ifNone: [ nil ]) notNil
-!
-
-subtreeNeedsAliasing
-	^ (self shouldBeAliased or: [ self shouldBeInlined ]) or: [
-		self nodes anySatisfy: [ :each | each subtreeNeedsAliasing ] ]
 ! !
 
 !Node methodsFor: 'visiting'!
@@ -259,10 +234,6 @@ right: aNode
 
 isAssignmentNode
 	^ true
-!
-
-shouldBeAliased
-	^ super shouldBeAliased or: [ self isReferenced ]
 ! !
 
 !AssignmentNode methodsFor: 'visiting'!
@@ -299,10 +270,6 @@ scope: aLexicalScope
 
 isBlockNode
 	^ true
-!
-
-subtreeNeedsAliasing
-	^ self shouldBeAliased or: [ self shouldBeInlined ]
 ! !
 
 !BlockNode methodsFor: 'visiting'!
@@ -331,10 +298,6 @@ receiver: aNode
 
 isCascadeNode
 	^ true
-!
-
-subtreeNeedsAliasing
-	^ self parent isSequenceNode not
 ! !
 
 !CascadeNode methodsFor: 'visiting'!
@@ -501,7 +464,7 @@ accept: aVisitor
 ! !
 
 Node subclass: #SendNode
-	instanceVariableNames: 'selector arguments receiver index'
+	instanceVariableNames: 'selector arguments receiver index shouldBeInlined'
 	package: 'Compiler-AST'!
 !SendNode commentStamp!
 I represent an message send node.!
@@ -567,6 +530,14 @@ selector: aString
 	selector := aString
 !
 
+shouldBeInlined
+	^ shouldBeInlined ifNil: [ false ]
+!
+
+shouldBeInlined: aBoolean
+	shouldBeInlined := aBoolean
+!
+
 superSend
 	^ self receiver notNil and: [ self receiver isSuperKeyword ]
 ! !
@@ -587,10 +558,6 @@ valueForReceiver: anObject
 
 !SendNode methodsFor: 'testing'!
 
-isCascadeSendNode
-	^ self parent isCascadeNode
-!
-
 isNavigationNode
 	^ true
 !
@@ -601,20 +568,6 @@ isSendNode
 
 requiresSmalltalkContext
 	^ true
-!
-
-shouldBeAliased
-	"Because we keep track of send indexes, some send nodes need additional care for aliasing. 
-	See IRJSVisitor >> visitIRSend:"
-	
-	| sends |
-	
-	sends := (self method sendIndexes at: self selector) size.
-	
-	^ (super shouldBeAliased or: [
-		self isReferenced and: [
-			self index < sends or: [
-				self superSend ] ] ])
 ! !
 
 !SendNode methodsFor: 'visiting'!

+ 263 - 0
src/Compiler-IR.js

@@ -5621,6 +5621,46 @@ messageSends: ["ifNotEmpty:", "nextPutAll:", "do:separatedBy:", "lf"]
 $globals.JSStream);
 
 
+$core.addMethod(
+$core.method({
+selector: "shouldBeAliased",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($globals.AssignmentNode.superclass||$boot.nilAsClass).fn.prototype._shouldBeAliased.apply($recv(self), []));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+return $recv($1)._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._isReferenced();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"shouldBeAliased",{},$globals.AssignmentNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "shouldBeAliased\x0a\x09^ super shouldBeAliased or: [ self isReferenced ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["or:", "shouldBeAliased", "isReferenced"]
+}),
+$globals.AssignmentNode);
+
 $core.addMethod(
 $core.method({
 selector: "appendToInstruction:",
@@ -5645,4 +5685,227 @@ messageSends: ["appendBlock:"]
 }),
 $globals.BlockClosure);
 
+$core.addMethod(
+$core.method({
+selector: "subtreeNeedsAliasing",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return self._shouldBeAliased();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.BlockNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "subtreeNeedsAliasing\x0a\x09^ self shouldBeAliased",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["shouldBeAliased"]
+}),
+$globals.BlockNode);
+
+$core.addMethod(
+$core.method({
+selector: "subtreeNeedsAliasing",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv($recv(self._parent())._isSequenceNode())._not();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.CascadeNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "subtreeNeedsAliasing\x0a\x09^ self parent isSequenceNode not",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["not", "isSequenceNode", "parent"]
+}),
+$globals.CascadeNode);
+
+$core.addMethod(
+$core.method({
+selector: "isReferenced",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $3,$2,$1;
+$3=self._parent();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["parent"]=1;
+//>>excludeEnd("ctx");
+$2=$recv($3)._isSequenceNode();
+$1=$recv($2)._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(self._parent())._isAssignmentNode();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return $recv($1)._not();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"isReferenced",{},$globals.Node)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "isReferenced\x0a\x09\x22Answer true if the receiver is referenced by other nodes.\x0a\x09Do not take sequences or assignments into account\x22\x0a\x09\x0a\x09^ (self parent isSequenceNode or: [\x0a\x09\x09self parent isAssignmentNode ]) not",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["not", "or:", "isSequenceNode", "parent", "isAssignmentNode"]
+}),
+$globals.Node);
+
+$core.addMethod(
+$core.method({
+selector: "subtreeNeedsAliasing",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(self._shouldBeAliased())._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(self._nodes())._anySatisfy_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(each)._subtreeNeedsAliasing();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx2,2)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.Node)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "subtreeNeedsAliasing\x0a\x09^ self shouldBeAliased or: [\x0a\x09\x09self nodes anySatisfy: [ :each | each subtreeNeedsAliasing ] ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["or:", "shouldBeAliased", "anySatisfy:", "nodes", "subtreeNeedsAliasing"]
+}),
+$globals.Node);
+
+$core.addMethod(
+$core.method({
+selector: "shouldBeAliased",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+var sends;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $2,$1;
+sends=$recv($recv($recv(self._method())._sendIndexes())._at_(self._selector()))._size();
+$2=(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($globals.SendNode.superclass||$boot.nilAsClass).fn.prototype._shouldBeAliased.apply($recv(self), []));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+$1=$recv($2)._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(self._isReferenced())._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(self._index()).__lt(sends))._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+return self._superSend();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["or:"]=1;
+//>>excludeEnd("ctx");
+return $1;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"shouldBeAliased",{sends:sends},$globals.SendNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "shouldBeAliased\x0a\x09\x22Because we keep track of send indexes, some send nodes need additional care for aliasing. \x0a\x09See IRJSVisitor >> visitIRSend:\x22\x0a\x09\x0a\x09| sends |\x0a\x09\x0a\x09sends := (self method sendIndexes at: self selector) size.\x0a\x09\x0a\x09^ (super shouldBeAliased or: [\x0a\x09\x09self isReferenced and: [\x0a\x09\x09\x09self index < sends or: [\x0a\x09\x09\x09\x09self superSend ] ] ])",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["size", "at:", "sendIndexes", "method", "selector", "or:", "shouldBeAliased", "and:", "isReferenced", "<", "index", "superSend"]
+}),
+$globals.SendNode);
+
+$core.addMethod(
+$core.method({
+selector: "subtreeNeedsAliasing",
+protocol: '*Compiler-IR',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(self._shouldBeInlined())._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return (
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.supercall = true,
+//>>excludeEnd("ctx");
+($globals.SendNode.superclass||$boot.nilAsClass).fn.prototype._subtreeNeedsAliasing.apply($recv(self), []));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.supercall = false;
+//>>excludeEnd("ctx");;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"subtreeNeedsAliasing",{},$globals.SendNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "subtreeNeedsAliasing\x0a\x09^ self shouldBeInlined or: [ super subtreeNeedsAliasing ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["or:", "shouldBeInlined", "subtreeNeedsAliasing"]
+}),
+$globals.SendNode);
+
 });

+ 53 - 0
src/Compiler-IR.st

@@ -1265,9 +1265,62 @@ nextPutVars: aCollection
 		stream nextPutAll: ';'; lf ]
 ! !
 
+!AssignmentNode methodsFor: '*Compiler-IR'!
+
+shouldBeAliased
+	^ super shouldBeAliased or: [ self isReferenced ]
+! !
+
 !BlockClosure methodsFor: '*Compiler-IR'!
 
 appendToInstruction: anIRInstruction
 	anIRInstruction appendBlock: self
 ! !
 
+!BlockNode methodsFor: '*Compiler-IR'!
+
+subtreeNeedsAliasing
+	^ self shouldBeAliased
+! !
+
+!CascadeNode methodsFor: '*Compiler-IR'!
+
+subtreeNeedsAliasing
+	^ self parent isSequenceNode not
+! !
+
+!Node methodsFor: '*Compiler-IR'!
+
+isReferenced
+	"Answer true if the receiver is referenced by other nodes.
+	Do not take sequences or assignments into account"
+	
+	^ (self parent isSequenceNode or: [
+		self parent isAssignmentNode ]) not
+!
+
+subtreeNeedsAliasing
+	^ self shouldBeAliased or: [
+		self nodes anySatisfy: [ :each | each subtreeNeedsAliasing ] ]
+! !
+
+!SendNode methodsFor: '*Compiler-IR'!
+
+shouldBeAliased
+	"Because we keep track of send indexes, some send nodes need additional care for aliasing. 
+	See IRJSVisitor >> visitIRSend:"
+	
+	| sends |
+	
+	sends := (self method sendIndexes at: self selector) size.
+	
+	^ (super shouldBeAliased or: [
+		self isReferenced and: [
+			self index < sends or: [
+				self superSend ] ] ])
+!
+
+subtreeNeedsAliasing
+	^ self shouldBeInlined or: [ super subtreeNeedsAliasing ]
+! !
+

+ 46 - 0
src/Compiler-Interpreter.js

@@ -3868,6 +3868,29 @@ messageSends: []
 }),
 $globals.JSStatementNode);
 
+$core.addMethod(
+$core.method({
+selector: "isLastChild",
+protocol: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv($recv($recv(self._parent())._nodes())._last()).__eq(self);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"isLastChild",{},$globals.Node)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "isLastChild\x0a\x09^ self parent nodes last = self",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["=", "last", "nodes", "parent"]
+}),
+$globals.Node);
+
 $core.addMethod(
 $core.method({
 selector: "isSteppingNode",
@@ -3921,6 +3944,29 @@ messageSends: ["at:ifAbsent:", "nodes", "+", "indexOf:"]
 }),
 $globals.Node);
 
+$core.addMethod(
+$core.method({
+selector: "isCascadeSendNode",
+protocol: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(self._parent())._isCascadeNode();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"isCascadeSendNode",{},$globals.SendNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "isCascadeSendNode\x0a\x09^ self parent isCascadeNode",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["isCascadeNode", "parent"]
+}),
+$globals.SendNode);
+
 $core.addMethod(
 $core.method({
 selector: "isSteppingNode",

+ 8 - 0
src/Compiler-Interpreter.st

@@ -1011,6 +1011,10 @@ isSteppingNode
 
 !Node methodsFor: '*Compiler-Interpreter'!
 
+isLastChild
+	^ self parent nodes last = self
+!
+
 isSteppingNode
 	^ false
 !
@@ -1025,6 +1029,10 @@ nextSiblingNode: aNode
 
 !SendNode methodsFor: '*Compiler-Interpreter'!
 
+isCascadeSendNode
+	^ self parent isCascadeNode
+!
+
 isSteppingNode
 	^ true
 ! !