Browse Source

IRNonLocalReturnInliner. Fix #1244.

Herby Vojčík 4 years ago
parent
commit
ef1f6f1881
4 changed files with 184 additions and 5 deletions
  1. 117 4
      lang/src/Compiler-Inlining.js
  2. 39 1
      lang/src/Compiler-Inlining.st
  3. 24 0
      lang/src/Compiler-Tests.js
  4. 4 0
      lang/src/Compiler-Tests.st

+ 117 - 4
lang/src/Compiler-Inlining.js

@@ -436,6 +436,32 @@ return $recv($1)._yourself();
 }; }),
 $globals.IRInliner);
 
+$core.addMethod(
+$core.method({
+selector: "nonLocalReturnInliner",
+protocol: "factory",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "nonLocalReturnInliner\x0a\x09^ IRNonLocalReturnInliner new\x0a\x09\x09translator: self;\x0a\x09\x09yourself",
+referencedClasses: ["IRNonLocalReturnInliner"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["translator:", "new", "yourself"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=$recv($globals.IRNonLocalReturnInliner)._new();
+$recv($1)._translator_(self);
+return $recv($1)._yourself();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"nonLocalReturnInliner",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.IRInliner);
+
 $core.addMethod(
 $core.method({
 selector: "returnInliner",
@@ -659,11 +685,11 @@ selector: "visitIRNonLocalReturn:",
 protocol: "visiting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anIRNonLocalReturn"],
-source: "visitIRNonLocalReturn: anIRNonLocalReturn\x0a\x09| localReturn |\x0a\x09anIRNonLocalReturn scope canInlineNonLocalReturns ifTrue: [\x0a\x09\x09anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.\x0a\x09\x09localReturn := IRReturn new\x0a\x09\x09\x09scope: anIRNonLocalReturn scope;\x0a\x09\x09\x09yourself.\x0a\x09\x09anIRNonLocalReturn dagChildren do: [ :each |\x0a\x09\x09\x09localReturn add: each ].\x0a\x09\x09anIRNonLocalReturn replaceWith: localReturn.\x0a\x09\x09^ self visitIRReturn: localReturn ].\x0a\x09^ super visitIRNonLocalReturn: anIRNonLocalReturn",
+source: "visitIRNonLocalReturn: anIRNonLocalReturn\x0a\x09| localReturn |\x0a\x09anIRNonLocalReturn scope canInlineNonLocalReturns ifTrue: [\x0a\x09\x09anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.\x0a\x09\x09localReturn := IRReturn new\x0a\x09\x09\x09scope: anIRNonLocalReturn scope;\x0a\x09\x09\x09yourself.\x0a\x09\x09anIRNonLocalReturn dagChildren do: [ :each |\x0a\x09\x09\x09localReturn add: each ].\x0a\x09\x09anIRNonLocalReturn replaceWith: localReturn.\x0a\x09\x09^ self visitIRReturn: localReturn ].\x0a\x09^ (self shouldInlineReturn: anIRNonLocalReturn)\x0a\x09\x09ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]\x0a\x09\x09ifFalse: [ super visitIRNonLocalReturn: anIRNonLocalReturn ]",
 referencedClasses: ["IRReturn"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["ifTrue:", "canInlineNonLocalReturns", "scope", "removeNonLocalReturn:", "methodScope", "scope:", "new", "yourself", "do:", "dagChildren", "add:", "replaceWith:", "visitIRReturn:", "visitIRNonLocalReturn:"]
+messageSends: ["ifTrue:", "canInlineNonLocalReturns", "scope", "removeNonLocalReturn:", "methodScope", "scope:", "new", "yourself", "do:", "dagChildren", "add:", "replaceWith:", "visitIRReturn:", "ifTrue:ifFalse:", "shouldInlineReturn:", "inlineReturn:", "nonLocalReturnInliner", "visitIRNonLocalReturn:"]
 }, function ($methodClass){ return function (anIRNonLocalReturn){
 var self=this,$self=this;
 var localReturn;
@@ -702,7 +728,11 @@ return $recv(localReturn)._add_(each);
 $recv(anIRNonLocalReturn)._replaceWith_(localReturn);
 return $self._visitIRReturn_(localReturn);
 }
-$7=(
+$7=$self._shouldInlineReturn_(anIRNonLocalReturn);
+if($core.assert($7)){
+return $recv($self._nonLocalReturnInliner())._inlineReturn_(anIRNonLocalReturn);
+} else {
+return (
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.supercall = true,
 //>>excludeEnd("ctx");
@@ -710,7 +740,7 @@ $ctx1.supercall = true,
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.supercall = false;
 //>>excludeEnd("ctx");;
-return $7;
+}
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"visitIRNonLocalReturn:",{anIRNonLocalReturn:anIRNonLocalReturn,localReturn:localReturn})});
 //>>excludeEnd("ctx");
@@ -2028,6 +2058,89 @@ $globals.IRAssignmentInliner);
 
 
 
+$core.addClass("IRNonLocalReturnInliner", $globals.IRSendInliner, [], "Compiler-Inlining");
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.IRNonLocalReturnInliner.comment="I inline message sends with inlined closure together with a return instruction.";
+//>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "inlineClosure:",
+protocol: "inlining",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anIRClosure"],
+source: "inlineClosure: anIRClosure\x0a\x09| closure sequence statements |\x0a\x0a\x09closure := super inlineClosure: anIRClosure.\x0a\x09sequence := closure sequence.\x0a\x09statements := sequence dagChildren.\x0a\x09\x0a\x09statements ifNotEmpty: [\x0a\x09\x09| final |\x0a\x09\x09final := statements last.\x0a\x09\x09final yieldsValue ifTrue: [\x0a\x09\x09\x09sequence replace: final with: (IRNonLocalReturn new\x0a\x09\x09\x09\x09add: final copy;\x0a\x09\x09\x09\x09yourself) ] ].\x0a\x0a\x09^ closure",
+referencedClasses: ["IRNonLocalReturn"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["inlineClosure:", "sequence", "dagChildren", "ifNotEmpty:", "last", "ifTrue:", "yieldsValue", "replace:with:", "add:", "new", "copy", "yourself"]
+}, function ($methodClass){ return function (anIRClosure){
+var self=this,$self=this;
+var closure,sequence,statements;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2,$3,$5,$4;
+closure=(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($methodClass.superclass||$boot.nilAsClass).fn.prototype._inlineClosure_.call($self,anIRClosure));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+sequence=$recv(closure)._sequence();
+statements=$recv(sequence)._dagChildren();
+$recv(statements)._ifNotEmpty_((function(){
+var final;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+final=$recv(statements)._last();
+$1=$recv(final)._yieldsValue();
+if($core.assert($1)){
+$2=sequence;
+$3=final;
+$5=$recv($globals.IRNonLocalReturn)._new();
+$recv($5)._add_($recv(final)._copy());
+$4=$recv($5)._yourself();
+return $recv($2)._replace_with_($3,$4);
+}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({final:final},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return closure;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,closure:closure,sequence:sequence,statements:statements})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.IRNonLocalReturnInliner);
+
+$core.addMethod(
+$core.method({
+selector: "inlineReturn:",
+protocol: "inlining",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anIRReturn"],
+source: "inlineReturn: anIRReturn\x0a\x09^ self inlineSend: anIRReturn expression andReplace: anIRReturn",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["inlineSend:andReplace:", "expression"]
+}, function ($methodClass){ return function (anIRReturn){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $self._inlineSend_andReplace_($recv(anIRReturn)._expression(),anIRReturn);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"inlineReturn:",{anIRReturn:anIRReturn})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.IRNonLocalReturnInliner);
+
+
+
 $core.addClass("IRReturnInliner", $globals.IRSendInliner, [], "Compiler-Inlining");
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.IRReturnInliner.comment="I inline message sends with inlined closure together with a return instruction.";

+ 39 - 1
lang/src/Compiler-Inlining.st

@@ -159,6 +159,12 @@ assignmentInliner
 		yourself
 !
 
+nonLocalReturnInliner
+	^ IRNonLocalReturnInliner new
+		translator: self;
+		yourself
+!
+
 returnInliner
 	^ IRReturnInliner new
 		translator: self;
@@ -209,7 +215,9 @@ visitIRNonLocalReturn: anIRNonLocalReturn
 			localReturn add: each ].
 		anIRNonLocalReturn replaceWith: localReturn.
 		^ self visitIRReturn: localReturn ].
-	^ super visitIRNonLocalReturn: anIRNonLocalReturn
+	^ (self shouldInlineReturn: anIRNonLocalReturn)
+		ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]
+		ifFalse: [ super visitIRNonLocalReturn: anIRNonLocalReturn ]
 !
 
 visitIRReturn: anIRReturn
@@ -523,6 +531,36 @@ inlineClosure: anIRClosure
 	^ closure
 ! !
 
+IRSendInliner subclass: #IRNonLocalReturnInliner
+	slots: {}
+	package: 'Compiler-Inlining'!
+!IRNonLocalReturnInliner commentStamp!
+I inline message sends with inlined closure together with a return instruction.!
+
+!IRNonLocalReturnInliner methodsFor: 'inlining'!
+
+inlineClosure: anIRClosure
+	| closure sequence statements |
+
+	closure := super inlineClosure: anIRClosure.
+	sequence := closure sequence.
+	statements := sequence dagChildren.
+	
+	statements ifNotEmpty: [
+		| final |
+		final := statements last.
+		final yieldsValue ifTrue: [
+			sequence replace: final with: (IRNonLocalReturn new
+				add: final copy;
+				yourself) ] ].
+
+	^ closure
+!
+
+inlineReturn: anIRReturn
+	^ self inlineSend: anIRReturn expression andReplace: anIRReturn
+! !
+
 IRSendInliner subclass: #IRReturnInliner
 	slots: {}
 	package: 'Compiler-Inlining'!

+ 24 - 0
lang/src/Compiler-Tests.js

@@ -964,6 +964,30 @@ return self;
 }; }),
 $globals.AbstractCompilerTest);
 
+$core.addMethod(
+$core.method({
+selector: "testRegression1244",
+protocol: "tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRegression1244\x0a\x09self should: 'foo [ ^ true ifTrue: [1] ifFalse: [2] ] value' return: 1",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:return:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_return_("foo [ ^ true ifTrue: [1] ifFalse: [2] ] value",(1));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRegression1244",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.AbstractCompilerTest);
+
 $core.addMethod(
 $core.method({
 selector: "testRootSuperSend",

+ 4 - 0
lang/src/Compiler-Tests.st

@@ -232,6 +232,10 @@ testPragmaJSStatement
 	self should: 'foo < inlineJS: ''return 2+3'' >' return: 5
 !
 
+testRegression1244
+	self should: 'foo [ ^ true ifTrue: [1] ifFalse: [2] ] value' return: 1
+!
+
 testRootSuperSend
 	self 
 		should: 'foo ^ super class'