From 581da2cf6a2ea27e9537aa6589ec01d6829e2d01 Mon Sep 17 00:00:00 2001 From: Renaud Date: Wed, 18 Jun 2025 10:32:43 +0200 Subject: [PATCH 01/86] add tests for bugs --- .../SLMockInliningTestClass.class.st | 98 +++++- .../Slang-Tests/SlangInliningTest.class.st | 317 +++++++++++++++++- .../VMMaker/SlangInliningTest.extension.st | 2 + 3 files changed, 409 insertions(+), 8 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index d0612c0ab80..b18ab910e98 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -37,14 +37,32 @@ SLMockInliningTestClass >> methodAAssignOnReturn [ ^ self methodBAssignOnReturn ] +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodAInlineIfFalseReturningIfTrueInAssignement [ + + | a | + a := self methodBIfFalseReturningIfTrue +] + { #category : 'inlining-returning-conditional' } -SLMockInliningTestClass >> methodAInlineIfFalseReturningIfTrue [ +SLMockInliningTestClass >> methodAInlineIfFalseReturningIfTrueInReturn [ ^ self methodBIfFalseReturningIfTrue ] +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodAInlineMultipleIfFalseReturningIfTrueInAssignment [ + + | a | + a := self methodB + ifFalse: [ self methodBIfFalseReturningIfTrue ] + ifTrue: [ + 5. + self methodBIfFalseReturningIfTrue ] +] + { #category : 'inlining-returning-conditional' } -SLMockInliningTestClass >> methodAInlineMultipleIfFalseReturningIfTrue [ +SLMockInliningTestClass >> methodAInlineMultipleIfFalseReturningIfTrueInReturn [ ^ self methodB ifFalse: [ self methodBIfFalseReturningIfTrue ] @@ -53,6 +71,58 @@ SLMockInliningTestClass >> methodAInlineMultipleIfFalseReturningIfTrue [ self methodBIfFalseReturningIfTrue ] ] +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodAInlineReturningIfTrueIfFalseInAssignment [ + + | a | + a := self methodBReturningIfTrueIfFalse +] + +{ #category : 'inlining-returning-conditional' } +SLMockInliningTestClass >> methodAInlineReturningIfTrueIfFalseInReturn [ + + ^ self methodBReturningIfTrueIfFalse +] + +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodAInlineReturningIfTrueInAssignment [ + + | a | + a := self methodBReturningIfTrue +] + +{ #category : 'inlining-returning-conditional' } +SLMockInliningTestClass >> methodAInlineReturningIfTrueInReturn [ + + ^ self methodBReturningIfTrue +] + +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodAInlineReturningInlinedIfTrueIfFalseInAssignment [ + + | a | + a := self methodBReturningInlinedIfTrueIfFalse +] + +{ #category : 'inlining-returning-conditional' } +SLMockInliningTestClass >> methodAInlineReturningInlinedIfTrueIfFalseInReturn [ + + ^ self methodBReturningInlinedIfTrueIfFalse +] + +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodAInlineReturningInlinedIfTrueInAssignment [ + + | a | + a := self methodBReturningInlinedIfTrue +] + +{ #category : 'inlining-returning-conditional' } +SLMockInliningTestClass >> methodAInlineReturningInlinedIfTrueInReturn [ + + ^ self methodBReturningInlinedIfTrue +] + { #category : 'inlining-jump' } SLMockInliningTestClass >> methodAMultipleReturn [ @@ -215,6 +285,30 @@ SLMockInliningTestClass >> methodBReturnOnAssignment: aBlock [ ^ aBlock value: var ] +{ #category : 'inlining-returning-conditional-helpers' } +SLMockInliningTestClass >> methodBReturningIfTrue [ + + ^ self methodB ifTrue: [ true ] +] + +{ #category : 'inlining-returning-conditional-helpers' } +SLMockInliningTestClass >> methodBReturningIfTrueIfFalse [ + + ^ self methodB ifTrue: [ 1 ] ifFalse: [ 2 ] +] + +{ #category : 'inlining-returning-conditional-helpers' } +SLMockInliningTestClass >> methodBReturningInlinedIfTrue [ + + ^ self methodB ifTrue: [ self methodB ] +] + +{ #category : 'inlining-returning-conditional-helpers' } +SLMockInliningTestClass >> methodBReturningInlinedIfTrueIfFalse [ + + ^ self methodB ifTrue: [ self methodB ] ifFalse: [ self methodA ] +] + { #category : 'inlining-jump-helpers' } SLMockInliningTestClass >> methodBSimpleReturn [ diff --git a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st index 32e4e6f2d16..058e1c05a4b 100644 --- a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st @@ -471,14 +471,98 @@ SlangInliningTest >> testInlineInSwitchRemovesReturnStatement [ self assert: firstCaseConstantExpression parent equals: theSwitch ] +{ #category : 'inlining-assignment' } +SlangInliningTest >> testMethodAInlineIfFalseReturningIfTrueInAssignement [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineIfFalseReturningIfTrueInAssignement. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineIfFalseReturningIfTrueInAssignement */ +static sqInt +methodAInlineIfFalseReturningIfTrueInAssignement(void) +{ + sqInt a; + + /* begin methodBIfFalseReturningIfTrue */ + if (methodB()) { + a = 0; + goto l2; + } else { + } + a = 1; + l2: + ; + /* end methodBIfFalseReturningIfTrue */ + return 0; +}' +] + +{ #category : 'inlining-assignment' } +SlangInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInAssignment [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineMultipleIfFalseReturningIfTrueInAssignment. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineMultipleIfFalseReturningIfTrueInAssignment */ +static sqInt +methodAInlineMultipleIfFalseReturningIfTrueInAssignment(void) +{ + sqInt a; + + if (methodB()) { + /* begin methodBIfFalseReturningIfTrue */ + if (methodB()) { + a = 0; + goto l3; + } else { + } + a = 1; + l3: + ; + /* end methodBIfFalseReturningIfTrue */ + } + else { + /* begin methodBIfFalseReturningIfTrue */ + if (methodB()) { + a = 0; + goto l2; + } else { + } + a = 1; + l2: + ; + /* end methodBIfFalseReturningIfTrue */ + } + return 0; +}' +] + { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrue [ +SlangInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInReturn [ | method translation | ccg doBasicInlining: true. method := ccg methodNamed: - #methodAInlineMultipleIfFalseReturningIfTrue. + #methodAInlineMultipleIfFalseReturningIfTrueInReturn. translation := self translate: method. translation := translation trimBoth. @@ -512,12 +596,233 @@ methodAInlineMultipleIfFalseReturningIfTrue(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineifFalseReturningIfTrue [ +SlangInliningTest >> testMethodAInlineReturningIfTrue [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodAInlineReturningIfTrueInReturn. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningIfTrueInReturn */ +static sqInt +methodAInlineReturningIfTrueInReturn(void) +{ + return ((methodB()) + ? 1 + : 0); +}' +] + +{ #category : 'inlining-assignment' } +SlangInliningTest >> testMethodAInlineReturningIfTrueIfFalseInAssignment [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineReturningIfTrueIfFalseInAssignment. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningIfTrueIfFalseInAssignment */ +static sqInt +methodAInlineReturningIfTrueIfFalseInAssignment(void) +{ + sqInt a; + + a = ((methodB()) + ? 1 + : 2); + return 0; +}' +] + +{ #category : 'inlining-returning-conditional' } +SlangInliningTest >> testMethodAInlineReturningIfTrueIfFalseInReturn [ | method translation | ccg doBasicInlining: true. - method := ccg methodNamed: #methodAInlineIfFalseReturningIfTrue. + method := ccg methodNamed: + #methodAInlineReturningIfTrueIfFalseInReturn. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningIfTrueIfFalseInReturn */ +static sqInt +methodAInlineReturningIfTrueIfFalseInReturn(void) +{ + return ((methodB()) + ? 1 + : 2); +}' +] + +{ #category : 'inlining-assignment' } +SlangInliningTest >> testMethodAInlineReturningIfTrueInAssignment [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineReturningIfTrueInAssignment. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningIfTrueInAssignment */ +static sqInt +methodAInlineReturningIfTrueInAssignment(void) +{ + sqInt a; + + a = ((methodB()) + ? 1 + : 0); + return 0; +}' +] + +{ #category : 'inlining-assignment' } +SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInAssignment [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineReturningInlinedIfTrueIfFalseInAssignment. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningInlinedIfTrueIfFalseInAssignment */ +static sqInt +methodAInlineReturningInlinedIfTrueIfFalseInAssignment(void) +{ + sqInt a; + + if (methodB()) { + /* begin methodB */ + a = 2 + 2; + /* end methodB */ + } else { + /* begin methodA */ + 1 + 1; + /* begin methodB */ + a = 2 + 2; + /* end methodB */ + /* end methodA */ + } + return 0; +}' +] + +{ #category : 'inlining-returning-conditional' } +SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInReturn [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineReturningInlinedIfTrueIfFalseInReturn. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningInlinedIfTrueIfFalseInReturn */ +static sqInt +methodAInlineReturningInlinedIfTrueIfFalseInReturn(void) +{ + return ((methodB()) + ? (/* begin methodB */ 2 + 2 /* end methodB */) + : (/* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */)); +}' +] + +{ #category : 'inlining-assignment' } +SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueInAssignment [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineReturningInlinedIfTrueInAssignment. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningInlinedIfTrueInAssignment */ +static sqInt +methodAInlineReturningInlinedIfTrueInAssignment(void) +{ + sqInt a; + + a = ((methodB()) + ? (/* begin methodB */ 2 + 2 /* end methodB */) + : 0); + return 0; +}' +] + +{ #category : 'inlining-returning-conditional' } +SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueInReturn [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineReturningInlinedIfTrueInReturn. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAInlineReturningInlinedIfTrueInReturn */ +static sqInt +methodAInlineReturningInlinedIfTrueInReturn(void) +{ + return ((methodB()) + ? (/* begin methodB */ 2 + 2 /* end methodB */) + : 0); +}' +] + +{ #category : 'inlining-returning-conditional' } +SlangInliningTest >> testMethodAInlineifFalseReturningIfTrueInReturn [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAInlineIfFalseReturningIfTrueInReturn. translation := self translate: method. translation := translation trimBoth. @@ -525,9 +830,9 @@ SlangInliningTest >> testMethodAInlineifFalseReturningIfTrue [ self assert: translation equals: - '/* SLMockInliningTestClass>>#methodAInlineIfFalseReturningIfTrue */ + '/* SLMockInliningTestClass>>#methodAInlineIfFalseReturningIfTrueInReturn */ static sqInt -methodAInlineIfFalseReturningIfTrue(void) +methodAInlineIfFalseReturningIfTrueInReturn(void) { /* begin methodBIfFalseReturningIfTrue */ if (methodB()) { diff --git a/smalltalksrc/VMMaker/SlangInliningTest.extension.st b/smalltalksrc/VMMaker/SlangInliningTest.extension.st index 8b75295e40a..c6b5def57e9 100644 --- a/smalltalksrc/VMMaker/SlangInliningTest.extension.st +++ b/smalltalksrc/VMMaker/SlangInliningTest.extension.st @@ -5,6 +5,8 @@ SlangInliningTest >> setUp [ super setUp. ccg addClass: SLMockInliningTestClass. + "necessary to get the type of sqInt" + SpurMemoryManager initBytesPerWord: 8. ccg inferTypes. sLInliner := SLInliner new ] From cecaa681992e00e298f824fc95d32ef5daddd050 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 18 Jun 2025 11:36:15 +0200 Subject: [PATCH 02/86] better visitor --- .../SLAnnotatorVisitorTest.class.st | 16 +-- smalltalksrc/Slang/SLInliner.class.st | 51 +++++-- .../Slang/SLNodeAnnotatorVisitor.class.st | 128 +++++++++++++----- smalltalksrc/Slang/TAssignmentNode.class.st | 10 +- smalltalksrc/Slang/TGoToNode.class.st | 22 ++- 5 files changed, 172 insertions(+), 55 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 523efd47c29..4409ee85354 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -45,19 +45,19 @@ SLAnnotatorVisitorTest >> testMethodWithAssignment [ self assert: ((expressions select: [ :node | node isConstant and: [ node value = 1 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor exitVarFor: node) name = #x and: [ + (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) name = #x and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: ((expressions select: [ :node | node isConstant and: [ node value ~= 1 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor exitVarFor: node) isNil and: [ + (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) isNil and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: ((expressions select: [ :node | node isSend ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor exitVarFor: node) name = #y and: [ + (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) name = #y and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: (statements allSatisfy: [ :node | @@ -133,13 +133,13 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor exitVarFor: node) name = #x ] ] ]). + (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) name = #x ] ] ]). self assert: ((expressions reject: [ :node | node isConstant ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor exitVarFor: node) isNil ] ] ]). + (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) isNil ] ] ]). self assert: (statements allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ @@ -318,18 +318,18 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ self assert: ((sLNodeAnnotatorVisitor isInAssignment: firstSend) and: [ - (sLNodeAnnotatorVisitor exitVarFor: firstSend) name = #x and: [ + (sLNodeAnnotatorVisitor getAssignmentForExitVar: firstSend) name = #x and: [ (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: secondSend) and: [ - (sLNodeAnnotatorVisitor exitVarFor: secondSend) isNil and: [ + (sLNodeAnnotatorVisitor getAssignmentForExitVar: secondSend) isNil and: [ (sLNodeAnnotatorVisitor isInReturn: secondSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: thirdSend) and: [ - (sLNodeAnnotatorVisitor exitVarFor: thirdSend) name = #y and: [ + (sLNodeAnnotatorVisitor getAssignmentForExitVar: thirdSend) name = #y and: [ (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) ] diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index d38dd904a87..402c422b7bd 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -343,13 +343,13 @@ SLInliner >> inlineSend: aSendNode [ otherwise the assignee variable type must match the return type of the inlinee. Return types are not propagated." - | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn | + | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn assignDirectReturnAndExitVarBlock | sel := aSendNode selector. callee := codeGenerator methodNamed: sel. - directReturn := sLNodeAnnotatorVisitor isDirectReturn: aSendNode. - exitVar := (sLNodeAnnotatorVisitor exitVarFor: aSendNode) ifNotNil: [ - :var | var name ]. + assignDirectReturnAndExitVarBlock := [ :res | + directReturn := res first. + exitVar := res second ]. "convenient for debugging..." codeGenerator maybeBreakForInlineOf: aSendNode in: self. @@ -363,6 +363,9 @@ SLInliner >> inlineSend: aSendNode [ self checkForFlagIn: callee. + assignDirectReturnAndExitVarBlock value: + (self setDirectReturnAndExitVar: aSendNode). + self propagateReturnTypeDirectReturn: directReturn exitVar: exitVar @@ -380,20 +383,29 @@ SLInliner >> inlineSend: aSendNode [ currentMethod addVarsDeclarationsAndLabelsOf: callee except: omittedParameters. - callee hasReturn ifTrue: [ directReturn ifFalse: [ exitLabel := currentMethod unusedLabelForInliningInto: currentMethod. - (callee exitVar: exitVar label: exitLabel) + (callee exitVar: nil label: exitLabel) ifTrue: [ currentMethod labels add: exitLabel ] - ifFalse: [ "is label used?" exitLabel := nil ] ]. + ifFalse: [ "is label used?" exitLabel := nil ] ] ]. + + (sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode) + ifTrue: [ + assignDirectReturnAndExitVarBlock value: + (self setDirectReturnAndExitVar: aSendNode) ]. + + "little hack to move assignment to the lowest part of the ast" + aSendNode parent replaceChild: aSendNode with: callee parseTree. + (sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode) + ifTrue: [ + aSendNode parent parent + replaceChild: callee parseTree + with: aSendNode. + assignDirectReturnAndExitVarBlock value: + (self setDirectReturnAndExitVar: aSendNode) ]. - sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode. - sLNodeAnnotatorVisitor visit: currentMethod parseTree. - directReturn := sLNodeAnnotatorVisitor isDirectReturn: aSendNode. - exitVar := (sLNodeAnnotatorVisitor exitVarFor: aSendNode) ifNotNil: [ - :var | var name ] ]. (inlineStmts := OrderedCollection new: callee statements size + callee args size + 2) @@ -585,6 +597,20 @@ SLInliner >> replaceNodeIn: aSendNode with: data [ except: method args ] +{ #category : 'inlining-support' } +SLInliner >> setDirectReturnAndExitVar: aSendNode [ + + | directReturn exitVar | + sLNodeAnnotatorVisitor visit: currentMethod parseTree. + directReturn := sLNodeAnnotatorVisitor isDirectReturn: aSendNode. + exitVar := (sLNodeAnnotatorVisitor getAssignmentForExitVar: aSendNode) ifNotNil: [ + :var | var name ]. + ^ OrderedCollection new + add: directReturn; + add: exitVar; + yourself +] + { #category : 'inlining-preparation' } SLInliner >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. @@ -727,7 +753,6 @@ SLInliner >> tryToInlineMethodStatementsListsInCurrentMethod [ | stmtLists didSomething newStatements | didSomething := false. stmtLists := self statementsListsForInliningInCurrentMethod. - sLNodeAnnotatorVisitor visit: currentMethod parseTree. stmtLists do: [ :stmtList | newStatements := TStatementListNode statements: diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index ae1d012ae66..65e4e446ff8 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -7,7 +7,8 @@ Class { 'currentMethod', 'info', 'useAsExpressionStack', - 'inExpressionStack' + 'inExpressionStack', + 'jumpMetStack' ], #category : 'Slang', #package : 'Slang' @@ -35,14 +36,19 @@ SLNodeAnnotatorVisitor >> addToInExpression: aNode [ { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> addToReturn: aNode [ - "mark the given node has being inside a return expression, it doesn't mean that the node itself is an expression> - true or false on the stack indicate if the node is the a direct return and is the returning expression or not" + "mark the given node has being inside a return expression, it doesn't mean that the node itself is the exit value + true or false on the stack only indicate if the node is the effective value of the returning expression or not" | entry assoc | returnStack isEmpty ifTrue: [ ^ self ]. entry := info at: aNode. assoc := returnStack top. + + "we are just above a jump thus the node is an exitValue" + (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifTrue: [ + assoc := true -> assoc value ]. + entry at: self returnString put: assoc. returnStack pop. "no longer the direct return" @@ -72,11 +78,37 @@ SLNodeAnnotatorVisitor >> addToStatementOrExpression: aNode [ { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> annotateNodeState: aNode [ + self annotateNodeState: aNode canBeAnExpression: true +] + +{ #category : 'helpers-annotate' } +SLNodeAnnotatorVisitor >> annotateNodeState: aNode canBeAnExpression: aBoolean [ + "annotate the node by assuming the ast exploration ensure that the different exitValue are the first to reach this method for each branch" + self ensureEntryInInfoDictFor: aNode. + self addToInExpression: aNode. + + "comment, label and goTo cannot be an expression, in C as in Pharo + so if we may be looking for an exitValue, the node should be ignored + and we can mark it as statement" + aBoolean ifFalse: [ + | top | + top := useAsExpressionStack top. + useAsExpressionStack pop. + useAsExpressionStack push: self statementString. + self addToStatementOrExpression: aNode. + useAsExpressionStack pop. + useAsExpressionStack push: top. + ^ self ]. + self addToStatementOrExpression: aNode. self addToReturn: aNode. self addToExitVar: aNode. - self addToInExpression: aNode + + (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifFalse: [ + ^ self ]. + jumpMetStack pop. + jumpMetStack push: false ] { #category : 'cleanup' } @@ -97,12 +129,6 @@ SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ info at: aNode ifAbsentPut: [ IdentityDictionary new ] ] -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> exitVarFor: aNode [ - - ^ (info at: aNode) at: self exitVarString ifAbsent: [ nil ] -] - { #category : 'helpers-constant' } SLNodeAnnotatorVisitor >> exitVarString [ @@ -115,11 +141,22 @@ SLNodeAnnotatorVisitor >> expressionString [ ^ #expression ] +{ #category : 'accessing' } +SLNodeAnnotatorVisitor >> getAssignmentForExitVar: aNode [ + + | assignmentNode | + assignmentNode := (info at: aNode) + at: self exitVarString + ifAbsent: [ nil ]. + (assignmentNode isNil or: [ assignmentNode key isNil ]) ifTrue: [ + ^ nil ]. + ^ assignmentNode value +] + { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ | returnTop exitVarTop size | - useAsExpressionStack push: self expressionString. inExpressionStack push: self inExpressionString. size := aSendNode arguments size. @@ -138,7 +175,6 @@ SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ aSendNode receiver accept: self. inExpressionStack pop. - useAsExpressionStack pop. self annotateNodeState: aSendNode ] @@ -158,6 +194,7 @@ SLNodeAnnotatorVisitor >> initialize [ returnStack := Stack new. exitVarStack := Stack new. inExpressionStack := Stack new. + jumpMetStack := Stack new. info := IdentityDictionary new ] @@ -205,10 +242,11 @@ SLNodeAnnotatorVisitor >> isInReturn: aNode [ { #category : 'transformation' } SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ - "use in inlining for redondant return : return {return exp} -> {return exp}, clean all the info as a side effect, it needs to be recalculated" + "use in inlining for redondant return : return {return exp} -> {return exp}, clean all the info as a side effect, it needs to be recalculated + answer if anything was done" | returningParent | - (self isInReturn: aSendNode) ifFalse: [ ^ self ]. + (self isDirectReturn: aSendNode) ifFalse: [ ^ false ]. returningParent := self returningParentFor: aSendNode. @@ -218,7 +256,8 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ replaceChild: returningParent with: returningParent expression. - self cleanInfo + self cleanInfo. + ^ true ] { #category : 'helpers-constant' } @@ -229,7 +268,7 @@ SLNodeAnnotatorVisitor >> returnString [ { #category : 'accessing' } SLNodeAnnotatorVisitor >> returningParentFor: aNode [ - + "if the node is in a return give the returningParent even if the node is not the exitvalue itself" ^ (info at: aNode) at: self returnString ifPresent: [ :assoc | assoc value ] @@ -254,6 +293,21 @@ SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ ifFalse: [ self transformDirectReturnFrom: child ] ] ] +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode exitVar: exitVar [ + "recursively go throught the ast to transfrom expression of assignment + a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" + + parentNode children do: [ :child | + (self getAssignmentForExitVar: child) == exitVar + ifTrue: [ + child parent replaceChild: child with: (TAssignmentNode new + variable: exitVar; + expression: child; + yourself) ] + ifFalse: [ self transformToAssignmentFrom: child exitVar: exitVar ] ] +] + { #category : 'accessing' } SLNodeAnnotatorVisitor >> useAsExpressionsSet [ @@ -283,7 +337,8 @@ SLNodeAnnotatorVisitor >> visit: aNode [ self assert: exitVarStack isEmpty. self assert: returnStack isEmpty. self assert: useAsExpressionStack isEmpty. - self assert: inExpressionStack isEmpty + self assert: inExpressionStack isEmpty. + self assert: jumpMetStack isEmpty ] { #category : 'visiting' } @@ -294,13 +349,18 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ useAsExpressionStack push: self expressionString. inExpressionStack push: self inExpressionString. - exitVarStack push: anAssignmentNode variable. + +" anAssignmentNode haveBeenMetInInlining ifTrue: [ + exitVarStack push: true -> anAssignmentNode variable ]." + exitVarStack push: true -> anAssignmentNode variable. anAssignmentNode expression accept: self. anAssignmentNode variable accept: self. inExpressionStack pop. useAsExpressionStack pop. + +" anAssignmentNode haveBeenMetInInlining ifTrue: [ exitVarStack pop ]." exitVarStack pop ] @@ -350,7 +410,7 @@ SLNodeAnnotatorVisitor >> visitInlineNode: anInlineNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitLabeledCommentNode: aLabeledCommentNode [ - self annotateNodeState: aLabeledCommentNode + self annotateNodeState: aLabeledCommentNode canBeAnExpression: false ] { #category : 'visiting' } @@ -378,33 +438,37 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ self annotateNodeState: aSendNode. - useAsExpressionStack push: self expressionString. inExpressionStack push: self inExpressionString. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. - inExpressionStack pop. - useAsExpressionStack pop + inExpressionStack pop ] { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ - | stmts | - aStatementsListNode isEmptyOrHasNothingButComments + | stmts needExitValue | + "aStatementsListNode isEmptyOrHasNothingButComments ifFalse: [ - aStatementsListNode lastNonCommentStatement accept: self. - stmts := aStatementsListNode allButLastNonCommentStatement ] - ifTrue: [ stmts := aStatementsListNode statements ]. - - self annotateNodeState: aStatementsListNode. - + aStatementsListNode lastNonCommentOrGoToOrLabelStatement accept: + self. + stmts := aStatementsListNode allButLastNonCommentOrGotoStatement ] + ifTrue: [ " + stmts := aStatementsListNode statements. "]" + needExitValue := useAsExpressionStack isEmpty + ifTrue: [ false ] + ifFalse: [ + useAsExpressionStack top = self expressionString ]. useAsExpressionStack push: self statementString. + jumpMetStack push: needExitValue. - stmts do: [ :stmt | stmt accept: self ]. + stmts reverse do: [ :stmt | stmt accept: self ]. - useAsExpressionStack pop + useAsExpressionStack pop. + jumpMetStack pop. + self annotateNodeState: aStatementsListNode ] { #category : 'visiting' } diff --git a/smalltalksrc/Slang/TAssignmentNode.class.st b/smalltalksrc/Slang/TAssignmentNode.class.st index 2006c6f1075..60515bd989c 100644 --- a/smalltalksrc/Slang/TAssignmentNode.class.st +++ b/smalltalksrc/Slang/TAssignmentNode.class.st @@ -3,7 +3,8 @@ Class { #superclass : 'TParseNode', #instVars : [ 'variable', - 'expression' + 'expression', + 'metInInlining' ], #category : 'Slang-AST', #package : 'Slang', @@ -260,6 +261,13 @@ TAssignmentNode >> expression: anExpression [ expression parent: self. ] +{ #category : 'initialization' } +TAssignmentNode >> initialize [ + + super initialize. + metInInlining := false +] + { #category : 'testing' } TAssignmentNode >> isAssignment [ diff --git a/smalltalksrc/Slang/TGoToNode.class.st b/smalltalksrc/Slang/TGoToNode.class.st index e7f860455a7..1e7c770bf7e 100644 --- a/smalltalksrc/Slang/TGoToNode.class.st +++ b/smalltalksrc/Slang/TGoToNode.class.st @@ -2,7 +2,8 @@ Class { #name : 'TGoToNode', #superclass : 'TParseNode', #instVars : [ - 'label' + 'label', + 'metInInlining' ], #category : 'Slang-AST', #package : 'Slang', @@ -39,6 +40,19 @@ TGoToNode >> children [ ^ #() ] +{ #category : 'accessing' } +TGoToNode >> haveBeenMetInInlining [ + + ^ metInInlining +] + +{ #category : 'initialization' } +TGoToNode >> initialize [ + + super initialize. + metInInlining := false +] + { #category : 'testing' } TGoToNode >> isGoTo [ @@ -58,6 +72,12 @@ TGoToNode >> label [ ^label ] +{ #category : 'accessing' } +TGoToNode >> metInInlining [ + + metInInlining := true +] + { #category : 'enumerating' } TGoToNode >> nodesDo: aBlock parent: parent [ aBlock value: self value: parent From ca24c18188052bd832f1a5a2d163f90ea90121c0 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 18 Jun 2025 11:37:38 +0200 Subject: [PATCH 03/86] change not comitted --- .../Slang/SLNodeAnnotatorVisitor.class.st | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 65e4e446ff8..35c79bc685a 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -16,15 +16,21 @@ Class { { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> addToExitVar: aNode [ - "when an assignment has been met, all of the expression is in it but regarding exitVar, we only want the top level expression node to have the exitVar, the sub node if they exist have exitvar to nil and are still considered inside an assignment" + "when an assignment has been met, the effective exitVar is not all of the nodes in it, often it is just a single node (or more if conditionals) the nodes have a link to theirs assignment + true or nil on the stack indicate if the node is the effective exitVar" - | entry | + | entry top | exitVarStack isEmpty ifTrue: [ ^ self ]. + top := exitVarStack top. entry := info at: aNode. - entry at: self exitVarString put: exitVarStack top. + "we are just above a jump thus the node is an exitVar" + (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifTrue: [ + top := true -> top value ]. + + entry at: self exitVarString put: top. exitVarStack pop. - exitVarStack push: nil + exitVarStack push: nil -> top value ] { #category : 'helpers-annotate' } @@ -240,6 +246,30 @@ SLNodeAnnotatorVisitor >> isInReturn: aNode [ ifAbsent: [ false ] ] +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ + "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated + answer if something was done" + + | assignment exitVar | + exitVar := self getAssignmentForExitVar: aSendNode. + exitVar ifNil: [ ^ false ]. + + assignment := exitVar parent. + + "necessary because of the inlining hack" + self cleanInfo. + self visit: assignment tMethod parseTree. + self transformToAssignmentFrom: assignment exitVar: exitVar. + + assignment parent + replaceChild: assignment + with: assignment expression. + + self cleanInfo. + ^ true +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ "use in inlining for redondant return : return {return exp} -> {return exp}, clean all the info as a side effect, it needs to be recalculated @@ -396,7 +426,12 @@ SLNodeAnnotatorVisitor >> visitConstantNode: aConstantNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ - self annotateNodeState: aTGotoNode + self annotateNodeState: aTGotoNode canBeAnExpression: false. + + aTGotoNode haveBeenMetInInlining ifTrue: [ ^ self ]. + jumpMetStack pop. + jumpMetStack push: true. + aTGotoNode metInInlining ] { #category : 'visiting' } From dbbff3a88502ff81a768dd4f64beb9c44dad8238 Mon Sep 17 00:00:00 2001 From: Renaud Date: Wed, 18 Jun 2025 11:43:13 +0200 Subject: [PATCH 04/86] renaming --- smalltalksrc/Slang-Tests/SlangInliningTest.class.st | 4 ++-- ...trategy.class.st => SLAbstractInlineStrategy.class.st} | 8 ++++---- smalltalksrc/Slang/SLInlinerStrategy.class.st | 2 +- smalltalksrc/Slang/SLOldInlineStrategy.class.st | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename smalltalksrc/Slang/{SLInlineStrategy.class.st => SLAbstractInlineStrategy.class.st} (60%) diff --git a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st index 058e1c05a4b..04d8df62d1a 100644 --- a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st @@ -570,9 +570,9 @@ SlangInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInReturn [ self assert: translation equals: - '/* SLMockInliningTestClass>>#methodAInlineMultipleIfFalseReturningIfTrue */ + '/* SLMockInliningTestClass>>#methodAInlineMultipleIfFalseReturningIfTrueInReturn */ static sqInt -methodAInlineMultipleIfFalseReturningIfTrue(void) +methodAInlineMultipleIfFalseReturningIfTrueInReturn(void) { if (methodB()) { /* begin methodBIfFalseReturningIfTrue */ diff --git a/smalltalksrc/Slang/SLInlineStrategy.class.st b/smalltalksrc/Slang/SLAbstractInlineStrategy.class.st similarity index 60% rename from smalltalksrc/Slang/SLInlineStrategy.class.st rename to smalltalksrc/Slang/SLAbstractInlineStrategy.class.st index d62662aaee2..db248d1f2d2 100644 --- a/smalltalksrc/Slang/SLInlineStrategy.class.st +++ b/smalltalksrc/Slang/SLAbstractInlineStrategy.class.st @@ -1,5 +1,5 @@ Class { - #name : 'SLInlineStrategy', + #name : 'SLAbstractInlineStrategy', #superclass : 'Object', #instVars : [ 'codeGenerator' @@ -9,7 +9,7 @@ Class { } { #category : 'accessing' } -SLInlineStrategy class >> codeGenerator: aCodeGenerator [ +SLAbstractInlineStrategy class >> codeGenerator: aCodeGenerator [ ^ self new codeGenerator: aCodeGenerator; @@ -17,13 +17,13 @@ SLInlineStrategy class >> codeGenerator: aCodeGenerator [ ] { #category : 'accessing' } -SLInlineStrategy >> codeGenerator: aCodeGenerator [ +SLAbstractInlineStrategy >> codeGenerator: aCodeGenerator [ codeGenerator := aCodeGenerator ] { #category : 'inlining' } -SLInlineStrategy >> doInliningIn: aTMethod [ +SLAbstractInlineStrategy >> doInliningIn: aTMethod [ self subclassResponsibility ] diff --git a/smalltalksrc/Slang/SLInlinerStrategy.class.st b/smalltalksrc/Slang/SLInlinerStrategy.class.st index 98e605321bb..c710f37e813 100644 --- a/smalltalksrc/Slang/SLInlinerStrategy.class.st +++ b/smalltalksrc/Slang/SLInlinerStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : 'SLInlinerStrategy', - #superclass : 'SLInlineStrategy', + #superclass : 'SLAbstractInlineStrategy', #instVars : [ 'sLInliner' ], diff --git a/smalltalksrc/Slang/SLOldInlineStrategy.class.st b/smalltalksrc/Slang/SLOldInlineStrategy.class.st index d42fe863c8d..9ba69a18cae 100644 --- a/smalltalksrc/Slang/SLOldInlineStrategy.class.st +++ b/smalltalksrc/Slang/SLOldInlineStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : 'SLOldInlineStrategy', - #superclass : 'SLInlineStrategy', + #superclass : 'SLAbstractInlineStrategy', #category : 'Slang', #package : 'Slang' } From 86247798a9e933862566d72cd0e995f75e682c3d Mon Sep 17 00:00:00 2001 From: Renaud Date: Wed, 18 Jun 2025 19:36:06 +0200 Subject: [PATCH 05/86] little bit cleaner separation of things in the visitor fix bugs --- .../SLAnnotatorVisitorTest.class.st | 27 ++++------- .../Slang/MLStatementListBuider.class.st | 25 ++++++---- smalltalksrc/Slang/RBCascadeNode.extension.st | 2 +- smalltalksrc/Slang/SLInliner.class.st | 3 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 48 ++++++++----------- .../Slang/TStatementListNode.class.st | 16 +++++++ 6 files changed, 65 insertions(+), 56 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 4409ee85354..14222e9ffdb 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -80,8 +80,8 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ expressions. constantSetStatement := self getValueOfConstantNodeInSet: statements. - self assert: statements size equals: 4. - self assert: expressions size equals: 2. + self assert: statements size equals: 5. + self assert: expressions size equals: 1. self assert: constantSetExpression size equals: 1. self assert: (constantSetExpression includes: 4). @@ -90,13 +90,6 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ self assert: (constantSetStatement includes: 2). self assert: (constantSetStatement includes: 3). - self assert: - ((expressions select: [ :node | node isConstant not ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). - self assert: ((expressions select: [ :node | node isConstant ]) allSatisfy: [ :node | @@ -123,8 +116,8 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. - self assert: statements size equals: 2. - self assert: expressions size equals: 7. + self assert: statements size equals: 5. + self assert: expressions size equals: 4. self assert: (constantSetExpression includes: 1). self assert: (constantSetExpression includes: 2). self assert: constantSetExpression size equals: 3. @@ -161,15 +154,15 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ constantSetExpression := self getValueOfConstantNodeInSet: expressions. - self assert: statements size equals: 7. - self assert: expressions size equals: 5. + self assert: statements size equals: 10. + self assert: expressions size equals: 2. self assert: (constantSetStatement includes: 1). self assert: (constantSetStatement includes: 2). self assert: (constantSetStatement includes: 3). self assert: (constantSetStatement includes: 4). + self assert: (constantSetStatement includes: true). - self assert: (constantSetExpression includes: true). self assert: (constantSetExpression includes: 5). self assert: (constantSetExpression includes: 6). @@ -232,12 +225,12 @@ SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ constantSetExpression := self getValueOfConstantNodeInSet: expressions. - self assert: statements size equals: 2. - self assert: expressions size equals: 7. + self assert: statements size equals: 7. + self assert: expressions size equals: 2. self assert: (constantSetExpression includes: 2). self assert: (constantSetExpression includes: 4). - self assert: constantSetExpression size equals: 3. + self assert: constantSetExpression size equals: 2. self assert: ((expressions select: [ :node | node isConstant and: [ node value isInteger ] ]) allSatisfy: [ diff --git a/smalltalksrc/Slang/MLStatementListBuider.class.st b/smalltalksrc/Slang/MLStatementListBuider.class.st index 281701a71d9..95107fdd984 100644 --- a/smalltalksrc/Slang/MLStatementListBuider.class.st +++ b/smalltalksrc/Slang/MLStatementListBuider.class.st @@ -65,23 +65,30 @@ MLStatementListBuider >> addStatement: aStatement [ ] { #category : 'transforming' } -MLStatementListBuider >> assignLastExpressionOf: aNode toVariable: aTVariableNode [ - +MLStatementListBuider >> assignLastExpressionOf: aNode toVariable: aTVariableNode [ "We need to take the expression and find its last expression recursively. We should rewrite it to add an assignment to its last expression" - + | worklist | worklist := OrderedCollection with: aNode. - [ worklist isEmpty ] whileFalse: [ | current | + [ worklist isEmpty ] whileFalse: [ + | current | current := worklist removeLast. - current isStatementList ifTrue: [ - worklist add: current lastNonCommentStatement - ] ifFalse: [ | replacement parentBeforeReplacement | + current isStatementList + ifTrue: [ + worklist add: current lastNonCommentOrGoToOrLabelStatement ] + ifFalse: [ + | replacement parentBeforeReplacement | self flag: #polymorphism. parentBeforeReplacement := current parent. replacement := (current isSend and: [ current isConditionalSend ]) - ifTrue: [ self transformControlFlowNodeForValue: current withVariable: aTVariableNode copy ] - ifFalse: [ current assignLastExpressionTo: aTVariableNode copy ]. + ifTrue: [ + self + transformControlFlowNodeForValue: current + withVariable: aTVariableNode copy ] + ifFalse: [ + current assignLastExpressionTo: + aTVariableNode copy ]. parentBeforeReplacement replaceChild: current with: replacement ] ]. ^ aNode ] diff --git a/smalltalksrc/Slang/RBCascadeNode.extension.st b/smalltalksrc/Slang/RBCascadeNode.extension.st index a9de1f2aff9..ff42b80d52f 100644 --- a/smalltalksrc/Slang/RBCascadeNode.extension.st +++ b/smalltalksrc/Slang/RBCascadeNode.extension.st @@ -21,6 +21,6 @@ RBCascadeNode >> asTranslatorNodeIn: aTMethod [ expression: receiverNode). receiverNode := varNode]. messages do: - [ :msg | s nextPut: ((msg asTranslatorNodeIn: aTMethod) receiver: receiverNode)]]); + [ :msg | s nextPut: ((msg asTranslatorNodeIn: aTMethod) receiver: receiverNode copy)]]); comment: self commentOrNil ] diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 402c422b7bd..1284aa85c18 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -400,9 +400,10 @@ SLInliner >> inlineSend: aSendNode [ aSendNode parent replaceChild: aSendNode with: callee parseTree. (sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode) ifTrue: [ - aSendNode parent parent + callee parseTree parent replaceChild: callee parseTree with: aSendNode. + callee parseTree parent: callee. assignDirectReturnAndExitVarBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 35c79bc685a..c0cb15b6be8 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -66,6 +66,10 @@ SLNodeAnnotatorVisitor >> addToStatementOrExpression: aNode [ | entry | entry := info at: aNode. + (self isDirectExpression: aNode) ifTrue: [ + entry at: self expressionString put: true. + aNode isAnExpression: true. + ^ self ]. (useAsExpressionStack isNotEmpty and: [ useAsExpressionStack top = self expressionString ]) ifTrue: [ @@ -98,18 +102,14 @@ SLNodeAnnotatorVisitor >> annotateNodeState: aNode canBeAnExpression: aBoolean [ so if we may be looking for an exitValue, the node should be ignored and we can mark it as statement" aBoolean ifFalse: [ - | top | - top := useAsExpressionStack top. - useAsExpressionStack pop. useAsExpressionStack push: self statementString. self addToStatementOrExpression: aNode. useAsExpressionStack pop. - useAsExpressionStack push: top. ^ self ]. - self addToStatementOrExpression: aNode. self addToReturn: aNode. self addToExitVar: aNode. + self addToStatementOrExpression: aNode. (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifFalse: [ ^ self ]. @@ -161,11 +161,13 @@ SLNodeAnnotatorVisitor >> getAssignmentForExitVar: aNode [ { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ - + + "conditionals are handle like they would be in C, they are not considered as expression like other sends comming from pharo" | returnTop exitVarTop size | inExpressionStack push: self inExpressionString. size := aSendNode arguments size. + "re give the informations for each branch after using it" aSendNode arguments doWithIndex: [ :arg :index | returnTop := returnStack isEmpty ifFalse: [ returnStack top ]. exitVarTop := exitVarStack isEmpty ifFalse: [ exitVarStack top ]. @@ -177,12 +179,14 @@ SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ exitVarTop ifNotNil: [ exitVarStack pop. exitVarStack push: exitVarTop ] ] ]. - + + "can't be a exitValue, but the information have been taken by the arguments" aSendNode receiver accept: self. inExpressionStack pop. - - self annotateNodeState: aSendNode + + "the effective values are not the result of this message" + self annotateNodeState: aSendNode canBeAnExpression: false ] { #category : 'helpers-constant' } @@ -207,10 +211,7 @@ SLNodeAnnotatorVisitor >> initialize [ { #category : 'testing' } SLNodeAnnotatorVisitor >> isDirectExpression: aNode [ - ^ (info at: aNode) - at: self expressionString - ifPresent: [ true ] - ifAbsent: [ false ] + ^ (self isDirectReturn: aNode) or: [ (self getAssignmentForExitVar: aNode) isNotNil ] ] { #category : 'testing' } @@ -256,7 +257,7 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ exitVar ifNil: [ ^ false ]. assignment := exitVar parent. - + self haltIf: [ assignment isAssignment not ]. "necessary because of the inlining hack" self cleanInfo. self visit: assignment tMethod parseTree. @@ -379,9 +380,6 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ useAsExpressionStack push: self expressionString. inExpressionStack push: self inExpressionString. - -" anAssignmentNode haveBeenMetInInlining ifTrue: [ - exitVarStack push: true -> anAssignmentNode variable ]." exitVarStack push: true -> anAssignmentNode variable. anAssignmentNode expression accept: self. @@ -390,7 +388,6 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ inExpressionStack pop. useAsExpressionStack pop. -" anAssignmentNode haveBeenMetInInlining ifTrue: [ exitVarStack pop ]." exitVarStack pop ] @@ -474,36 +471,31 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ self annotateNodeState: aSendNode. inExpressionStack push: self inExpressionString. + useAsExpressionStack push: self expressionString. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. - inExpressionStack pop + inExpressionStack pop. + useAsExpressionStack pop ] { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ | stmts needExitValue | - "aStatementsListNode isEmptyOrHasNothingButComments - ifFalse: [ - aStatementsListNode lastNonCommentOrGoToOrLabelStatement accept: - self. - stmts := aStatementsListNode allButLastNonCommentOrGotoStatement ] - ifTrue: [ " - stmts := aStatementsListNode statements. "]" + stmts := aStatementsListNode statements. needExitValue := useAsExpressionStack isEmpty ifTrue: [ false ] ifFalse: [ useAsExpressionStack top = self expressionString ]. useAsExpressionStack push: self statementString. jumpMetStack push: needExitValue. - stmts reverse do: [ :stmt | stmt accept: self ]. useAsExpressionStack pop. jumpMetStack pop. - self annotateNodeState: aStatementsListNode + self annotateNodeState: aStatementsListNode canBeAnExpression: false ] { #category : 'visiting' } diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index fa6bc9d1dba..39335423c28 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -532,6 +532,22 @@ TStatementListNode >> last [ ^ self statements last ] +{ #category : 'accessing' } +TStatementListNode >> lastNonCommentOrGoToOrLabelStatement [ + "the last statement can be a comment if the TStatementList has been through inlining, return the actual last statement" + + | last indexOfLast | + last := statements last. + indexOfLast := statements size. + [ last isComment or: [ last isGoTo or: [ last isLabel ] ] ] + whileTrue: [ + indexOfLast := indexOfLast - 1. + indexOfLast = 0 ifTrue: [ ^ nil ]. + last := statements at: indexOfLast ]. + + ^ last +] + { #category : 'accessing' } TStatementListNode >> lastNonCommentStatement [ "the last statement can be a comment if the TStatementList has been through inlining, return the actual last statement" From 7794151a90c5b1765d1247feecfcedf827a0e1a0 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 20 Jun 2025 18:12:36 +0200 Subject: [PATCH 06/86] beaucoup de refactor fix tous les tests de bug lier seulement a l'inlining --- .../SLAnnotatorVisitorTest.class.st | 103 +++---- .../Slang-Tests/SlangInliningTest.class.st | 32 ++- smalltalksrc/Slang/SLInliner.class.st | 11 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 262 ++++++++++-------- 4 files changed, 226 insertions(+), 182 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 14222e9ffdb..f71134ae5d3 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -29,8 +29,8 @@ SLAnnotatorVisitorTest >> testMethodWithAssignment [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. @@ -45,19 +45,19 @@ SLAnnotatorVisitorTest >> testMethodWithAssignment [ self assert: ((expressions select: [ :node | node isConstant and: [ node value = 1 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) name = #x and: [ + (sLNodeAnnotatorVisitor getAssigningParentFor: node) name = #x and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: ((expressions select: [ :node | node isConstant and: [ node value ~= 1 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) isNil and: [ + (sLNodeAnnotatorVisitor getAssigningParentFor: node) isNil and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: ((expressions select: [ :node | node isSend ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) name = #y and: [ + (sLNodeAnnotatorVisitor getAssigningParentFor: node) name = #y and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: (statements allSatisfy: [ :node | @@ -73,8 +73,8 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. @@ -112,8 +112,8 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ tMethod := ccg methodNamed: #methodWithConditionalAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. self assert: statements size equals: 5. @@ -126,13 +126,13 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) name = #x ] ] ]). + (sLNodeAnnotatorVisitor getAssigningParentFor: node) name = #x ] ] ]). self assert: ((expressions reject: [ :node | node isConstant ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: node) isNil ] ] ]). + (sLNodeAnnotatorVisitor getAssigningParentFor: node) isNil ] ] ]). self assert: (statements allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ @@ -147,8 +147,8 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetStatement := self getValueOfConstantNodeInSet: statements. constantSetExpression := self getValueOfConstantNodeInSet: @@ -191,8 +191,8 @@ SLAnnotatorVisitorTest >> testMethodWithReturn [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. @@ -219,8 +219,8 @@ SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. @@ -247,33 +247,38 @@ SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturningConditional [ - | tMethod statements expressions constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithReturningConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. - - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. - self assert: statements size equals: 2. - self assert: expressions size equals: 6. - self assert: (constantSetExpression includes: 2). - self assert: (constantSetExpression includes: 4). - self assert: constantSetExpression size equals: 3. - self assert: ((expressions select: [ :node | + + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. + self assert: nonEffectiveExpressionOrStatementSet size equals: 5. + self assert: effectiveExpressionValueSet size equals: 3. + self assert: (effectiveConstantExpressionValueSet includes: 2). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: effectiveConstantExpressionValueSet size equals: 3. + + self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value isInteger ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). self assert: - ((expressions reject: [ :node | node isConstant ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). - - self assert: (statements allSatisfy: [ :node | + ((effectiveExpressionValueSet reject: [ :node | node isConstant ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + self halt. + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] @@ -286,8 +291,8 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetStatement := self getValueOfConstantNodeInSet: statements. @@ -311,18 +316,18 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ self assert: ((sLNodeAnnotatorVisitor isInAssignment: firstSend) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: firstSend) name = #x and: [ + (sLNodeAnnotatorVisitor getAssigningParentFor: firstSend) name = #x and: [ (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: secondSend) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: secondSend) isNil and: [ + (sLNodeAnnotatorVisitor getAssigningParentFor: secondSend) isNil and: [ (sLNodeAnnotatorVisitor isInReturn: secondSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: thirdSend) and: [ - (sLNodeAnnotatorVisitor getAssignmentForExitVar: thirdSend) name = #y and: [ + (sLNodeAnnotatorVisitor getAssigningParentFor: thirdSend) name = #y and: [ (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) ] @@ -334,8 +339,8 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetExpression := self getValueOfConstantNodeInSet: expressions. @@ -370,8 +375,8 @@ SLAnnotatorVisitorTest >> testMethodWithStatementList [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetStatement := self getValueOfConstantNodeInSet: statements. constantSetExpression := self getValueOfConstantNodeInSet: @@ -412,8 +417,8 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsExpression [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetStatement := self getValueOfConstantNodeInSet: statements. constantSetExpression := self getValueOfConstantNodeInSet: @@ -439,8 +444,8 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsStatement [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor useAsStatementsSet. - expressions := sLNodeAnnotatorVisitor useAsExpressionsSet. + statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. + expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. constantSetStatement := self getValueOfConstantNodeInSet: statements. constantSetExpression := self getValueOfConstantNodeInSet: diff --git a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st index 04d8df62d1a..81828a4e2f9 100644 --- a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st @@ -756,9 +756,18 @@ SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInReturn [ static sqInt methodAInlineReturningInlinedIfTrueIfFalseInReturn(void) { - return ((methodB()) - ? (/* begin methodB */ 2 + 2 /* end methodB */) - : (/* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */)); + if (methodB()) { + /* begin methodB */ + return 2 + 2; + /* end methodB */ + } else { + /* begin methodA */ + 1 + 1; + /* begin methodB */ + return 2 + 2; + /* end methodB */ + /* end methodA */ + } }' ] @@ -783,9 +792,11 @@ methodAInlineReturningInlinedIfTrueInAssignment(void) { sqInt a; - a = ((methodB()) - ? (/* begin methodB */ 2 + 2 /* end methodB */) - : 0); + if (methodB()) { + /* begin methodB */ + a = 2 + 2; + /* end methodB */ + } return 0; }' ] @@ -809,9 +820,12 @@ SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueInReturn [ static sqInt methodAInlineReturningInlinedIfTrueInReturn(void) { - return ((methodB()) - ? (/* begin methodB */ 2 + 2 /* end methodB */) - : 0); + if (methodB()) { + /* begin methodB */ + return 2 + 2; + /* end methodB */ + } + return 0; }' ] diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 1284aa85c18..ff9d98c8e47 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -383,6 +383,7 @@ SLInliner >> inlineSend: aSendNode [ currentMethod addVarsDeclarationsAndLabelsOf: callee except: omittedParameters. + callee hasReturn ifTrue: [ directReturn ifFalse: [ exitLabel := currentMethod unusedLabelForInliningInto: @@ -391,12 +392,18 @@ SLInliner >> inlineSend: aSendNode [ ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. + "little hack to move returns to the lowest part of the ast" + aSendNode parent replaceChild: aSendNode with: callee parseTree. (sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode) ifTrue: [ + callee parseTree parent + replaceChild: callee parseTree + with: aSendNode. + callee parseTree parent: callee. assignDirectReturnAndExitVarBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. - "little hack to move assignment to the lowest part of the ast" + "little hack to move assignments to the lowest part of the ast" aSendNode parent replaceChild: aSendNode with: callee parseTree. (sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode) ifTrue: [ @@ -604,7 +611,7 @@ SLInliner >> setDirectReturnAndExitVar: aSendNode [ | directReturn exitVar | sLNodeAnnotatorVisitor visit: currentMethod parseTree. directReturn := sLNodeAnnotatorVisitor isDirectReturn: aSendNode. - exitVar := (sLNodeAnnotatorVisitor getAssignmentForExitVar: aSendNode) ifNotNil: [ + exitVar := (sLNodeAnnotatorVisitor getAssigningParentFor: aSendNode) ifNotNil: [ :var | var name ]. ^ OrderedCollection new add: directReturn; diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index c0cb15b6be8..cd4dcd3513c 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -2,35 +2,57 @@ Class { #name : 'SLNodeAnnotatorVisitor', #superclass : 'Object', #instVars : [ - 'returnStack', - 'exitVarStack', 'currentMethod', 'info', - 'useAsExpressionStack', 'inExpressionStack', - 'jumpMetStack' + 'searchForAssignValueStack', + 'searchForReturnValueStack', + 'searchForExpressionValueStack', + 'searchForStmtListValue', + 'currentShouldBePartiallyIgnored' ], #category : 'Slang', #package : 'Slang' } +{ #category : 'helpers-annotate' } +SLNodeAnnotatorVisitor >> addReturnInfoFor: aNode [ + "mark the given node has being inside a return expression, it doesn't mean that the node itself is the exit value + true or false on the stack only indicate if the node is the effective value of the returning expression or not" + + | entry assoc | + searchForReturnValueStack isEmpty ifTrue: [ ^ self ]. + + entry := info at: aNode. + assoc := searchForReturnValueStack top. + + "we are just above a jump (or just in need for an expression value in a StmtList) thus the node is an exitValue" + (searchForStmtListValue isNotEmpty and: [ searchForStmtListValue top ]) ifTrue: [ + assoc := true -> assoc value ]. + + entry at: self returnString put: assoc. + searchForReturnValueStack pop. + "no longer the direct return" + searchForReturnValueStack push: false -> assoc value +] + { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> addToExitVar: aNode [ "when an assignment has been met, the effective exitVar is not all of the nodes in it, often it is just a single node (or more if conditionals) the nodes have a link to theirs assignment true or nil on the stack indicate if the node is the effective exitVar" | entry top | - exitVarStack isEmpty ifTrue: [ ^ self ]. + searchForAssignValueStack isEmpty ifTrue: [ ^ self ]. - top := exitVarStack top. + top := searchForAssignValueStack top. entry := info at: aNode. "we are just above a jump thus the node is an exitVar" - (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifTrue: [ + (searchForStmtListValue isNotEmpty and: [ searchForStmtListValue top ]) ifTrue: [ top := true -> top value ]. entry at: self exitVarString put: top. - exitVarStack pop. - exitVarStack push: nil -> top value + searchForAssignValueStack pop. + searchForAssignValueStack push: false -> top value ] { #category : 'helpers-annotate' } @@ -40,27 +62,6 @@ SLNodeAnnotatorVisitor >> addToInExpression: aNode [ (info at: aNode) at: self inExpressionString put: true ] -{ #category : 'helpers-annotate' } -SLNodeAnnotatorVisitor >> addToReturn: aNode [ - "mark the given node has being inside a return expression, it doesn't mean that the node itself is the exit value - true or false on the stack only indicate if the node is the effective value of the returning expression or not" - - | entry assoc | - returnStack isEmpty ifTrue: [ ^ self ]. - - entry := info at: aNode. - assoc := returnStack top. - - "we are just above a jump thus the node is an exitValue" - (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifTrue: [ - assoc := true -> assoc value ]. - - entry at: self returnString put: assoc. - returnStack pop. - "no longer the direct return" - returnStack push: false -> assoc value -] - { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> addToStatementOrExpression: aNode [ @@ -70,8 +71,8 @@ SLNodeAnnotatorVisitor >> addToStatementOrExpression: aNode [ entry at: self expressionString put: true. aNode isAnExpression: true. ^ self ]. - (useAsExpressionStack isNotEmpty and: [ - useAsExpressionStack top = self expressionString ]) + (searchForExpressionValueStack isNotEmpty and: [ + searchForExpressionValueStack top ]) ifTrue: [ entry at: self expressionString put: true. aNode isAnExpression: true ] @@ -80,9 +81,8 @@ SLNodeAnnotatorVisitor >> addToStatementOrExpression: aNode [ aNode isAnExpression: false ]. "default to a statement by default" - useAsExpressionStack isEmpty ifFalse: [ ^ self ]. + searchForExpressionValueStack isEmpty ifFalse: [ ^ self ]. entry at: self expressionString put: false. - aNode isAnExpression: false ] { #category : 'helpers-annotate' } @@ -102,19 +102,19 @@ SLNodeAnnotatorVisitor >> annotateNodeState: aNode canBeAnExpression: aBoolean [ so if we may be looking for an exitValue, the node should be ignored and we can mark it as statement" aBoolean ifFalse: [ - useAsExpressionStack push: self statementString. + searchForExpressionValueStack push: false. self addToStatementOrExpression: aNode. - useAsExpressionStack pop. + searchForExpressionValueStack pop. ^ self ]. - self addToReturn: aNode. + self addReturnInfoFor: aNode. self addToExitVar: aNode. self addToStatementOrExpression: aNode. - (jumpMetStack isNotEmpty and: [ jumpMetStack top ]) ifFalse: [ + (searchForStmtListValue isNotEmpty and: [ searchForStmtListValue top ]) ifFalse: [ ^ self ]. - jumpMetStack pop. - jumpMetStack push: false + searchForStmtListValue pop. + searchForStmtListValue push: false ] { #category : 'cleanup' } @@ -129,32 +129,43 @@ SLNodeAnnotatorVisitor >> currendMethod: aTmethod [ currentMethod := aTmethod ] +{ #category : 'accessing' } +SLNodeAnnotatorVisitor >> effectiveExpressionValueSet [ + + | expressions | + expressions := IdentitySet new. + info associationsDo: [ :assoc | + (assoc value at: self expressionString) ifTrue: [ + expressions add: assoc key ] ]. + ^ expressions +] + { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ info at: aNode ifAbsentPut: [ IdentityDictionary new ] ] -{ #category : 'helpers-constant' } +{ #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> exitVarString [ ^ #exitVar ] -{ #category : 'helpers-constant' } +{ #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> expressionString [ ^ #expression ] { #category : 'accessing' } -SLNodeAnnotatorVisitor >> getAssignmentForExitVar: aNode [ +SLNodeAnnotatorVisitor >> getAssigningParentFor: aNode [ | assignmentNode | assignmentNode := (info at: aNode) at: self exitVarString ifAbsent: [ nil ]. - (assignmentNode isNil or: [ assignmentNode key isNil ]) ifTrue: [ + (assignmentNode isNil or: [ assignmentNode key not ]) ifTrue: [ ^ nil ]. ^ assignmentNode value ] @@ -162,23 +173,23 @@ SLNodeAnnotatorVisitor >> getAssignmentForExitVar: aNode [ { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ - "conditionals are handle like they would be in C, they are not considered as expression like other sends comming from pharo" + "conditionals are handle a bit like they would be in C, they are control-flow node" | returnTop exitVarTop size | - inExpressionStack push: self inExpressionString. + inExpressionStack push: true. size := aSendNode arguments size. "re give the informations for each branch after using it" aSendNode arguments doWithIndex: [ :arg :index | - returnTop := returnStack isEmpty ifFalse: [ returnStack top ]. - exitVarTop := exitVarStack isEmpty ifFalse: [ exitVarStack top ]. + returnTop := searchForReturnValueStack isEmpty ifFalse: [ searchForReturnValueStack top ]. + exitVarTop := searchForAssignValueStack isEmpty ifFalse: [ searchForAssignValueStack top ]. arg accept: self. index ~= size ifTrue: [ returnTop ifNotNil: [ - returnStack pop. - returnStack push: returnTop ]. + searchForReturnValueStack pop. + searchForReturnValueStack push: returnTop ]. exitVarTop ifNotNil: [ - exitVarStack pop. - exitVarStack push: exitVarTop ] ] ]. + searchForAssignValueStack pop. + searchForAssignValueStack push: exitVarTop ] ] ]. "can't be a exitValue, but the information have been taken by the arguments" aSendNode receiver accept: self. @@ -189,7 +200,7 @@ SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ self annotateNodeState: aSendNode canBeAnExpression: false ] -{ #category : 'helpers-constant' } +{ #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> inExpressionString [ "value doesn't actually matter, but the stack need elements" @@ -200,18 +211,32 @@ SLNodeAnnotatorVisitor >> inExpressionString [ SLNodeAnnotatorVisitor >> initialize [ super initialize. - useAsExpressionStack := Stack new. - returnStack := Stack new. - exitVarStack := Stack new. + + "empty, true or false on top depending of if the parent needs an expression as a dependance" + searchForExpressionValueStack := Stack new. + "either empty or have true on top depending if the children are in the expression of one of their parents" inExpressionStack := Stack new. - jumpMetStack := Stack new. + + "empty or true/false -> returningParent on top depending of if the parent has found its return value or not" + searchForReturnValueStack := Stack new. + + "empty or true/false -> assigningParent on top depending of if the parent has found its assigning value or not" + searchForAssignValueStack := Stack new. + + "StmtList throught jump can end up having multiple value point, this stack is re updated when crossing goToNode and is a boolean stack" + searchForStmtListValue := Stack new. + + "when searching for the deepest node that give the expression value, some nodes can be pass through like conditionals and stmtLists or statement (goTos, comments or labels" + currentShouldBePartiallyIgnored := Stack new. + + "collect all the informations gathered for a given node" info := IdentityDictionary new ] { #category : 'testing' } SLNodeAnnotatorVisitor >> isDirectExpression: aNode [ - ^ (self isDirectReturn: aNode) or: [ (self getAssignmentForExitVar: aNode) isNotNil ] + ^ (self isDirectReturn: aNode) or: [ (self getAssigningParentFor: aNode) isNotNil ] ] { #category : 'testing' } @@ -247,17 +272,27 @@ SLNodeAnnotatorVisitor >> isInReturn: aNode [ ifAbsent: [ false ] ] +{ #category : 'accessing' } +SLNodeAnnotatorVisitor >> nonEffectiveExpressionOrStatementSet [ + + | statements | + statements := IdentitySet new. + info associationsDo: [ :assoc | + (assoc value at: self expressionString) ifFalse: [ + statements add: assoc key ] ]. + ^ statements +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated answer if something was done" | assignment exitVar | - exitVar := self getAssignmentForExitVar: aSendNode. + exitVar := self getAssigningParentFor: aSendNode. exitVar ifNil: [ ^ false ]. assignment := exitVar parent. - self haltIf: [ assignment isAssignment not ]. "necessary because of the inlining hack" self cleanInfo. self visit: assignment tMethod parseTree. @@ -280,7 +315,9 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ (self isDirectReturn: aSendNode) ifFalse: [ ^ false ]. returningParent := self returningParentFor: aSendNode. - + "necessary because of the inlining hack" + self cleanInfo. + self visit: returningParent tMethod parseTree. self transformDirectReturnFrom: returningParent. returningParent parent @@ -291,7 +328,7 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ ^ true ] -{ #category : 'helpers-constant' } +{ #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> returnString [ ^ #return @@ -306,7 +343,7 @@ SLNodeAnnotatorVisitor >> returningParentFor: aNode [ ifAbsent: [ nil ] ] -{ #category : 'helpers-constant' } +{ #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> statementString [ ^ #statement @@ -321,7 +358,10 @@ SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ (self isDirectReturn: child) ifTrue: [ child parent replaceChild: child with: child asReturnNode ] - ifFalse: [ self transformDirectReturnFrom: child ] ] + ifFalse: [ + child isReturn ifTrue: [ "already got a return for this branch" + ^ self ]. + self transformDirectReturnFrom: child ] ] ] { #category : 'transformation' } @@ -330,7 +370,7 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode exitVar: exitVar a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" parentNode children do: [ :child | - (self getAssignmentForExitVar: child) == exitVar + (self getAssigningParentFor: child) == exitVar ifTrue: [ child parent replaceChild: child with: (TAssignmentNode new variable: exitVar; @@ -339,37 +379,15 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode exitVar: exitVar ifFalse: [ self transformToAssignmentFrom: child exitVar: exitVar ] ] ] -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> useAsExpressionsSet [ - - | expressions | - expressions := IdentitySet new. - info associationsDo: [ :assoc | - (assoc value at: self expressionString) ifTrue: [ - expressions add: assoc key ] ]. - ^ expressions -] - -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> useAsStatementsSet [ - - | statements | - statements := IdentitySet new. - info associationsDo: [ :assoc | - (assoc value at: self expressionString) ifFalse: [ - statements add: assoc key ] ]. - ^ statements -] - { #category : 'visiting' } SLNodeAnnotatorVisitor >> visit: aNode [ aNode accept: self. - self assert: exitVarStack isEmpty. - self assert: returnStack isEmpty. - self assert: useAsExpressionStack isEmpty. + self assert: searchForAssignValueStack isEmpty. + self assert: searchForReturnValueStack isEmpty. + self assert: searchForExpressionValueStack isEmpty. self assert: inExpressionStack isEmpty. - self assert: jumpMetStack isEmpty + self assert: searchForStmtListValue isEmpty ] { #category : 'visiting' } @@ -377,18 +395,18 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ self annotateNodeState: anAssignmentNode. - useAsExpressionStack push: self expressionString. - inExpressionStack push: self inExpressionString. + searchForExpressionValueStack push: true. + inExpressionStack push: true. - exitVarStack push: true -> anAssignmentNode variable. + searchForAssignValueStack push: true -> anAssignmentNode variable. anAssignmentNode expression accept: self. anAssignmentNode variable accept: self. inExpressionStack pop. - useAsExpressionStack pop. + searchForExpressionValueStack pop. - exitVarStack pop + searchForAssignValueStack pop ] { #category : 'visiting' } @@ -404,14 +422,14 @@ SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ self annotateNodeState: aCaseStmtNode. - useAsExpressionStack push: self expressionString. - inExpressionStack push: self inExpressionString. + searchForExpressionValueStack push: true. + inExpressionStack push: true. aCaseStmtNode expression accept: self. aCaseStmtNode cases do: [ :case | case accept: self ]. inExpressionStack pop. - useAsExpressionStack pop + searchForExpressionValueStack pop ] { #category : 'visiting' } @@ -426,8 +444,8 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ self annotateNodeState: aTGotoNode canBeAnExpression: false. aTGotoNode haveBeenMetInInlining ifTrue: [ ^ self ]. - jumpMetStack pop. - jumpMetStack push: true. + searchForStmtListValue pop. + searchForStmtListValue push: true. aTGotoNode metInInlining ] @@ -450,15 +468,15 @@ SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ self annotateNodeState: aReturnNode. - useAsExpressionStack push: self expressionString. - returnStack push: true -> aReturnNode. - inExpressionStack push: self inExpressionString. + searchForExpressionValueStack push: true. + searchForReturnValueStack push: true -> aReturnNode. + inExpressionStack push: true. aReturnNode expression accept: self. inExpressionStack pop. - useAsExpressionStack pop. - returnStack pop + searchForExpressionValueStack pop. + searchForReturnValueStack pop ] { #category : 'visiting' } @@ -470,14 +488,14 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ self annotateNodeState: aSendNode. - inExpressionStack push: self inExpressionString. - useAsExpressionStack push: self expressionString. + searchForExpressionValueStack push: true. + inExpressionStack push: true. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. - inExpressionStack pop. - useAsExpressionStack pop + searchForExpressionValueStack pop. + inExpressionStack pop ] { #category : 'visiting' } @@ -485,16 +503,16 @@ SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ | stmts needExitValue | stmts := aStatementsListNode statements. - needExitValue := useAsExpressionStack isEmpty + needExitValue := searchForExpressionValueStack isEmpty ifTrue: [ false ] - ifFalse: [ - useAsExpressionStack top = self expressionString ]. - useAsExpressionStack push: self statementString. - jumpMetStack push: needExitValue. + ifFalse: [ searchForExpressionValueStack top ]. + + searchForExpressionValueStack push: false. + searchForStmtListValue push: needExitValue. stmts reverse do: [ :stmt | stmt accept: self ]. - useAsExpressionStack pop. - jumpMetStack pop. + searchForExpressionValueStack pop. + searchForStmtListValue pop. self annotateNodeState: aStatementsListNode canBeAnExpression: false ] @@ -508,20 +526,20 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ caseLabels := case first. caseStatements := case second. caseLabels do: [ :label | - useAsExpressionStack push: self statementString. + searchForExpressionValueStack push: false. label accept: self. - useAsExpressionStack pop. + searchForExpressionValueStack pop. caseStatements accept: self ] ]. - useAsExpressionStack push: self expressionString. - inExpressionStack push: self inExpressionString. + searchForExpressionValueStack push: true. + inExpressionStack push: true. aSwitchStatementNode expression accept: self. aSwitchStatementNode otherwiseOrNil ifNotNil: [ :o | o accept: self ]. inExpressionStack pop. - useAsExpressionStack pop + searchForExpressionValueStack pop ] { #category : 'visiting' } From 8164838a1729eb8c50c2b5eeab855920cd925371 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 23 Jun 2025 16:08:23 +0200 Subject: [PATCH 07/86] refactor + fix a bug with switch --- .../SLAnnotatorVisitorTest.class.st | 494 +++++++++++------- .../SLNodeAnnotatorVisitorTestClass.class.st | 2 +- smalltalksrc/Slang/SLInliner.class.st | 2 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 249 ++++----- smalltalksrc/Slang/TParseNode.class.st | 6 - smalltalksrc/Slang/TSendNode.class.st | 4 +- 6 files changed, 432 insertions(+), 325 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index f71134ae5d3..51379c7895b 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -24,43 +24,48 @@ SLAnnotatorVisitorTest >> getValueOfConstantNodeInSet: aSetOfNode [ { #category : 'assignment' } SLAnnotatorVisitorTest >> testMethodWithAssignment [ - | tMethod statements expressions constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - self assert: statements size equals: 3. - self assert: expressions size equals: 6. + self assert: nonEffectiveExpressionOrStatementSet size equals: 3. + self assert: effectiveExpressionValueSet size equals: 6. - self assert: (constantSetExpression includes: 1). - self assert: (constantSetExpression includes: 2). - self assert: (constantSetExpression includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 1). + self assert: (effectiveConstantExpressionValueSet includes: 2). + self assert: (effectiveConstantExpressionValueSet includes: 3). - self assert: ((expressions select: [ :node | + self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value = 1 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: node) name = #x and: [ - (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x + and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). - self assert: ((expressions select: [ :node | + self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value ~= 1 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). self assert: - ((expressions select: [ :node | node isSend ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: node) name = #y and: [ - (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). + ((effectiveExpressionValueSet select: [ :node | node isSend ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #y + and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). - self assert: (statements allSatisfy: [ :node | + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ]) ] @@ -68,38 +73,47 @@ SLAnnotatorVisitorTest >> testMethodWithAssignment [ { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ - | tMethod statements expressions constantSetExpression constantSetStatement | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet | tMethod := ccg methodNamed: #methodWithBlockReturn. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. - constantSetStatement := self getValueOfConstantNodeInSet: statements. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + nonEffectiveExpressionOrStatementSet. - self assert: statements size equals: 5. - self assert: expressions size equals: 1. + self assert: nonEffectiveExpressionOrStatementSet size equals: 5. + self assert: effectiveExpressionValueSet size equals: 1. - self assert: constantSetExpression size equals: 1. - self assert: (constantSetExpression includes: 4). + self assert: effectiveConstantExpressionValueSet size equals: 1. + self assert: (effectiveConstantExpressionValueSet includes: 4). - self assert: constantSetStatement size equals: 2. - self assert: (constantSetStatement includes: 2). - self assert: (constantSetStatement includes: 3). + self + assert: nonEffectiveExpressionOrStatementConstantSet size + equals: 2. + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 2). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 3). self assert: - ((expressions select: [ :node | node isConstant ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + ((effectiveExpressionValueSet select: [ :node | node isConstant ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). self assert: - ((statements select: [ :node | node isConstant ]) allSatisfy: [ - :node | + ((nonEffectiveExpressionOrStatementSet select: [ :node | + node isConstant ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]) @@ -108,33 +122,41 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ { #category : 'assignment' } SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ - | tMethod statements expressions constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet | tMethod := ccg methodNamed: #methodWithConditionalAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. - - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. - self assert: statements size equals: 5. - self assert: expressions size equals: 4. - self assert: (constantSetExpression includes: 1). - self assert: (constantSetExpression includes: 2). - self assert: constantSetExpression size equals: 3. - self assert: ((expressions select: [ :node | + + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. + self assert: nonEffectiveExpressionOrStatementSet size equals: 5. + self assert: effectiveExpressionValueSet size equals: 4. + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 1). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 2). + self + assert: nonEffectiveExpressionOrStatementConstantSet size + equals: 3. + self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value isInteger ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: node) name = #x ] ] ]). + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x ] ] ]). self assert: - ((expressions reject: [ :node | node isConstant ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: node) isNil ] ] ]). + ((effectiveExpressionValueSet reject: [ :node | node isConstant ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ] ]). - self assert: (statements allSatisfy: [ :node | + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] @@ -142,43 +164,54 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ { #category : 'constant' } SLAnnotatorVisitorTest >> testMethodWithConstant [ - | tMethod statements expressions constantSetExpression constantSetStatement | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet | tMethod := ccg methodNamed: #methodWithConstant. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetStatement := self getValueOfConstantNodeInSet: statements. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + nonEffectiveExpressionOrStatementSet. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - self assert: statements size equals: 10. - self assert: expressions size equals: 2. + self assert: nonEffectiveExpressionOrStatementSet size equals: 10. + self assert: effectiveExpressionValueSet size equals: 2. - self assert: (constantSetStatement includes: 1). - self assert: (constantSetStatement includes: 2). - self assert: (constantSetStatement includes: 3). - self assert: (constantSetStatement includes: 4). - self assert: (constantSetStatement includes: true). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 1). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 2). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 3). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 4). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: true). - self assert: (constantSetExpression includes: 5). - self assert: (constantSetExpression includes: 6). + self assert: (effectiveConstantExpressionValueSet includes: 5). + self assert: (effectiveConstantExpressionValueSet includes: 6). self assert: - ((expressions select: [ :node | node value isInteger ]) allSatisfy: [ - :node | + ((effectiveExpressionValueSet select: [ :node | + node value isInteger ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). self assert: - ((expressions select: [ :node | node value = true ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + ((effectiveExpressionValueSet select: [ :node | node value = true ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). - self assert: (statements allSatisfy: [ :node | + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] @@ -186,27 +219,31 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturn [ - | tMethod statements expressions constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithReturn. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - self assert: statements size equals: 2. - self assert: expressions size equals: 1. + self assert: nonEffectiveExpressionOrStatementSet size equals: 2. + self assert: effectiveExpressionValueSet size equals: 1. - self assert: (constantSetExpression includes: 2). + self assert: (effectiveConstantExpressionValueSet includes: 2). - self assert: (expressions allSatisfy: [ :node | + self assert: (effectiveExpressionValueSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). - self assert: (statements allSatisfy: [ :node | + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] @@ -214,32 +251,36 @@ SLAnnotatorVisitorTest >> testMethodWithReturn [ { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ - | tMethod statements expressions constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithReturnInConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - self assert: statements size equals: 7. - self assert: expressions size equals: 2. + self assert: nonEffectiveExpressionOrStatementSet size equals: 7. + self assert: effectiveExpressionValueSet size equals: 2. - self assert: (constantSetExpression includes: 2). - self assert: (constantSetExpression includes: 4). - self assert: constantSetExpression size equals: 2. + self assert: (effectiveConstantExpressionValueSet includes: 2). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: effectiveConstantExpressionValueSet size equals: 2. - self assert: ((expressions select: [ :node | + self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value isInteger ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). - self assert: (statements allSatisfy: [ :node | + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] @@ -276,93 +317,116 @@ SLAnnotatorVisitorTest >> testMethodWithReturningConditional [ (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). - self halt. + + self assert: + ((nonEffectiveExpressionOrStatementSet select: [ :node | + node isStatementList and: [ node children first isConstant ] ]) + allSatisfy: [ :node | sLNodeAnnotatorVisitor isInReturn: node ]). + + self assert: + ((nonEffectiveExpressionOrStatementSet select: [ :node | + node isStatementList and: [ node children first isConstant not ] ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not ]). + + self assert: + ((nonEffectiveExpressionOrStatementSet select: [ :node | + node isSend ]) allSatisfy: [ :node | + sLNodeAnnotatorVisitor isInReturn: node ]). + self assert: (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: node) not ]) ] { #category : 'send' } SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ - | tMethod statements expressions sendStatementSet sendExpressionSet constantSetStatement firstSend secondSend thirdSend | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet firstSend secondSend thirdSend | tMethod := ccg methodNamed: #methodWithSendAsExpression. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetStatement := self getValueOfConstantNodeInSet: statements. - sendStatementSet := self getSendNodeInSet: statements. - sendExpressionSet := self getSendNodeInSet: expressions. + nonEffectiveExpressionOrStatementSendSet := self getSendNodeInSet: + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSendSet := self getSendNodeInSet: + effectiveExpressionValueSet. firstSend := ((tMethod parseTree statements at: 1) statements at: 1) expression. secondSend := firstSend arguments at: 1. thirdSend := (tMethod parseTree statements at: 2) expression. - self assert: statements size equals: 4. - self assert: expressions size equals: 8. + self assert: nonEffectiveExpressionOrStatementSet size equals: 4. + self assert: effectiveExpressionValueSet size equals: 8. - self assert: sendStatementSet isEmpty. + self assert: nonEffectiveExpressionOrStatementSendSet isEmpty. - self assert: sendExpressionSet size equals: 3. - self assert: (sendExpressionSet includes: firstSend). - self assert: (sendExpressionSet includes: secondSend). - self assert: (sendExpressionSet includes: thirdSend). + self assert: effectiveExpressionValueSendSet size equals: 3. + self assert: (effectiveExpressionValueSendSet includes: firstSend). + self assert: (effectiveExpressionValueSendSet includes: secondSend). + self assert: (effectiveExpressionValueSendSet includes: thirdSend). self assert: ((sLNodeAnnotatorVisitor isInAssignment: firstSend) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: firstSend) name = #x and: [ - (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). + (sLNodeAnnotatorVisitor getAssigningVariableFor: firstSend) name + = #x and: [ (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: secondSend) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: secondSend) isNil and: [ - (sLNodeAnnotatorVisitor isInReturn: secondSend) not ] ]). + (sLNodeAnnotatorVisitor getAssigningVariableFor: secondSend) isNil + and: [ (sLNodeAnnotatorVisitor isInReturn: secondSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: thirdSend) and: [ - (sLNodeAnnotatorVisitor getAssigningParentFor: thirdSend) name = #y and: [ - (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) + (sLNodeAnnotatorVisitor getAssigningVariableFor: thirdSend) name + = #y and: [ (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) ] { #category : 'send' } SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ - | tMethod statements expressions constantSetExpression sendStatementSet sendExpressionSet constantSetStatement | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet | tMethod := ccg methodNamed: #methodWithSendAsStatement. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. - constantSetStatement := self getValueOfConstantNodeInSet: statements. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - sendStatementSet := self getSendNodeInSet: statements. - sendExpressionSet := self getSendNodeInSet: expressions. + nonEffectiveExpressionOrStatementSendSet := self getSendNodeInSet: + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSendSet := self getSendNodeInSet: + effectiveExpressionValueSet. - self assert: statements size equals: 4. - self assert: expressions size equals: 3. + self assert: nonEffectiveExpressionOrStatementSet size equals: 4. + self assert: effectiveExpressionValueSet size equals: 3. - self assert: (constantSetExpression includes: 1). + self assert: (effectiveConstantExpressionValueSet includes: 1). - self assert: sendStatementSet size equals: 2. - self assert: (sendStatementSet includes: + self assert: nonEffectiveExpressionOrStatementSendSet size equals: 2. + self assert: (nonEffectiveExpressionOrStatementSendSet includes: ((tMethod parseTree statements at: 1) statements at: 1)). - self assert: - (sendStatementSet includes: (tMethod parseTree statements at: 2)). + self assert: (nonEffectiveExpressionOrStatementSendSet includes: + (tMethod parseTree statements at: 2)). - self assert: sendExpressionSet isEmpty. + self assert: effectiveExpressionValueSendSet isEmpty. - self assert: (sendStatementSet allSatisfy: [ :node | + self assert: + (nonEffectiveExpressionOrStatementSendSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] @@ -370,94 +434,138 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ { #category : 'statement-list' } SLAnnotatorVisitorTest >> testMethodWithStatementList [ - | tMethod statements expressions constantSetStatement constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet constant11 | tMethod := ccg methodNamed: #methodWithStatementList. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. - - constantSetStatement := self getValueOfConstantNodeInSet: statements. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - self assert: statements size equals: 9. - self assert: expressions size equals: 2. + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + nonEffectiveExpressionOrStatementSet. - self assert: (constantSetStatement includes: 5). - self assert: (constantSetStatement includes: 6). - self assert: (constantSetStatement includes: 7). - self assert: (constantSetStatement includes: 8). - self assert: (constantSetStatement includes: 9). - self assert: (constantSetStatement includes: 10). + self assert: nonEffectiveExpressionOrStatementSet size equals: 10. + self assert: effectiveExpressionValueSet size equals: 1. - self assert: (constantSetExpression includes: 11). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 5). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 6). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 7). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 8). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 9). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 10). - self assert: ((statements select: [ :node | + self assert: + ((nonEffectiveExpressionOrStatementSet select: [ :node | node isConstant and: [ node value = 10 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). - self assert: ((statements select: [ :node | + self assert: + ((nonEffectiveExpressionOrStatementSet select: [ :node | node isConstant and: [ node value ~= 10 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). - self assert: (expressions allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + + constant11 := effectiveExpressionValueSet asArray first. + self assert: constant11 value equals: 11. + self assert: ((sLNodeAnnotatorVisitor isInReturn: constant11) and: [ + (sLNodeAnnotatorVisitor isDirectReturn: constant11) and: [ + (sLNodeAnnotatorVisitor isInAssignment: constant11) not ] ]) ] { #category : 'switch' } -SLAnnotatorVisitorTest >> testMethodWithSwitchAsExpression [ +SLAnnotatorVisitorTest >> testMethodWithSwitchAsAssignmentExpression [ - | tMethod statements expressions constantSetStatement constantSetExpression | - tMethod := ccg methodNamed: #methodWithSwitchAsExpression. + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | + tMethod := ccg methodNamed: #methodWithSwitchAsAssignmentExpression. tMethod prepareMethodIn: ccg. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. + + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + nonEffectiveExpressionOrStatementSet. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - constantSetStatement := self getValueOfConstantNodeInSet: statements. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + self assert: nonEffectiveExpressionOrStatementSet size equals: 7. + self assert: effectiveExpressionValueSet size equals: 4. - self assert: statements size equals: 4. - self assert: expressions size equals: 7. + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 4). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 5). - self assert: (constantSetStatement includes: 4). - self assert: (constantSetStatement includes: 5). + self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 6). + self assert: (effectiveConstantExpressionValueSet includes: 7). - self assert: (constantSetExpression includes: 3). - self assert: (constantSetExpression includes: 6). - self assert: (constantSetExpression includes: 7) + self assert: ((effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x ]). + self assert: ((effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ]) ] { #category : 'switch' } SLAnnotatorVisitorTest >> testMethodWithSwitchAsStatement [ - | tMethod statements expressions constantSetStatement constantSetExpression | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithSwitchAsStatement. tMethod prepareMethodIn: ccg. sLNodeAnnotatorVisitor visit: tMethod parseTree. - statements := sLNodeAnnotatorVisitor nonEffectiveExpressionOrStatementSet. - expressions := sLNodeAnnotatorVisitor effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. - constantSetStatement := self getValueOfConstantNodeInSet: statements. - constantSetExpression := self getValueOfConstantNodeInSet: - expressions. + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + nonEffectiveExpressionOrStatementSet. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. - self assert: statements size equals: 8. - self assert: expressions size equals: 1. + self assert: nonEffectiveExpressionOrStatementSet size equals: 8. + self assert: effectiveExpressionValueSet size equals: 1. + + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 4). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 5). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 6). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 7). - self assert: (constantSetStatement includes: 4). - self assert: (constantSetStatement includes: 5). - self assert: (constantSetStatement includes: 6). - self assert: (constantSetStatement includes: 7). + self assert: (effectiveConstantExpressionValueSet includes: 3). - self assert: (constantSetExpression includes: 3) + self assert: ((effectiveExpressionValueSet + addAll: nonEffectiveExpressionOrStatementSet; + yourself) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ] ] ]) ] diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index 449734a4a93..736da11eda3 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -99,7 +99,7 @@ SLNodeAnnotatorVisitorTestClass >> methodWithStatementList [ ] { #category : 'switch' } -SLNodeAnnotatorVisitorTestClass >> methodWithSwitchAsExpression [ +SLNodeAnnotatorVisitorTestClass >> methodWithSwitchAsAssignmentExpression [ | x | x := 3 caseOf: { diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index ff9d98c8e47..49c4a2b0441 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -611,7 +611,7 @@ SLInliner >> setDirectReturnAndExitVar: aSendNode [ | directReturn exitVar | sLNodeAnnotatorVisitor visit: currentMethod parseTree. directReturn := sLNodeAnnotatorVisitor isDirectReturn: aSendNode. - exitVar := (sLNodeAnnotatorVisitor getAssigningParentFor: aSendNode) ifNotNil: [ + exitVar := (sLNodeAnnotatorVisitor getAssigningVariableFor: aSendNode) ifNotNil: [ :var | var name ]. ^ OrderedCollection new add: directReturn; diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index cd4dcd3513c..64fad47cfcd 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -2,7 +2,6 @@ Class { #name : 'SLNodeAnnotatorVisitor', #superclass : 'Object', #instVars : [ - 'currentMethod', 'info', 'inExpressionStack', 'searchForAssignValueStack', @@ -15,44 +14,41 @@ Class { #package : 'Slang' } +{ #category : 'helpers-annotate' } +SLNodeAnnotatorVisitor >> addAssignmentInfoFor: aNode [ + "when an assignment has been met, the effective exitVar is not all of the nodes in it, often it is just a single node (or more if conditionals) the nodes have a link to theirs assignment + true or nil on the stack indicate if the node is the effective exitVar" + + self + processValueSourceFor: aNode + from: searchForAssignValueStack + constantString: self exitVarString +] + { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> addReturnInfoFor: aNode [ "mark the given node has being inside a return expression, it doesn't mean that the node itself is the exit value true or false on the stack only indicate if the node is the effective value of the returning expression or not" - | entry assoc | - searchForReturnValueStack isEmpty ifTrue: [ ^ self ]. - - entry := info at: aNode. - assoc := searchForReturnValueStack top. - - "we are just above a jump (or just in need for an expression value in a StmtList) thus the node is an exitValue" - (searchForStmtListValue isNotEmpty and: [ searchForStmtListValue top ]) ifTrue: [ - assoc := true -> assoc value ]. - - entry at: self returnString put: assoc. - searchForReturnValueStack pop. - "no longer the direct return" - searchForReturnValueStack push: false -> assoc value + self + processValueSourceFor: aNode + from: searchForReturnValueStack + constantString: self returnString ] { #category : 'helpers-annotate' } -SLNodeAnnotatorVisitor >> addToExitVar: aNode [ - "when an assignment has been met, the effective exitVar is not all of the nodes in it, often it is just a single node (or more if conditionals) the nodes have a link to theirs assignment - true or nil on the stack indicate if the node is the effective exitVar" +SLNodeAnnotatorVisitor >> addToEffectiveExpressionOrNot: aNode [ - | entry top | - searchForAssignValueStack isEmpty ifTrue: [ ^ self ]. - - top := searchForAssignValueStack top. + | entry isAnEffectiveExpression | entry := info at: aNode. - "we are just above a jump thus the node is an exitVar" - (searchForStmtListValue isNotEmpty and: [ searchForStmtListValue top ]) ifTrue: [ - top := true -> top value ]. + isAnEffectiveExpression := false. + + ((self topOfBooleanStack: currentShouldBePartiallyIgnored) not and: [ + (self topOfBooleanStack: searchForExpressionValueStack) or: [ + self isEffectiveExpression: aNode ] ]) ifTrue: [ + isAnEffectiveExpression := true ]. - entry at: self exitVarString put: top. - searchForAssignValueStack pop. - searchForAssignValueStack push: false -> top value + entry at: self expressionString put: isAnEffectiveExpression ] { #category : 'helpers-annotate' } @@ -62,57 +58,21 @@ SLNodeAnnotatorVisitor >> addToInExpression: aNode [ (info at: aNode) at: self inExpressionString put: true ] -{ #category : 'helpers-annotate' } -SLNodeAnnotatorVisitor >> addToStatementOrExpression: aNode [ - - | entry | - entry := info at: aNode. - (self isDirectExpression: aNode) ifTrue: [ - entry at: self expressionString put: true. - aNode isAnExpression: true. - ^ self ]. - (searchForExpressionValueStack isNotEmpty and: [ - searchForExpressionValueStack top ]) - ifTrue: [ - entry at: self expressionString put: true. - aNode isAnExpression: true ] - ifFalse: [ - entry at: self expressionString put: false. - aNode isAnExpression: false ]. - - "default to a statement by default" - searchForExpressionValueStack isEmpty ifFalse: [ ^ self ]. - entry at: self expressionString put: false. -] - { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> annotateNodeState: aNode [ - self annotateNodeState: aNode canBeAnExpression: true -] - -{ #category : 'helpers-annotate' } -SLNodeAnnotatorVisitor >> annotateNodeState: aNode canBeAnExpression: aBoolean [ - "annotate the node by assuming the ast exploration ensure that the different exitValue are the first to reach this method for each branch" - self ensureEntryInInfoDictFor: aNode. - self addToInExpression: aNode. - "comment, label and goTo cannot be an expression, in C as in Pharo - so if we may be looking for an exitValue, the node should be ignored - and we can mark it as statement" - aBoolean ifFalse: [ - searchForExpressionValueStack push: false. - self addToStatementOrExpression: aNode. - searchForExpressionValueStack pop. - ^ self ]. + self addToInExpression: aNode. self addReturnInfoFor: aNode. - self addToExitVar: aNode. - self addToStatementOrExpression: aNode. + self addAssignmentInfoFor: aNode. + self addToEffectiveExpressionOrNot: aNode. - (searchForStmtListValue isNotEmpty and: [ searchForStmtListValue top ]) ifFalse: [ + (self topOfBooleanStack: currentShouldBePartiallyIgnored) ifTrue: [ + currentShouldBePartiallyIgnored pop. ^ self ]. + (self topOfBooleanStack: searchForStmtListValue) ifFalse: [ ^ self ]. searchForStmtListValue pop. searchForStmtListValue push: false ] @@ -123,12 +83,6 @@ SLNodeAnnotatorVisitor >> cleanInfo [ self initialize ] -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> currendMethod: aTmethod [ - - currentMethod := aTmethod -] - { #category : 'accessing' } SLNodeAnnotatorVisitor >> effectiveExpressionValueSet [ @@ -159,7 +113,7 @@ SLNodeAnnotatorVisitor >> expressionString [ ] { #category : 'accessing' } -SLNodeAnnotatorVisitor >> getAssigningParentFor: aNode [ +SLNodeAnnotatorVisitor >> getAssigningVariableFor: aNode [ | assignmentNode | assignmentNode := (info at: aNode) @@ -170,34 +124,37 @@ SLNodeAnnotatorVisitor >> getAssigningParentFor: aNode [ ^ assignmentNode value ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ + "re give the informations for each branch after using it" + + | tops size | + tops := OrderedCollection new. + size := aCollectionOfNode size. + aCollectionOfNode doWithIndex: [ :arg :index | + self stacksForSpecificValueHolder do: [ :stack | + stack isEmpty ifFalse: [ tops add: stack top -> stack ] ]. + arg accept: self. + index ~= size ifTrue: [ + tops do: [ :assoc | + assoc value pop. + assoc value push: assoc key ] ] ] +] + { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ - "conditionals are handle a bit like they would be in C, they are control-flow node" - | returnTop exitVarTop size | + inExpressionStack push: true. - size := aSendNode arguments size. - "re give the informations for each branch after using it" - aSendNode arguments doWithIndex: [ :arg :index | - returnTop := searchForReturnValueStack isEmpty ifFalse: [ searchForReturnValueStack top ]. - exitVarTop := searchForAssignValueStack isEmpty ifFalse: [ searchForAssignValueStack top ]. - arg accept: self. - index ~= size ifTrue: [ - returnTop ifNotNil: [ - searchForReturnValueStack pop. - searchForReturnValueStack push: returnTop ]. - exitVarTop ifNotNil: [ - searchForAssignValueStack pop. - searchForAssignValueStack push: exitVarTop ] ] ]. - - "can't be a exitValue, but the information have been taken by the arguments" + self giveStacksInfoForMultipleBranches: aSendNode arguments. + aSendNode receiver accept: self. inExpressionStack pop. - - "the effective values are not the result of this message" - self annotateNodeState: aSendNode canBeAnExpression: false + + currentShouldBePartiallyIgnored push: true. + self annotateNodeState: aSendNode ] { #category : 'helpers-string-constant' } @@ -226,19 +183,14 @@ SLNodeAnnotatorVisitor >> initialize [ "StmtList throught jump can end up having multiple value point, this stack is re updated when crossing goToNode and is a boolean stack" searchForStmtListValue := Stack new. - "when searching for the deepest node that give the expression value, some nodes can be pass through like conditionals and stmtLists or statement (goTos, comments or labels" + "when searching for the deepest node that give the expression value, some nodes can be pass through like conditionals and stmtLists or statement only node (goTos, comments or labels) + inactivate the search... stack" currentShouldBePartiallyIgnored := Stack new. "collect all the informations gathered for a given node" info := IdentityDictionary new ] -{ #category : 'testing' } -SLNodeAnnotatorVisitor >> isDirectExpression: aNode [ - - ^ (self isDirectReturn: aNode) or: [ (self getAssigningParentFor: aNode) isNotNil ] -] - { #category : 'testing' } SLNodeAnnotatorVisitor >> isDirectReturn: aNode [ @@ -248,6 +200,12 @@ SLNodeAnnotatorVisitor >> isDirectReturn: aNode [ ifAbsent: [ false ] ] +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> isEffectiveExpression: aNode [ + + ^ (self isDirectReturn: aNode) or: [ (self getAssigningVariableFor: aNode) isNotNil ] +] + { #category : 'testing' } SLNodeAnnotatorVisitor >> isInAssignment: aNode [ @@ -283,13 +241,34 @@ SLNodeAnnotatorVisitor >> nonEffectiveExpressionOrStatementSet [ ^ statements ] +{ #category : 'helpers-annotate' } +SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantString: aString [ + + | entry assoc | + aStack isEmpty ifTrue: [ ^ self ]. + + assoc := aStack top copy. + entry := info at: aNode. + + (self topOfBooleanStack: searchForStmtListValue) ifTrue: [ + assoc := true -> assoc value ]. + + (self topOfBooleanStack: currentShouldBePartiallyIgnored) + ifTrue: [ assoc := false -> assoc value ] + ifFalse: [ + aStack pop. + aStack push: false -> assoc value ]. + + entry at: aString put: assoc +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated answer if something was done" | assignment exitVar | - exitVar := self getAssigningParentFor: aSendNode. + exitVar := self getAssigningVariableFor: aSendNode. exitVar ifNil: [ ^ false ]. assignment := exitVar parent. @@ -343,12 +322,27 @@ SLNodeAnnotatorVisitor >> returningParentFor: aNode [ ifAbsent: [ nil ] ] +{ #category : 'accessing' } +SLNodeAnnotatorVisitor >> stacksForSpecificValueHolder [ + + ^ { + searchForAssignValueStack. + searchForReturnValueStack } +] + { #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> statementString [ ^ #statement ] +{ #category : 'helpers' } +SLNodeAnnotatorVisitor >> topOfBooleanStack: aStack [ + "return the top value of a boolean stack" + + ^ aStack isNotEmpty and: [ aStack top ] +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ "recursively go throught the ast to transfrom directReturn @@ -370,7 +364,7 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode exitVar: exitVar a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" parentNode children do: [ :child | - (self getAssigningParentFor: child) == exitVar + (self getAssigningVariableFor: child) == exitVar ifTrue: [ child parent replaceChild: child with: (TAssignmentNode new variable: exitVar; @@ -420,13 +414,14 @@ SLNodeAnnotatorVisitor >> visitBraceCaseNode: aTBraceCaseNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ + currentShouldBePartiallyIgnored push: true. self annotateNodeState: aCaseStmtNode. searchForExpressionValueStack push: true. inExpressionStack push: true. + self giveStacksInfoForMultipleBranches: aCaseStmtNode cases. aCaseStmtNode expression accept: self. - aCaseStmtNode cases do: [ :case | case accept: self ]. inExpressionStack pop. searchForExpressionValueStack pop @@ -441,7 +436,8 @@ SLNodeAnnotatorVisitor >> visitConstantNode: aConstantNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ - self annotateNodeState: aTGotoNode canBeAnExpression: false. + currentShouldBePartiallyIgnored push: true. + self annotateNodeState: aTGotoNode. aTGotoNode haveBeenMetInInlining ifTrue: [ ^ self ]. searchForStmtListValue pop. @@ -460,7 +456,8 @@ SLNodeAnnotatorVisitor >> visitInlineNode: anInlineNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitLabeledCommentNode: aLabeledCommentNode [ - self annotateNodeState: aLabeledCommentNode canBeAnExpression: false + currentShouldBePartiallyIgnored push: true. + self annotateNodeState: aLabeledCommentNode ] { #category : 'visiting' } @@ -503,9 +500,8 @@ SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ | stmts needExitValue | stmts := aStatementsListNode statements. - needExitValue := searchForExpressionValueStack isEmpty - ifTrue: [ false ] - ifFalse: [ searchForExpressionValueStack top ]. + needExitValue := self topOfBooleanStack: + searchForExpressionValueStack. searchForExpressionValueStack push: false. searchForStmtListValue push: needExitValue. @@ -513,30 +509,41 @@ SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ searchForExpressionValueStack pop. searchForStmtListValue pop. - self annotateNodeState: aStatementsListNode canBeAnExpression: false + + currentShouldBePartiallyIgnored push: true. + self annotateNodeState: aStatementsListNode ] { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ + | caseStatementsAndOtherWiseOrNilCollection | + currentShouldBePartiallyIgnored push: true. self annotateNodeState: aSwitchStatementNode. + caseStatementsAndOtherWiseOrNilCollection := OrderedCollection new. + caseStatementsAndOtherWiseOrNilCollection := aSwitchStatementNode + cases collect: [ :case | + case second ]. + aSwitchStatementNode otherwiseOrNil ifNotNil: [ :o | + caseStatementsAndOtherWiseOrNilCollection add: o ]. + + searchForExpressionValueStack push: true. + inExpressionStack push: true. + self giveStacksInfoForMultipleBranches: + caseStatementsAndOtherWiseOrNilCollection. + aSwitchStatementNode cases do: [ :case | - | caseLabels caseStatements | + | caseLabels | caseLabels := case first. - caseStatements := case second. + caseLabels do: [ :label | searchForExpressionValueStack push: false. label accept: self. - searchForExpressionValueStack pop. - - caseStatements accept: self ] ]. + searchForExpressionValueStack pop ] ]. - searchForExpressionValueStack push: true. - inExpressionStack push: true. aSwitchStatementNode expression accept: self. - aSwitchStatementNode otherwiseOrNil ifNotNil: [ :o | o accept: self ]. inExpressionStack pop. searchForExpressionValueStack pop diff --git a/smalltalksrc/Slang/TParseNode.class.st b/smalltalksrc/Slang/TParseNode.class.st index 4089ec64f6f..c3691662660 100644 --- a/smalltalksrc/Slang/TParseNode.class.st +++ b/smalltalksrc/Slang/TParseNode.class.st @@ -221,12 +221,6 @@ TParseNode >> isAnExpression [ ^ isExpression ] -{ #category : 'accessing' } -TParseNode >> isAnExpression: aBoolean [ - - isExpression := aBoolean -] - { #category : 'testing' } TParseNode >> isAssertion [ ^false diff --git a/smalltalksrc/Slang/TSendNode.class.st b/smalltalksrc/Slang/TSendNode.class.st index a41b896c59e..7ea4d1a71fb 100644 --- a/smalltalksrc/Slang/TSendNode.class.st +++ b/smalltalksrc/Slang/TSendNode.class.st @@ -194,9 +194,7 @@ TSendNode >> asTransformedConstantPerform [ receiver: receiver arguments: arguments allButFirst isBuiltInOp: isBuiltinOperator ]. - replacement ifNotNil: [ :node | - node isAnExpression: isExpression. - node parent: parent ]. + replacement ifNotNil: [ :node | node parent: parent ]. ^ replacement ] From 741b7a24ea2a9916dc73f6d6731ec8886e098a1b Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 23 Jun 2025 17:04:18 +0200 Subject: [PATCH 08/86] fin des tests (sans mutation testing) --- .../SLAnnotatorVisitorTest.class.st | 113 +++++++++++++++--- .../SLNodeAnnotatorVisitorTestClass.class.st | 8 ++ 2 files changed, 106 insertions(+), 15 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 51379c7895b..2265539e727 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -122,7 +122,7 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ { #category : 'assignment' } SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet | + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet firstStmtList | tMethod := ccg methodNamed: #methodWithConditionalAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -130,18 +130,16 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ nonEffectiveExpressionOrStatementSet. effectiveExpressionValueSet := sLNodeAnnotatorVisitor effectiveExpressionValueSet. - nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. self assert: nonEffectiveExpressionOrStatementSet size equals: 5. self assert: effectiveExpressionValueSet size equals: 4. - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 1). - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 2). - self - assert: nonEffectiveExpressionOrStatementConstantSet size - equals: 3. + + self assert: (effectiveConstantExpressionValueSet includes: 1). + self assert: (effectiveConstantExpressionValueSet includes: 2). + self assert: effectiveConstantExpressionValueSet size equals: 3. + self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value isInteger ] ]) allSatisfy: [ :node | @@ -155,10 +153,20 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ] ]). + firstStmtList := tMethod parseTree. self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + ((nonEffectiveExpressionOrStatementSet select: [ :node | + node isAssignment not and: [ node ~= firstStmtList ] ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + sLNodeAnnotatorVisitor isInAssignment: node ] ]). + + self assert: + ((nonEffectiveExpressionOrStatementSet reject: [ :node | + node isAssignment not and: [ node ~= firstStmtList ] ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) ] { #category : 'constant' } @@ -523,7 +531,82 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsAssignmentExpression [ (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x ]). self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ]) + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ]). + + self assert: + (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ]). + + self assert: ((effectiveExpressionValueSet + addAll: nonEffectiveExpressionOrStatementSet; + yourself) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]) +] + +{ #category : 'switch' } +SLAnnotatorVisitorTest >> testMethodWithSwitchAsReturnExpression [ + + | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet firstStmtList | + tMethod := ccg methodNamed: #methodWithSwitchAsReturnExpression. + tMethod prepareMethodIn: ccg. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor + nonEffectiveExpressionOrStatementSet. + effectiveExpressionValueSet := sLNodeAnnotatorVisitor + effectiveExpressionValueSet. + + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeInSet: + nonEffectiveExpressionOrStatementSet. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeInSet: + effectiveExpressionValueSet. + + self assert: nonEffectiveExpressionOrStatementSet size equals: 7. + self assert: effectiveExpressionValueSet size equals: 3. + + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 4). + self assert: + (nonEffectiveExpressionOrStatementConstantSet includes: 5). + + self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 6). + self assert: (effectiveConstantExpressionValueSet includes: 7). + + self assert: ((effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + sLNodeAnnotatorVisitor isDirectReturn: node ] ]). + self assert: ((effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]). + + firstStmtList := tMethod parseTree. + + self assert: + ((nonEffectiveExpressionOrStatementSet select: [ :node | + node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ + :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]). + + self assert: + ((nonEffectiveExpressionOrStatementSet reject: [ :node | + node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ + :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]). + + self assert: ((effectiveExpressionValueSet + addAll: nonEffectiveExpressionOrStatementSet; + yourself) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ]) ] { #category : 'switch' } diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index 736da11eda3..ca2c1d5fac7 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -107,6 +107,14 @@ SLNodeAnnotatorVisitorTestClass >> methodWithSwitchAsAssignmentExpression [ ([ 5 ] -> [ 7 ]) } ] +{ #category : 'switch' } +SLNodeAnnotatorVisitorTestClass >> methodWithSwitchAsReturnExpression [ + + ^ 3 caseOf: { + ([ 4 ] -> [ 6 ]). + ([ 5 ] -> [ 7 ]) } +] + { #category : 'switch' } SLNodeAnnotatorVisitorTestClass >> methodWithSwitchAsStatement [ From 18eeabfc7004f6813506256b8575a3ae0fd0d450 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 24 Jun 2025 18:39:05 +0200 Subject: [PATCH 09/86] refactor start of better tests --- .../SLAnnotatorVisitorTest.class.st | 392 ++++++++++++------ smalltalksrc/Slang/SLInliner.class.st | 4 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 129 +++--- 3 files changed, 336 insertions(+), 189 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 2265539e727..1f29fe844fd 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -2,7 +2,9 @@ Class { #name : 'SLAnnotatorVisitorTest', #superclass : 'SlangAbstractTestCase', #instVars : [ - 'sLNodeAnnotatorVisitor' + 'sLNodeAnnotatorVisitor', + 'nonEffectiveExpressionOrStatementSet', + 'effectiveExpressionValueSet' ], #category : 'Slang-Tests', #package : 'Slang-Tests' @@ -24,7 +26,7 @@ SLAnnotatorVisitorTest >> getValueOfConstantNodeInSet: aSetOfNode [ { #category : 'assignment' } SLAnnotatorVisitorTest >> testMethodWithAssignment [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | + | tMethod effectiveConstantExpressionValueSet constant1 constant2And3 send2Add3 assignmentNodes assignmentNodeX assignmentNodeY | tMethod := ccg methodNamed: #methodWithAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -44,36 +46,56 @@ SLAnnotatorVisitorTest >> testMethodWithAssignment [ self assert: (effectiveConstantExpressionValueSet includes: 1). self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: effectiveConstantExpressionValueSet size equals: 3. - self assert: ((effectiveExpressionValueSet select: [ :node | - node isConstant and: [ node value = 1 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x - and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). - - self assert: ((effectiveExpressionValueSet select: [ :node | - node isConstant and: [ node value ~= 1 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil and: [ - (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). - - self assert: - ((effectiveExpressionValueSet select: [ :node | node isSend ]) - allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #y - and: [ (sLNodeAnnotatorVisitor isInReturn: node) not ] ] ]). + assignmentNodes := nonEffectiveExpressionOrStatementSet select: [ + :node | node isAssignment ]. + assignmentNodeX := (assignmentNodes select: [ :node | + node variable name = #x ]) asArray first. + assignmentNodeY := (assignmentNodes select: [ :node | + node variable name = #y ]) asArray first. + + constant1 := (effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value = 1 ] ]) asArray + first. + self assert: ((sLNodeAnnotatorVisitor assigningParentFor: constant1) + = assignmentNodeX and: [ + sLNodeAnnotatorVisitor isEffectiveAssignmentValue: constant1 ]). + + constant2And3 := effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value ~= 1 ] ]. + self assert: (constant2And3 allSatisfy: [ :node | + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assignmentNodeY and: [ + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ] ]). + + send2Add3 := (effectiveExpressionValueSet select: [ :node | + node isSend ]) asArray first. + self assert: + ((sLNodeAnnotatorVisitor isInAssignment: send2Add3) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: send2Add3) + = assignmentNodeY and: [ + sLNodeAnnotatorVisitor isEffectiveAssignmentValue: send2Add3 ] ]). + + effectiveExpressionValueSet do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + sLNodeAnnotatorVisitor isInExpression: node ] ]) ]. self assert: (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor isInReturn: node) not ] ]) + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not + and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ] ] ]) ] { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet | + | tMethod nonEffectiveExpressionOrStatementConstantSet constant4 returnNode constant2And3 firstStmtList blockNode | tMethod := ccg methodNamed: #methodWithBlockReturn. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -83,9 +105,6 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ effectiveExpressionValueSet := sLNodeAnnotatorVisitor effectiveExpressionValueSet. - effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. nonEffectiveExpressionOrStatementConstantSet := self getValueOfConstantNodeInSet: nonEffectiveExpressionOrStatementSet. @@ -93,9 +112,6 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ self assert: nonEffectiveExpressionOrStatementSet size equals: 5. self assert: effectiveExpressionValueSet size equals: 1. - self assert: effectiveConstantExpressionValueSet size equals: 1. - self assert: (effectiveConstantExpressionValueSet includes: 4). - self assert: nonEffectiveExpressionOrStatementConstantSet size equals: 2. @@ -104,25 +120,45 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 3). - self assert: - ((effectiveExpressionValueSet select: [ :node | node isConstant ]) - allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + returnNode := (nonEffectiveExpressionOrStatementSet select: [ :node | + node isReturn ]) asArray first. + constant4 := effectiveExpressionValueSet asArray first. + self assert: constant4 value equals: 4. + self assert: ((sLNodeAnnotatorVisitor isInReturn: constant4) and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constant4) and: [ + (sLNodeAnnotatorVisitor isInAssignment: constant4) not and: [ + (sLNodeAnnotatorVisitor isInExpression: constant4) and: [ + (sLNodeAnnotatorVisitor returningParentFor: constant4) + = returnNode ] ] ] ]). - self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | - node isConstant ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]) + firstStmtList := tMethod parseTree. + { + firstStmtList. + returnNode } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + + constant2And3 := nonEffectiveExpressionOrStatementSet select: [ :node | + node isConstant ]. + blockNode := nonEffectiveExpressionOrStatementSet select: [ :node | + node isStatementList and: [ node ~= firstStmtList ] ]. + constant2And3 addAll: blockNode. + constant2And3 do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. + + nonEffectiveExpressionOrStatementSet do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not ]) ] ] { #category : 'assignment' } SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet firstStmtList | + | tMethod effectiveConstantExpressionValueSet firstStmtList assignmentNode constant1And2 variableX | tMethod := ccg methodNamed: #methodWithConditionalAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -133,46 +169,62 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ effectiveConstantExpressionValueSet := self getValueOfConstantNodeInSet: effectiveExpressionValueSet. + self assert: nonEffectiveExpressionOrStatementSet size equals: 5. self assert: effectiveExpressionValueSet size equals: 4. self assert: (effectiveConstantExpressionValueSet includes: 1). self assert: (effectiveConstantExpressionValueSet includes: 2). + self assert: (effectiveConstantExpressionValueSet includes: true). self assert: effectiveConstantExpressionValueSet size equals: 3. - self assert: ((effectiveExpressionValueSet select: [ :node | - node isConstant and: [ node value isInteger ] ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x ] ] ]). + assignmentNode := (nonEffectiveExpressionOrStatementSet select: [ + :node | node isAssignment ]) asArray first. + + constant1And2 := effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value isInteger ] ]. + self assert: (constant1And2 allSatisfy: [ :node | + sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node ]). + + variableX := (effectiveExpressionValueSet reject: [ :node | + node isConstant ]) asArray first. self assert: - ((effectiveExpressionValueSet reject: [ :node | node isConstant ]) - allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: variableX) not. + + effectiveExpressionValueSet do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInExpression: node) and: [ (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ] ]). + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assignmentNode ] ] ]) ]. firstStmtList := tMethod parseTree. - self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | - node isAssignment not and: [ node ~= firstStmtList ] ]) - allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - sLNodeAnnotatorVisitor isInAssignment: node ] ]). self assert: - ((nonEffectiveExpressionOrStatementSet reject: [ :node | + ((nonEffectiveExpressionOrStatementSet select: [ :node | node isAssignment not and: [ node ~= firstStmtList ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assignmentNode and: [ + sLNodeAnnotatorVisitor isInExpression: node ] ] ]). + { + firstStmtList. + assignmentNode } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not ] ]) ]. + + nonEffectiveExpressionOrStatementSet do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ]) ] ] { #category : 'constant' } SLAnnotatorVisitorTest >> testMethodWithConstant [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet | + | tMethod effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet constant5And6 constantTrue returns return5 return6 stmtListOfIfTrueAndConstant4AndAndReturn5 | tMethod := ccg methodNamed: #methodWithConstant. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -189,8 +241,8 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ getValueOfConstantNodeInSet: effectiveExpressionValueSet. - self assert: nonEffectiveExpressionOrStatementSet size equals: 10. - self assert: effectiveExpressionValueSet size equals: 2. + self assert: nonEffectiveExpressionOrStatementSet size equals: 9. + self assert: effectiveExpressionValueSet size equals: 3. self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 1). @@ -200,34 +252,72 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ (nonEffectiveExpressionOrStatementConstantSet includes: 3). self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 4). - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: true). + self assert: (effectiveConstantExpressionValueSet includes: true). self assert: (effectiveConstantExpressionValueSet includes: 5). self assert: (effectiveConstantExpressionValueSet includes: 6). - self assert: - ((effectiveExpressionValueSet select: [ :node | - node value isInteger ]) allSatisfy: [ :node | + constant5And6 := (effectiveExpressionValueSet select: [ :node | + node value isInteger ]) asArray. + self assert: (constant5And6 allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + sLNodeAnnotatorVisitor isInExpression: node ] ] ] ]). + + returns := nonEffectiveExpressionOrStatementSet select: [ :node | + node isReturn ]. + return5 := (returns select: [ :node | node expression value = 5 ]) + asArray first. + return6 := (returns select: [ :node | node expression value = 6 ]) + asArray first. + + self + assert: (sLNodeAnnotatorVisitor returningParentFor: + (constant5And6 select: [ :node | node value = 5 ]) first) + equals: return5. + self + assert: (sLNodeAnnotatorVisitor returningParentFor: + (constant5And6 select: [ :node | node value = 6 ]) first) + equals: return6. + constantTrue := (effectiveExpressionValueSet select: [ :node | + node value = true ]) asArray first. self assert: - ((effectiveExpressionValueSet select: [ :node | node value = true ]) - allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + ((sLNodeAnnotatorVisitor isInReturn: constantTrue) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: constantTrue) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constantTrue) not + and: [ + (sLNodeAnnotatorVisitor returningParentFor: constantTrue) isNil + and: [ sLNodeAnnotatorVisitor isInExpression: constantTrue ] ] ] ]). self assert: (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]). + + "ifTrue body" + stmtListOfIfTrueAndConstant4AndAndReturn5 := (nonEffectiveExpressionOrStatementSet + select: [ :node | + node isSend ]) asArray + first arguments first + asOrderedCollection. + stmtListOfIfTrueAndConstant4AndAndReturn5 first statements do: [ + :node | stmtListOfIfTrueAndConstant4AndAndReturn5 add: node ]. + stmtListOfIfTrueAndConstant4AndAndReturn5 do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInExpression: node) ]. + + (nonEffectiveExpressionOrStatementSet reject: [ :node | + stmtListOfIfTrueAndConstant4AndAndReturn5 includes: node ]) do: [ + :node | + self assert: (sLNodeAnnotatorVisitor isInExpression: node) not ] ] { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturn [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | + | tMethod constant2 returnNode | tMethod := ccg methodNamed: #methodWithReturn. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -237,29 +327,36 @@ SLAnnotatorVisitorTest >> testMethodWithReturn [ effectiveExpressionValueSet := sLNodeAnnotatorVisitor effectiveExpressionValueSet. - effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. self assert: nonEffectiveExpressionOrStatementSet size equals: 2. self assert: effectiveExpressionValueSet size equals: 1. - self assert: (effectiveConstantExpressionValueSet includes: 2). + constant2 := effectiveExpressionValueSet asArray first. + self assert: constant2 value equals: 2. + + returnNode := (nonEffectiveExpressionOrStatementSet select: [ :node | + node isReturn ]) asArray first. + self assert: ((sLNodeAnnotatorVisitor isInReturn: constant2) and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ + (sLNodeAnnotatorVisitor returningParentFor: constant2) + = returnNode and: [ + (sLNodeAnnotatorVisitor isInAssignment: constant2) not and: [ + sLNodeAnnotatorVisitor isInExpression: constant2 ] ] ] ]). - self assert: (effectiveExpressionValueSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). self assert: (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not ] ] ] ] ]) ] { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | + | tMethod effectiveConstantExpressionValueSet constant2And4 returnNodes return2 return4 ifTrueIfFalseNode firstStmtList | tMethod := ccg methodNamed: #methodWithReturnInConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -273,30 +370,62 @@ SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ getValueOfConstantNodeInSet: effectiveExpressionValueSet. - self assert: nonEffectiveExpressionOrStatementSet size equals: 7. - self assert: effectiveExpressionValueSet size equals: 2. + self assert: nonEffectiveExpressionOrStatementSet size equals: 6. + self assert: effectiveExpressionValueSet size equals: 3. self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: 4). - self assert: effectiveConstantExpressionValueSet size equals: 2. + self assert: (effectiveConstantExpressionValueSet includes: true). - self assert: ((effectiveExpressionValueSet select: [ :node | - node isConstant and: [ node value isInteger ] ]) allSatisfy: [ - :node | + constant2And4 := effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value isInteger ] ]. + self assert: (constant2And4 allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + returnNodes := nonEffectiveExpressionOrStatementSet select: [ :node | + node isReturn ]. + return2 := (returnNodes select: [ :node | node expression value = 2 ]) + asArray first. + return4 := (returnNodes select: [ :node | node expression value = 4 ]) + asArray first. + + self + assert: (sLNodeAnnotatorVisitor returningParentFor: + (effectiveExpressionValueSet select: [ :node | node value = 2 ]) + asArray first) + equals: return2. + self + assert: (sLNodeAnnotatorVisitor returningParentFor: + (effectiveExpressionValueSet select: [ :node | node value = 4 ]) + asArray first) + equals: return4. + self assert: (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ] ] ]). + + ifTrueIfFalseNode := (nonEffectiveExpressionOrStatementSet select: [ + :node | node isSend ]) asArray first. + firstStmtList := tMethod parseTree. + { + firstStmtList. + ifTrueIfFalseNode } do: [ :node | + self assert: + (sLNodeAnnotatorVisitor isInExpression: ifTrueIfFalseNode) not ]. + (nonEffectiveExpressionOrStatementSet reject: [ :node | + node = firstStmtList or: [ node = ifTrueIfFalseNode ] ]) do: [ + :node | (sLNodeAnnotatorVisitor isInExpression: node) not ] ] { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturningConditional [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet | + | tMethod effectiveConstantExpressionValueSet constant2And4 returnNode constantTrue | tMethod := ccg methodNamed: #methodWithReturningConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -307,24 +436,29 @@ SLAnnotatorVisitorTest >> testMethodWithReturningConditional [ effectiveConstantExpressionValueSet := self getValueOfConstantNodeInSet: effectiveExpressionValueSet. + self assert: nonEffectiveExpressionOrStatementSet size equals: 5. self assert: effectiveExpressionValueSet size equals: 3. self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: 4). - self assert: effectiveConstantExpressionValueSet size equals: 3. + self assert: (effectiveConstantExpressionValueSet includes: true). - self assert: ((effectiveExpressionValueSet select: [ :node | - node isConstant and: [ node value isInteger ] ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + returnNode := (nonEffectiveExpressionOrStatementSet select: [ :node | + node isReturn ]) asArray first. + constant2And4 := effectiveExpressionValueSet select: [ :node | + node isConstant and: [ node value isInteger ] ]. + self assert: (constant2And4 allSatisfy: [ :node | + sLNodeAnnotatorVisitor isEffectiveReturnValue: node ]). + + constantTrue := (effectiveExpressionValueSet select: [ :node | + node value = true ]) asArray first. self assert: - ((effectiveExpressionValueSet reject: [ :node | node isConstant ]) - allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constantTrue) not. + + self assert: (effectiveExpressionValueSet allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode + and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). self assert: ((nonEffectiveExpressionOrStatementSet select: [ :node | @@ -350,7 +484,7 @@ SLAnnotatorVisitorTest >> testMethodWithReturningConditional [ { #category : 'send' } SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet firstSend secondSend thirdSend | + | tMethod nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet firstSend secondSend thirdSend | tMethod := ccg methodNamed: #methodWithSendAsExpression. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -383,25 +517,25 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ self assert: ((sLNodeAnnotatorVisitor isInAssignment: firstSend) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: firstSend) name - = #x and: [ (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). + (sLNodeAnnotatorVisitor assigningParentFor: firstSend) name = #x + and: [ (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: secondSend) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: secondSend) isNil + (sLNodeAnnotatorVisitor assigningParentFor: secondSend) isNil and: [ (sLNodeAnnotatorVisitor isInReturn: secondSend) not ] ]). self assert: ((sLNodeAnnotatorVisitor isInAssignment: thirdSend) and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: thirdSend) name - = #y and: [ (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) + (sLNodeAnnotatorVisitor assigningParentFor: thirdSend) name = #y + and: [ (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) ] { #category : 'send' } SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet | + | tMethod effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet | tMethod := ccg methodNamed: #methodWithSendAsStatement. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -442,7 +576,7 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ { #category : 'statement-list' } SLAnnotatorVisitorTest >> testMethodWithStatementList [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet constant11 | + | tMethod nonEffectiveExpressionOrStatementConstantSet constant11 | tMethod := ccg methodNamed: #methodWithStatementList. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -476,27 +610,27 @@ SLAnnotatorVisitorTest >> testMethodWithStatementList [ ((nonEffectiveExpressionOrStatementSet select: [ :node | node isConstant and: [ node value = 10 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). self assert: ((nonEffectiveExpressionOrStatementSet select: [ :node | node isConstant and: [ node value ~= 10 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). constant11 := effectiveExpressionValueSet asArray first. self assert: constant11 value equals: 11. self assert: ((sLNodeAnnotatorVisitor isInReturn: constant11) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: constant11) and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constant11) and: [ (sLNodeAnnotatorVisitor isInAssignment: constant11) not ] ]) ] { #category : 'switch' } SLAnnotatorVisitorTest >> testMethodWithSwitchAsAssignmentExpression [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | + | tMethod nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithSwitchAsAssignmentExpression. tMethod prepareMethodIn: ccg. @@ -528,26 +662,26 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsAssignmentExpression [ self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) name = #x ]). + (sLNodeAnnotatorVisitor assigningParentFor: node) name = #x ]). self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ]). + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ]). self assert: (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ]). + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ]). self assert: ((effectiveExpressionValueSet addAll: nonEffectiveExpressionOrStatementSet; yourself) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]) + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]) ] { #category : 'switch' } SLAnnotatorVisitorTest >> testMethodWithSwitchAsReturnExpression [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet firstStmtList | + | tMethod nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet firstStmtList | tMethod := ccg methodNamed: #methodWithSwitchAsReturnExpression. tMethod prepareMethodIn: ccg. @@ -580,11 +714,11 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsReturnExpression [ self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - sLNodeAnnotatorVisitor isDirectReturn: node ] ]). + sLNodeAnnotatorVisitor isEffectiveReturnValue: node ] ]). self assert: ((effectiveExpressionValueSet select: [ :node | node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]). + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]). firstStmtList := tMethod parseTree. @@ -593,26 +727,26 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsReturnExpression [ node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]). + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]). self assert: ((nonEffectiveExpressionOrStatementSet reject: [ :node | node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not ] ]). + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]). self assert: ((effectiveExpressionValueSet addAll: nonEffectiveExpressionOrStatementSet; yourself) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ]) + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ]) ] { #category : 'switch' } SLAnnotatorVisitorTest >> testMethodWithSwitchAsStatement [ - | tMethod nonEffectiveExpressionOrStatementSet effectiveExpressionValueSet nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | + | tMethod nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | tMethod := ccg methodNamed: #methodWithSwitchAsStatement. tMethod prepareMethodIn: ccg. @@ -649,6 +783,6 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsStatement [ yourself) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor isDirectReturn: node) not and: [ - (sLNodeAnnotatorVisitor getAssigningVariableFor: node) isNil ] ] ] ]) + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ] ]) ] diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 49c4a2b0441..aa779f9c93c 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -610,8 +610,8 @@ SLInliner >> setDirectReturnAndExitVar: aSendNode [ | directReturn exitVar | sLNodeAnnotatorVisitor visit: currentMethod parseTree. - directReturn := sLNodeAnnotatorVisitor isDirectReturn: aSendNode. - exitVar := (sLNodeAnnotatorVisitor getAssigningVariableFor: aSendNode) ifNotNil: [ + directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: aSendNode. + exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) ifNotNil: [ :var | var name ]. ^ OrderedCollection new add: directReturn; diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 64fad47cfcd..4e8c4dfd663 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -22,7 +22,7 @@ SLNodeAnnotatorVisitor >> addAssignmentInfoFor: aNode [ self processValueSourceFor: aNode from: searchForAssignValueStack - constantString: self exitVarString + constantString: self assignmentString ] { #category : 'helpers-annotate' } @@ -77,6 +77,45 @@ SLNodeAnnotatorVisitor >> annotateNodeState: aNode [ searchForStmtListValue push: false ] +{ #category : 'accessing' } +SLNodeAnnotatorVisitor >> assigningParentFor: aNode [ + + ^ self associationValueInfoFor: aNode entry: self assignmentString +] + +{ #category : 'helpers-string-constant' } +SLNodeAnnotatorVisitor >> assignmentString [ + + ^ #exitVar +] + +{ #category : 'helpers' } +SLNodeAnnotatorVisitor >> associationKeyInfoFor: aNode entry: aString [ + + ^ (info at: aNode) + at: aString + ifPresent: [ :assoc | assoc key ] + ifAbsent: [ false ] +] + +{ #category : 'helpers' } +SLNodeAnnotatorVisitor >> associationValueInfoFor: aNode entry: aString [ + + ^ (info at: aNode) + at: aString + ifPresent: [ :assoc | assoc value ] + ifAbsent: [ nil ] +] + +{ #category : 'helpers' } +SLNodeAnnotatorVisitor >> booleanInfoFor: aNode entry: aString [ + + ^ (info at: aNode) + at: aString + ifPresent: [ true ] + ifAbsent: [ false ] +] + { #category : 'cleanup' } SLNodeAnnotatorVisitor >> cleanInfo [ @@ -100,30 +139,12 @@ SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ info at: aNode ifAbsentPut: [ IdentityDictionary new ] ] -{ #category : 'helpers-string-constant' } -SLNodeAnnotatorVisitor >> exitVarString [ - - ^ #exitVar -] - { #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> expressionString [ ^ #expression ] -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> getAssigningVariableFor: aNode [ - - | assignmentNode | - assignmentNode := (info at: aNode) - at: self exitVarString - ifAbsent: [ nil ]. - (assignmentNode isNil or: [ assignmentNode key not ]) ifTrue: [ - ^ nil ]. - ^ assignmentNode value -] - { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ "re give the informations for each branch after using it" @@ -143,18 +164,20 @@ SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ - "conditionals are handle a bit like they would be in C, they are control-flow node" + "conditionals are control-flow node and thus treated differently from the other sends" + + currentShouldBePartiallyIgnored push: true. + self annotateNodeState: aSendNode. inExpressionStack push: true. self giveStacksInfoForMultipleBranches: aSendNode arguments. + searchForExpressionValueStack push: true. aSendNode receiver accept: self. + searchForExpressionValueStack pop. - inExpressionStack pop. - - currentShouldBePartiallyIgnored push: true. - self annotateNodeState: aSendNode + inExpressionStack pop ] { #category : 'helpers-string-constant' } @@ -192,42 +215,41 @@ SLNodeAnnotatorVisitor >> initialize [ ] { #category : 'testing' } -SLNodeAnnotatorVisitor >> isDirectReturn: aNode [ +SLNodeAnnotatorVisitor >> isEffectiveAssignmentValue: aNode [ - ^ (info at: aNode) - at: self returnString - ifPresent: [ :assoc | assoc key ] - ifAbsent: [ false ] + ^ self associationKeyInfoFor: aNode entry: self assignmentString ] { #category : 'testing' } SLNodeAnnotatorVisitor >> isEffectiveExpression: aNode [ - ^ (self isDirectReturn: aNode) or: [ (self getAssigningVariableFor: aNode) isNotNil ] + ^ (self isEffectiveReturnValue: aNode) or: [ + (self assigningParentFor: aNode) isNotNil or: [ + self booleanInfoFor: aNode entry: self expressionString ] ] ] { #category : 'testing' } -SLNodeAnnotatorVisitor >> isInAssignment: aNode [ - - ^ (info at: aNode) at: self exitVarString ifPresent: [ true ] ifAbsent: [ false ] +SLNodeAnnotatorVisitor >> isEffectiveReturnValue: aNode [ + + ^ self associationKeyInfoFor: aNode entry: self returnString +] + +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> isInAssignment: aNode [ + + ^ self booleanInfoFor: aNode entry: self assignmentString ] { #category : 'testing' } SLNodeAnnotatorVisitor >> isInExpression: aNode [ - ^ (info at: aNode) - at: self inExpressionString - ifPresent: [ true ] - ifAbsent: [ false ] + ^ self booleanInfoFor: aNode entry: self inExpressionString ] { #category : 'testing' } SLNodeAnnotatorVisitor >> isInReturn: aNode [ - ^ (info at: aNode) - at: self returnString - ifPresent: [ true ] - ifAbsent: [ false ] + ^ self booleanInfoFor: aNode entry: self returnString ] { #category : 'accessing' } @@ -268,7 +290,7 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ answer if something was done" | assignment exitVar | - exitVar := self getAssigningVariableFor: aSendNode. + exitVar := self assigningParentFor: aSendNode. exitVar ifNil: [ ^ false ]. assignment := exitVar parent. @@ -291,7 +313,7 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ answer if anything was done" | returningParent | - (self isDirectReturn: aSendNode) ifFalse: [ ^ false ]. + (self isEffectiveReturnValue: aSendNode) ifFalse: [ ^ false ]. returningParent := self returningParentFor: aSendNode. "necessary because of the inlining hack" @@ -315,11 +337,8 @@ SLNodeAnnotatorVisitor >> returnString [ { #category : 'accessing' } SLNodeAnnotatorVisitor >> returningParentFor: aNode [ - "if the node is in a return give the returningParent even if the node is not the exitvalue itself" - ^ (info at: aNode) - at: self returnString - ifPresent: [ :assoc | assoc value ] - ifAbsent: [ nil ] + + ^ self associationValueInfoFor: aNode entry: self returnString ] { #category : 'accessing' } @@ -330,12 +349,6 @@ SLNodeAnnotatorVisitor >> stacksForSpecificValueHolder [ searchForReturnValueStack } ] -{ #category : 'helpers-string-constant' } -SLNodeAnnotatorVisitor >> statementString [ - - ^ #statement -] - { #category : 'helpers' } SLNodeAnnotatorVisitor >> topOfBooleanStack: aStack [ "return the top value of a boolean stack" @@ -349,7 +362,7 @@ SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ ^ exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [^exp] ifFalse [^exp]" parentNode children do: [ :child | - (self isDirectReturn: child) + (self isEffectiveReturnValue: child) ifTrue: [ child parent replaceChild: child with: child asReturnNode ] ifFalse: [ @@ -364,7 +377,7 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode exitVar: exitVar a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" parentNode children do: [ :child | - (self getAssigningVariableFor: child) == exitVar + (self assigningParentFor: child) == exitVar ifTrue: [ child parent replaceChild: child with: (TAssignmentNode new variable: exitVar; @@ -392,7 +405,7 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ searchForExpressionValueStack push: true. inExpressionStack push: true. - searchForAssignValueStack push: true -> anAssignmentNode variable. + searchForAssignValueStack push: true -> anAssignmentNode. anAssignmentNode expression accept: self. anAssignmentNode variable accept: self. @@ -405,7 +418,7 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitBraceCaseNode: aTBraceCaseNode [ - "shouldn't be reachable in real condition, TBraceCaseNode are translated to switch before going throught any operation. + "shouldn't be reachable in real condition, TBraceCaseNode are translated to switch before going through any operation. it can be reach throught tests as a side effect since inlining goes for all knowned methods in the codeGenerator even if they are not the tests focuss" From 30144a9ada554267be6e99f85a57a1e86984557e Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 25 Jun 2025 15:49:27 +0200 Subject: [PATCH 10/86] better tests fix some bugs --- .../SLAnnotatorVisitorTest.class.st | 672 ++++++++++-------- smalltalksrc/Slang/SLInliner.class.st | 7 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 63 +- .../SLAnnotatorVisitorTest.extension.st | 5 +- 4 files changed, 401 insertions(+), 346 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 1f29fe844fd..956d8b6f220 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -3,24 +3,48 @@ Class { #superclass : 'SlangAbstractTestCase', #instVars : [ 'sLNodeAnnotatorVisitor', - 'nonEffectiveExpressionOrStatementSet', - 'effectiveExpressionValueSet' + 'nonEffectiveExpressionOrStatementCollection', + 'effectiveExpressionValueCollection' ], #category : 'Slang-Tests', #package : 'Slang-Tests' } { #category : 'helper' } -SLAnnotatorVisitorTest >> getSendNodeInSet: aSetOfNode [ +SLAnnotatorVisitorTest >> checkEffectiveExpressionValueCollection [ - ^ aSetOfNode select: [ :node | node isSend ] + ^ effectiveExpressionValueCollection allSatisfy: [ :node | + sLNodeAnnotatorVisitor isInExpression: node ] ] { #category : 'helper' } -SLAnnotatorVisitorTest >> getValueOfConstantNodeInSet: aSetOfNode [ +SLAnnotatorVisitorTest >> checkNonEffectiveExpressionOrStatementCollection [ - ^ (aSetOfNode select: [ :node | node isConstant ]) collect: [ :node | - node value ] + ^ nonEffectiveExpressionOrStatementCollection allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ] ] +] + +{ #category : 'helper' } +SLAnnotatorVisitorTest >> fillCollectionValue: aTMethod [ + + aTMethod parseTree nodesDo: [ :node | + (sLNodeAnnotatorVisitor isEffectiveExpression: node) + ifTrue: [ effectiveExpressionValueCollection add: node ] + ifFalse: [ nonEffectiveExpressionOrStatementCollection add: node ] ] +] + +{ #category : 'helper' } +SLAnnotatorVisitorTest >> getSendNodeIn: aCollectionOfNode [ + + ^ aCollectionOfNode select: [ :node | node isSend ] +] + +{ #category : 'helper' } +SLAnnotatorVisitorTest >> getValueOfConstantNodeIn: aCollectionOfNode [ + + ^ (aCollectionOfNode select: [ :node | node isConstant ]) collect: [ + :node | node value ] ] { #category : 'assignment' } @@ -31,65 +55,63 @@ SLAnnotatorVisitorTest >> testMethodWithAssignment [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 3. - self assert: effectiveExpressionValueSet size equals: 6. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 3. + self assert: effectiveExpressionValueCollection size equals: 6. self assert: (effectiveConstantExpressionValueSet includes: 1). self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: 3). self assert: effectiveConstantExpressionValueSet size equals: 3. - assignmentNodes := nonEffectiveExpressionOrStatementSet select: [ - :node | node isAssignment ]. + assignmentNodes := nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]. assignmentNodeX := (assignmentNodes select: [ :node | - node variable name = #x ]) asArray first. + node variable name = #x ]) first. assignmentNodeY := (assignmentNodes select: [ :node | - node variable name = #y ]) asArray first. + node variable name = #y ]) first. - constant1 := (effectiveExpressionValueSet select: [ :node | - node isConstant and: [ node value = 1 ] ]) asArray - first. + constant1 := (effectiveExpressionValueCollection select: [ :node | + node isConstant and: [ node value = 1 ] ]) first. self assert: ((sLNodeAnnotatorVisitor assigningParentFor: constant1) = assignmentNodeX and: [ sLNodeAnnotatorVisitor isEffectiveAssignmentValue: constant1 ]). - constant2And3 := effectiveExpressionValueSet select: [ :node | + constant2And3 := effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value ~= 1 ] ]. self assert: (constant2And3 allSatisfy: [ :node | (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNodeY and: [ (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ] ]). - send2Add3 := (effectiveExpressionValueSet select: [ :node | - node isSend ]) asArray first. + send2Add3 := (effectiveExpressionValueCollection select: [ :node | + node isSend ]) first. self assert: ((sLNodeAnnotatorVisitor isInAssignment: send2Add3) and: [ (sLNodeAnnotatorVisitor assigningParentFor: send2Add3) = assignmentNodeY and: [ sLNodeAnnotatorVisitor isEffectiveAssignmentValue: send2Add3 ] ]). - effectiveExpressionValueSet do: [ :node | + effectiveExpressionValueCollection do: [ :node | self assert: ((sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - sLNodeAnnotatorVisitor isInExpression: node ] ]) ]. + (sLNodeAnnotatorVisitor isInReturn: node) not ]) ]. self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | + (nonEffectiveExpressionOrStatementCollection allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInExpression: node) not and: [ - (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not - and: [ - (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ] ] ]) + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ] ]). + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'return' } @@ -100,17 +122,16 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - nonEffectiveExpressionOrStatementSet. + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 5. - self assert: effectiveExpressionValueSet size equals: 1. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 5. + self assert: effectiveExpressionValueCollection size equals: 1. self assert: nonEffectiveExpressionOrStatementConstantSet size @@ -120,16 +141,15 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 3). - returnNode := (nonEffectiveExpressionOrStatementSet select: [ :node | - node isReturn ]) asArray first. - constant4 := effectiveExpressionValueSet asArray first. + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. + constant4 := effectiveExpressionValueCollection first. self assert: constant4 value equals: 4. self assert: ((sLNodeAnnotatorVisitor isInReturn: constant4) and: [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: constant4) and: [ (sLNodeAnnotatorVisitor isInAssignment: constant4) not and: [ - (sLNodeAnnotatorVisitor isInExpression: constant4) and: [ - (sLNodeAnnotatorVisitor returningParentFor: constant4) - = returnNode ] ] ] ]). + (sLNodeAnnotatorVisitor returningParentFor: constant4) + = returnNode ] ] ]). firstStmtList := tMethod parseTree. { @@ -139,9 +159,10 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ (sLNodeAnnotatorVisitor isInExpression: node) not and: [ (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. - constant2And3 := nonEffectiveExpressionOrStatementSet select: [ :node | - node isConstant ]. - blockNode := nonEffectiveExpressionOrStatementSet select: [ :node | + constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isConstant ]. + blockNode := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isStatementList and: [ node ~= firstStmtList ] ]. constant2And3 addAll: blockNode. constant2And3 do: [ :node | @@ -149,10 +170,11 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ (sLNodeAnnotatorVisitor isInExpression: node) and: [ (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. - nonEffectiveExpressionOrStatementSet do: [ :node | - self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ]) ] + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'assignment' } @@ -162,46 +184,45 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ tMethod := ccg methodNamed: #methodWithConditionalAssignment. sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. + effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 5. - self assert: effectiveExpressionValueSet size equals: 4. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 5. + self assert: effectiveExpressionValueCollection size equals: 4. self assert: (effectiveConstantExpressionValueSet includes: 1). self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: true). self assert: effectiveConstantExpressionValueSet size equals: 3. - assignmentNode := (nonEffectiveExpressionOrStatementSet select: [ - :node | node isAssignment ]) asArray first. + assignmentNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]) first. - constant1And2 := effectiveExpressionValueSet select: [ :node | + constant1And2 := effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value isInteger ] ]. self assert: (constant1And2 allSatisfy: [ :node | sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node ]). - variableX := (effectiveExpressionValueSet reject: [ :node | - node isConstant ]) asArray first. + variableX := (effectiveExpressionValueCollection reject: [ :node | + node isConstant ]) first. self assert: (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: variableX) not. - effectiveExpressionValueSet do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInExpression: node) and: [ - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) and: [ - (sLNodeAnnotatorVisitor assigningParentFor: node) - = assignmentNode ] ] ]) ]. + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assignmentNode ] ]) ]. firstStmtList := tMethod parseTree. self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | + ((nonEffectiveExpressionOrStatementCollection select: [ :node | node isAssignment not and: [ node ~= firstStmtList ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) and: [ @@ -216,9 +237,11 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ (sLNodeAnnotatorVisitor assigningParentFor: node) isNil and: [ (sLNodeAnnotatorVisitor isInExpression: node) not ] ]) ]. - nonEffectiveExpressionOrStatementSet do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ]) ] + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInReturn: node) not ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'constant' } @@ -229,20 +252,19 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - nonEffectiveExpressionOrStatementSet. + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 9. - self assert: effectiveExpressionValueSet size equals: 3. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 9. + self assert: effectiveExpressionValueCollection size equals: 3. self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 1). @@ -257,20 +279,18 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ self assert: (effectiveConstantExpressionValueSet includes: 5). self assert: (effectiveConstantExpressionValueSet includes: 6). - constant5And6 := (effectiveExpressionValueSet select: [ :node | - node value isInteger ]) asArray. + constant5And6 := effectiveExpressionValueCollection select: [ :node | + node value isInteger ]. self assert: (constant5And6 allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - sLNodeAnnotatorVisitor isInExpression: node ] ] ] ]). + sLNodeAnnotatorVisitor isEffectiveReturnValue: node ] ]). - returns := nonEffectiveExpressionOrStatementSet select: [ :node | - node isReturn ]. + returns := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]. return5 := (returns select: [ :node | node expression value = 5 ]) - asArray first. + first. return6 := (returns select: [ :node | node expression value = 6 ]) - asArray first. + first. self assert: (sLNodeAnnotatorVisitor returningParentFor: @@ -281,37 +301,41 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ (constant5And6 select: [ :node | node value = 6 ]) first) equals: return6. - constantTrue := (effectiveExpressionValueSet select: [ :node | - node value = true ]) asArray first. + constantTrue := (effectiveExpressionValueCollection select: [ :node | + node value = true ]) first. self assert: ((sLNodeAnnotatorVisitor isInReturn: constantTrue) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: constantTrue) not and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: constantTrue) not - and: [ - (sLNodeAnnotatorVisitor returningParentFor: constantTrue) isNil - and: [ sLNodeAnnotatorVisitor isInExpression: constantTrue ] ] ] ]). + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constantTrue) not + and: [ + (sLNodeAnnotatorVisitor returningParentFor: constantTrue) isNil ] ]). + + effectiveExpressionValueCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | + (nonEffectiveExpressionOrStatementCollection allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]). "ifTrue body" - stmtListOfIfTrueAndConstant4AndAndReturn5 := (nonEffectiveExpressionOrStatementSet + stmtListOfIfTrueAndConstant4AndAndReturn5 := (nonEffectiveExpressionOrStatementCollection select: [ :node | - node isSend ]) asArray - first arguments first + node isSend ]) first + arguments first asOrderedCollection. stmtListOfIfTrueAndConstant4AndAndReturn5 first statements do: [ :node | stmtListOfIfTrueAndConstant4AndAndReturn5 add: node ]. stmtListOfIfTrueAndConstant4AndAndReturn5 do: [ :node | self assert: (sLNodeAnnotatorVisitor isInExpression: node) ]. - (nonEffectiveExpressionOrStatementSet reject: [ :node | + (nonEffectiveExpressionOrStatementCollection reject: [ :node | stmtListOfIfTrueAndConstant4AndAndReturn5 includes: node ]) do: [ :node | - self assert: (sLNodeAnnotatorVisitor isInExpression: node) not ] + self assert: (sLNodeAnnotatorVisitor isInExpression: node) not ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'return' } @@ -322,191 +346,208 @@ SLAnnotatorVisitorTest >> testMethodWithReturn [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. - + self fillCollectionValue: tMethod. - self assert: nonEffectiveExpressionOrStatementSet size equals: 2. - self assert: effectiveExpressionValueSet size equals: 1. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 2. + self assert: effectiveExpressionValueCollection size equals: 1. - constant2 := effectiveExpressionValueSet asArray first. + constant2 := effectiveExpressionValueCollection first. self assert: constant2 value equals: 2. - returnNode := (nonEffectiveExpressionOrStatementSet select: [ :node | - node isReturn ]) asArray first. + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. self assert: ((sLNodeAnnotatorVisitor isInReturn: constant2) and: [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ (sLNodeAnnotatorVisitor returningParentFor: constant2) = returnNode and: [ - (sLNodeAnnotatorVisitor isInAssignment: constant2) not and: [ - sLNodeAnnotatorVisitor isInExpression: constant2 ] ] ] ]). + (sLNodeAnnotatorVisitor isInAssignment: constant2) not ] ] ]). self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | + (nonEffectiveExpressionOrStatementCollection allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ - (sLNodeAnnotatorVisitor isInExpression: node) not ] ] ] ] ]) + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not ] ] ] ]). + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ - | tMethod effectiveConstantExpressionValueSet constant2And4 returnNodes return2 return4 ifTrueIfFalseNode firstStmtList | + | tMethod effectiveConstantExpressionValueSet constant2And4 returnNodes return2 return4 ifTrueIfFalseNode firstStmtList constantTrue | tMethod := ccg methodNamed: #methodWithReturnInConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 6. - self assert: effectiveExpressionValueSet size equals: 3. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 6. + self assert: effectiveExpressionValueCollection size equals: 3. self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: 4). self assert: (effectiveConstantExpressionValueSet includes: true). - constant2And4 := effectiveExpressionValueSet select: [ :node | + constant2And4 := effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value isInteger ] ]. self assert: (constant2And4 allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + sLNodeAnnotatorVisitor isEffectiveReturnValue: node ] ]). - returnNodes := nonEffectiveExpressionOrStatementSet select: [ :node | - node isReturn ]. + + + returnNodes := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]. return2 := (returnNodes select: [ :node | node expression value = 2 ]) - asArray first. + first. return4 := (returnNodes select: [ :node | node expression value = 4 ]) - asArray first. + first. self assert: (sLNodeAnnotatorVisitor returningParentFor: - (effectiveExpressionValueSet select: [ :node | node value = 2 ]) - asArray first) + (effectiveExpressionValueCollection select: [ :node | + node value = 2 ]) first) equals: return2. self assert: (sLNodeAnnotatorVisitor returningParentFor: - (effectiveExpressionValueSet select: [ :node | node value = 4 ]) - asArray first) + (effectiveExpressionValueCollection select: [ :node | + node value = 4 ]) first) equals: return4. + constantTrue := (effectiveExpressionValueCollection select: [ :node | + node value = true ]) first. self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | + ((sLNodeAnnotatorVisitor isInReturn: constantTrue) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: constantTrue) not + and: [ + (sLNodeAnnotatorVisitor returningParentFor: constantTrue) isNil ] ]). + + effectiveExpressionValueCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + + self assert: + (nonEffectiveExpressionOrStatementCollection allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ] ] ]). + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]). - ifTrueIfFalseNode := (nonEffectiveExpressionOrStatementSet select: [ - :node | node isSend ]) asArray first. + ifTrueIfFalseNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isSend ]) first. firstStmtList := tMethod parseTree. { firstStmtList. ifTrueIfFalseNode } do: [ :node | self assert: (sLNodeAnnotatorVisitor isInExpression: ifTrueIfFalseNode) not ]. - (nonEffectiveExpressionOrStatementSet reject: [ :node | - node = firstStmtList or: [ node = ifTrueIfFalseNode ] ]) do: [ - :node | (sLNodeAnnotatorVisitor isInExpression: node) not ] + + (nonEffectiveExpressionOrStatementCollection reject: [ :node | + { + firstStmtList. + ifTrueIfFalseNode } includes: node ]) do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInExpression: node) ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturningConditional [ - | tMethod effectiveConstantExpressionValueSet constant2And4 returnNode constantTrue | + | tMethod effectiveConstantExpressionValueSet constant2And4 returnNode constantTrue firtStmtList | tMethod := ccg methodNamed: #methodWithReturningConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. + effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 5. - self assert: effectiveExpressionValueSet size equals: 3. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 5. + self assert: effectiveExpressionValueCollection size equals: 3. self assert: (effectiveConstantExpressionValueSet includes: 2). self assert: (effectiveConstantExpressionValueSet includes: 4). self assert: (effectiveConstantExpressionValueSet includes: true). - returnNode := (nonEffectiveExpressionOrStatementSet select: [ :node | - node isReturn ]) asArray first. - constant2And4 := effectiveExpressionValueSet select: [ :node | + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. + constant2And4 := effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value isInteger ] ]. self assert: (constant2And4 allSatisfy: [ :node | sLNodeAnnotatorVisitor isEffectiveReturnValue: node ]). - constantTrue := (effectiveExpressionValueSet select: [ :node | - node value = true ]) asArray first. + constantTrue := (effectiveExpressionValueCollection select: [ :node | + node value = true ]) first. self assert: (sLNodeAnnotatorVisitor isEffectiveReturnValue: constantTrue) not. - self assert: (effectiveExpressionValueSet allSatisfy: [ :node | + self assert: + (effectiveExpressionValueCollection allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + firtStmtList := tMethod parseTree. self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | - node isStatementList and: [ node children first isConstant ] ]) - allSatisfy: [ :node | sLNodeAnnotatorVisitor isInReturn: node ]). - - self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | - node isStatementList and: [ node children first isConstant not ] ]) - allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not ]). + ((nonEffectiveExpressionOrStatementCollection select: [ :node | + node ~= firtStmtList and: [ node ~= returnNode ] ]) allSatisfy: [ + :node | + (sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode + and: [ sLNodeAnnotatorVisitor isInExpression: node ] ] ]). + { + firtStmtList. + returnNode } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not ] ]) ]. self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | - node isSend ]) allSatisfy: [ :node | - sLNodeAnnotatorVisitor isInReturn: node ]). + (nonEffectiveExpressionOrStatementCollection allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInAssignment: node) not ]). - self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInAssignment: node) not ]) + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'send' } SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ + "for this method, the AST is modified by asTranslatorNodeIn: for assignment which push down the assignment inside a block " - | tMethod nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet firstSend secondSend thirdSend | + | tMethod nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet firstSend secondSend thirdSend assignmentNodes assignmentX assignmentY | tMethod := ccg methodNamed: #methodWithSendAsExpression. sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. - + self fillCollectionValue: tMethod. - nonEffectiveExpressionOrStatementSendSet := self getSendNodeInSet: - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSendSet := self getSendNodeInSet: - effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSendSet := self getSendNodeIn: + nonEffectiveExpressionOrStatementCollection. + effectiveExpressionValueSendSet := self getSendNodeIn: + effectiveExpressionValueCollection. firstSend := ((tMethod parseTree statements at: 1) statements at: 1) expression. secondSend := firstSend arguments at: 1. thirdSend := (tMethod parseTree statements at: 2) expression. - self assert: nonEffectiveExpressionOrStatementSet size equals: 4. - self assert: effectiveExpressionValueSet size equals: 8. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 4. + self assert: effectiveExpressionValueCollection size equals: 8. self assert: nonEffectiveExpressionOrStatementSendSet isEmpty. @@ -515,21 +556,45 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ self assert: (effectiveExpressionValueSendSet includes: secondSend). self assert: (effectiveExpressionValueSendSet includes: thirdSend). - self assert: - ((sLNodeAnnotatorVisitor isInAssignment: firstSend) and: [ - (sLNodeAnnotatorVisitor assigningParentFor: firstSend) name = #x - and: [ (sLNodeAnnotatorVisitor isInReturn: firstSend) not ] ]). + assignmentNodes := nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]. + assignmentX := (assignmentNodes select: [ :node | + node variable name = #x ]) first. + assignmentY := (assignmentNodes select: [ :node | + node variable name = #y ]) first. + { + firstSend. + secondSend } do: [ :node | + self + assert: (sLNodeAnnotatorVisitor assigningParentFor: node) + equals: assignmentX ]. - self assert: - ((sLNodeAnnotatorVisitor isInAssignment: secondSend) and: [ - (sLNodeAnnotatorVisitor assigningParentFor: secondSend) isNil - and: [ (sLNodeAnnotatorVisitor isInReturn: secondSend) not ] ]). + self + assert: (sLNodeAnnotatorVisitor assigningParentFor: thirdSend) + equals: assignmentY. + + { + firstSend. + thirdSend } do: [ :node | + self assert: + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) ]. self assert: - ((sLNodeAnnotatorVisitor isInAssignment: thirdSend) and: [ - (sLNodeAnnotatorVisitor assigningParentFor: thirdSend) name = #y - and: [ (sLNodeAnnotatorVisitor isInReturn: thirdSend) not ] ]) + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: secondSend) not. + + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + sLNodeAnnotatorVisitor isInAssignment: node ]) ]. + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInReturn: node) not ] ]) ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'send' } @@ -540,22 +605,21 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - nonEffectiveExpressionOrStatementSendSet := self getSendNodeInSet: - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSendSet := self getSendNodeInSet: - effectiveExpressionValueSet. + nonEffectiveExpressionOrStatementSendSet := self getSendNodeIn: + nonEffectiveExpressionOrStatementCollection. + effectiveExpressionValueSendSet := self getSendNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 4. - self assert: effectiveExpressionValueSet size equals: 3. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 4. + self assert: effectiveExpressionValueCollection size equals: 3. self assert: (effectiveConstantExpressionValueSet includes: 1). @@ -570,7 +634,10 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ self assert: (nonEffectiveExpressionOrStatementSendSet allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'statement-list' } @@ -581,17 +648,16 @@ SLAnnotatorVisitorTest >> testMethodWithStatementList [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - nonEffectiveExpressionOrStatementSet. + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 10. - self assert: effectiveExpressionValueSet size equals: 1. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 10. + self assert: effectiveExpressionValueCollection size equals: 1. self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 5). @@ -607,72 +673,73 @@ SLAnnotatorVisitorTest >> testMethodWithStatementList [ (nonEffectiveExpressionOrStatementConstantSet includes: 10). self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | + ((nonEffectiveExpressionOrStatementCollection select: [ :node | node isConstant and: [ node value = 10 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | + ((nonEffectiveExpressionOrStatementCollection select: [ :node | node isConstant and: [ node value ~= 10 ] ]) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). - constant11 := effectiveExpressionValueSet asArray first. + constant11 := effectiveExpressionValueCollection first. self assert: constant11 value equals: 11. self assert: ((sLNodeAnnotatorVisitor isInReturn: constant11) and: [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: constant11) and: [ - (sLNodeAnnotatorVisitor isInAssignment: constant11) not ] ]) + (sLNodeAnnotatorVisitor isInAssignment: constant11) not ] ]). + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection ] { #category : 'switch' } SLAnnotatorVisitorTest >> testMethodWithSwitchAsAssignmentExpression [ - | tMethod nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet | + | tMethod effectiveConstantExpressionValueSet assignmentNode | tMethod := ccg methodNamed: #methodWithSwitchAsAssignmentExpression. tMethod prepareMethodIn: ccg. sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. - nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - nonEffectiveExpressionOrStatementSet. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. - - self assert: nonEffectiveExpressionOrStatementSet size equals: 7. - self assert: effectiveExpressionValueSet size equals: 4. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 4). - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 5). + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 5. + self assert: effectiveExpressionValueCollection size equals: 6. self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: (effectiveConstantExpressionValueSet includes: 5). self assert: (effectiveConstantExpressionValueSet includes: 6). self assert: (effectiveConstantExpressionValueSet includes: 7). - self assert: ((effectiveExpressionValueSet select: [ :node | + assignmentNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]) first. + self assert: ((effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor assigningParentFor: node) name = #x ]). - self assert: ((effectiveExpressionValueSet select: [ :node | + (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNode ]). + self assert: ((effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ]). + (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNode ]). - self assert: - (nonEffectiveExpressionOrStatementSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ]). + effectiveExpressionValueCollection do: [ :node | + self + assert: (sLNodeAnnotatorVisitor assigningParentFor: node) + equals: assignmentNode ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection. - self assert: ((effectiveExpressionValueSet - addAll: nonEffectiveExpressionOrStatementSet; + self assert: ((effectiveExpressionValueCollection + addAll: nonEffectiveExpressionOrStatementCollection; yourself) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]) @@ -687,57 +754,52 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsReturnExpression [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - nonEffectiveExpressionOrStatementSet. + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 7. - self assert: effectiveExpressionValueSet size equals: 3. - - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 4). - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 5). + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 5. + self assert: effectiveExpressionValueCollection size equals: 5. self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: (effectiveConstantExpressionValueSet includes: 5). self assert: (effectiveConstantExpressionValueSet includes: 6). self assert: (effectiveConstantExpressionValueSet includes: 7). - self assert: ((effectiveExpressionValueSet select: [ :node | + self assert: ((effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - sLNodeAnnotatorVisitor isEffectiveReturnValue: node ] ]). - self assert: ((effectiveExpressionValueSet select: [ :node | + sLNodeAnnotatorVisitor isInReturn: node "and: [ + sLNodeAnnotatorVisitor isEffectiveReturnValue: node ]" ]). + self assert: ((effectiveExpressionValueCollection select: [ :node | node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]). + sLNodeAnnotatorVisitor isInReturn: node "and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ]" ]). firstStmtList := tMethod parseTree. self assert: - ((nonEffectiveExpressionOrStatementSet select: [ :node | + ((nonEffectiveExpressionOrStatementCollection select: [ :node | node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]). + :node | sLNodeAnnotatorVisitor isInReturn: node ]). self assert: - ((nonEffectiveExpressionOrStatementSet reject: [ :node | + ((nonEffectiveExpressionOrStatementCollection reject: [ :node | node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ - :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]). + :node | (sLNodeAnnotatorVisitor isInReturn: node) not ]). - self assert: ((effectiveExpressionValueSet - addAll: nonEffectiveExpressionOrStatementSet; + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection. + + self assert: ((effectiveExpressionValueCollection + addAll: nonEffectiveExpressionOrStatementCollection; yourself) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ]) @@ -752,34 +814,34 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsStatement [ sLNodeAnnotatorVisitor visit: tMethod parseTree. - nonEffectiveExpressionOrStatementSet := sLNodeAnnotatorVisitor - nonEffectiveExpressionOrStatementSet. - effectiveExpressionValueSet := sLNodeAnnotatorVisitor - effectiveExpressionValueSet. + self fillCollectionValue: tMethod. nonEffectiveExpressionOrStatementConstantSet := self - getValueOfConstantNodeInSet: - nonEffectiveExpressionOrStatementSet. + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. effectiveConstantExpressionValueSet := self - getValueOfConstantNodeInSet: - effectiveExpressionValueSet. + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. - self assert: nonEffectiveExpressionOrStatementSet size equals: 8. - self assert: effectiveExpressionValueSet size equals: 1. + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 6. + self assert: effectiveExpressionValueCollection size equals: 3. - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 4). - self assert: - (nonEffectiveExpressionOrStatementConstantSet includes: 5). self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 6). self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 7). self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: (effectiveConstantExpressionValueSet includes: 5). + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection. - self assert: ((effectiveExpressionValueSet - addAll: nonEffectiveExpressionOrStatementSet; + self assert: ((effectiveExpressionValueCollection + addAll: nonEffectiveExpressionOrStatementCollection; yourself) allSatisfy: [ :node | (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index aa779f9c93c..f4965765a85 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -610,9 +610,10 @@ SLInliner >> setDirectReturnAndExitVar: aSendNode [ | directReturn exitVar | sLNodeAnnotatorVisitor visit: currentMethod parseTree. - directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: aSendNode. - exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) ifNotNil: [ - :var | var name ]. + directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: + aSendNode. + exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) + ifNotNil: [ :assignment | assignment variable name ]. ^ OrderedCollection new add: directReturn; add: exitVar; diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 4e8c4dfd663..df6d2672487 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -112,7 +112,7 @@ SLNodeAnnotatorVisitor >> booleanInfoFor: aNode entry: aString [ ^ (info at: aNode) at: aString - ifPresent: [ true ] + ifPresent: [ :bool | bool ] ifAbsent: [ false ] ] @@ -122,17 +122,6 @@ SLNodeAnnotatorVisitor >> cleanInfo [ self initialize ] -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> effectiveExpressionValueSet [ - - | expressions | - expressions := IdentitySet new. - info associationsDo: [ :assoc | - (assoc value at: self expressionString) ifTrue: [ - expressions add: assoc key ] ]. - ^ expressions -] - { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ @@ -169,7 +158,7 @@ SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ currentShouldBePartiallyIgnored push: true. self annotateNodeState: aSendNode. - inExpressionStack push: true. + self pushInExpressionStack. self giveStacksInfoForMultipleBranches: aSendNode arguments. @@ -194,7 +183,7 @@ SLNodeAnnotatorVisitor >> initialize [ "empty, true or false on top depending of if the parent needs an expression as a dependance" searchForExpressionValueStack := Stack new. - "either empty or have true on top depending if the children are in the expression of one of their parents" + "either empty or have somethin on top depending if the children are in the expression of one of their parents" inExpressionStack := Stack new. "empty or true/false -> returningParent on top depending of if the parent has found its return value or not" @@ -224,7 +213,7 @@ SLNodeAnnotatorVisitor >> isEffectiveAssignmentValue: aNode [ SLNodeAnnotatorVisitor >> isEffectiveExpression: aNode [ ^ (self isEffectiveReturnValue: aNode) or: [ - (self assigningParentFor: aNode) isNotNil or: [ + (self isEffectiveAssignmentValue: aNode) or: [ self booleanInfoFor: aNode entry: self expressionString ] ] ] @@ -237,30 +226,28 @@ SLNodeAnnotatorVisitor >> isEffectiveReturnValue: aNode [ { #category : 'testing' } SLNodeAnnotatorVisitor >> isInAssignment: aNode [ - ^ self booleanInfoFor: aNode entry: self assignmentString + ^ self presenceInfoFor: aNode entry: self assignmentString ] { #category : 'testing' } SLNodeAnnotatorVisitor >> isInExpression: aNode [ - ^ self booleanInfoFor: aNode entry: self inExpressionString + ^ self presenceInfoFor: aNode entry: self inExpressionString ] { #category : 'testing' } SLNodeAnnotatorVisitor >> isInReturn: aNode [ - ^ self booleanInfoFor: aNode entry: self returnString + ^ self presenceInfoFor: aNode entry: self returnString ] -{ #category : 'accessing' } -SLNodeAnnotatorVisitor >> nonEffectiveExpressionOrStatementSet [ +{ #category : 'helpers' } +SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ - | statements | - statements := IdentitySet new. - info associationsDo: [ :assoc | - (assoc value at: self expressionString) ifFalse: [ - statements add: assoc key ] ]. - ^ statements + ^ (info at: aNode) + at: aString + ifPresent: [ true ] + ifAbsent: [ false ] ] { #category : 'helpers-annotate' } @@ -284,6 +271,13 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri entry at: aString put: assoc ] +{ #category : 'helpers' } +SLNodeAnnotatorVisitor >> pushInExpressionStack [ + "value doesn't matter for this stack " + + inExpressionStack push: true +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated @@ -403,7 +397,7 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ self annotateNodeState: anAssignmentNode. searchForExpressionValueStack push: true. - inExpressionStack push: true. + self pushInExpressionStack. searchForAssignValueStack push: true -> anAssignmentNode. @@ -477,10 +471,11 @@ SLNodeAnnotatorVisitor >> visitLabeledCommentNode: aLabeledCommentNode [ SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ self annotateNodeState: aReturnNode. - + + "line might be unecessary" searchForExpressionValueStack push: true. searchForReturnValueStack push: true -> aReturnNode. - inExpressionStack push: true. + self pushInExpressionStack. aReturnNode expression accept: self. @@ -499,7 +494,7 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ self annotateNodeState: aSendNode. searchForExpressionValueStack push: true. - inExpressionStack push: true. + self pushInExpressionStack. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. @@ -534,7 +529,6 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ currentShouldBePartiallyIgnored push: true. self annotateNodeState: aSwitchStatementNode. - caseStatementsAndOtherWiseOrNilCollection := OrderedCollection new. caseStatementsAndOtherWiseOrNilCollection := aSwitchStatementNode cases collect: [ :case | case second ]. @@ -542,7 +536,7 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ caseStatementsAndOtherWiseOrNilCollection add: o ]. searchForExpressionValueStack push: true. - inExpressionStack push: true. + self pushInExpressionStack. self giveStacksInfoForMultipleBranches: caseStatementsAndOtherWiseOrNilCollection. @@ -550,10 +544,7 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ | caseLabels | caseLabels := case first. - caseLabels do: [ :label | - searchForExpressionValueStack push: false. - label accept: self. - searchForExpressionValueStack pop ] ]. + caseLabels do: [ :label | label accept: self ] ]. aSwitchStatementNode expression accept: self. diff --git a/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st b/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st index b7b821ec3e7..3b0234f30ea 100644 --- a/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st +++ b/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st @@ -4,8 +4,9 @@ Extension { #name : 'SLAnnotatorVisitorTest' } SLAnnotatorVisitorTest >> setUp [ super setUp. - sLNodeAnnotatorVisitor := SLNodeAnnotatorVisitor - new. + sLNodeAnnotatorVisitor := SLNodeAnnotatorVisitor new. ccg addClass: SLNodeAnnotatorVisitorTestClass. + nonEffectiveExpressionOrStatementCollection := OrderedCollection new. + effectiveExpressionValueCollection := OrderedCollection new. ccg inferTypes ] From 2f7b1e9179e8d5d97bd5f9f483b9c1588da25c22 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 25 Jun 2025 16:16:56 +0200 Subject: [PATCH 11/86] better push down of returns and assignments --- smalltalksrc/Slang/SLInliner.class.st | 61 ++++++++++++------- .../Slang/SLNodeAnnotatorVisitor.class.st | 25 ++++---- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index f4965765a85..b57fd105204 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -392,27 +392,10 @@ SLInliner >> inlineSend: aSendNode [ ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. - "little hack to move returns to the lowest part of the ast" - aSendNode parent replaceChild: aSendNode with: callee parseTree. - (sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode) - ifTrue: [ - callee parseTree parent - replaceChild: callee parseTree - with: aSendNode. - callee parseTree parent: callee. - assignDirectReturnAndExitVarBlock value: - (self setDirectReturnAndExitVar: aSendNode) ]. - - "little hack to move assignments to the lowest part of the ast" - aSendNode parent replaceChild: aSendNode with: callee parseTree. - (sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode) - ifTrue: [ - callee parseTree parent - replaceChild: callee parseTree - with: aSendNode. - callee parseTree parent: callee. - assignDirectReturnAndExitVarBlock value: - (self setDirectReturnAndExitVar: aSendNode) ]. + self + moveDownReturnsAndAssignmentsFor: aSendNode + including: callee + resetInfoBlock: assignDirectReturnAndExitVarBlock. (inlineStmts := OrderedCollection new: @@ -442,6 +425,12 @@ SLInliner >> inlineSend: aSendNode [ ^ inlineStmts ] +{ #category : 'transformation' } +SLInliner >> invert: aSendNode replacement: aTMethod [ + + aSendNode parent replaceChild: aSendNode with: aTMethod parseTree +] + { #category : 'inlining-decision' } SLInliner >> isConditionalToBeTransformedForAssignment: aSend [ "Answer if a send is of the form @@ -559,6 +548,27 @@ SLInliner >> isSubstitutableNode: aNode intoMethod: targetMeth [ ^ true ] +{ #category : 'transformation' } +SLInliner >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ + + | needToResetData | + "move returns to the lowest part of the ast including in aTMethodReplacement" + self invert: aSendNode replacement: aTMethodReplacement. + needToResetData := sLNodeAnnotatorVisitor removeReturningParentFrom: + aSendNode. + self revert: aSendNode replacement: aTMethodReplacement. + needToResetData ifTrue: [ + aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. + + "move assignments to the lowest part of the ast including in callee" + self invert: aSendNode replacement: aTMethodReplacement. + needToResetData := sLNodeAnnotatorVisitor removeAssigningParentFrom: + aSendNode. + self revert: aSendNode replacement: aTMethodReplacement. + needToResetData ifFalse: [ ^ self ]. + aBlock value: (self setDirectReturnAndExitVar: aSendNode) +] + { #category : 'transformation' } SLInliner >> propagateArgumentTypeArguments: calleeParameters in: callee from: aSendNode [ "Propagate any unusual argument types to untyped argument variables" @@ -605,6 +615,15 @@ SLInliner >> replaceNodeIn: aSendNode with: data [ except: method args ] +{ #category : 'transformation' } +SLInliner >> revert: aSendNode replacement: aTMethod [ + + aTMethod parseTree parent + replaceChild: aTMethod parseTree + with: aSendNode. + aTMethod parseTree parent: aTMethod +] + { #category : 'inlining-support' } SLInliner >> setDirectReturnAndExitVar: aSendNode [ diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index df6d2672487..70208eb4173 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -279,19 +279,18 @@ SLNodeAnnotatorVisitor >> pushInExpressionStack [ ] { #category : 'transformation' } -SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aSendNode [ +SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated answer if something was done" - | assignment exitVar | - exitVar := self assigningParentFor: aSendNode. - exitVar ifNil: [ ^ false ]. + | assignment | + assignment := self assigningParentFor: aNode. + assignment ifNil: [ ^ false ]. - assignment := exitVar parent. - "necessary because of the inlining hack" + "necessary because in inlining we switched the send node with its replacement" self cleanInfo. self visit: assignment tMethod parseTree. - self transformToAssignmentFrom: assignment exitVar: exitVar. + self transformToAssignmentFrom: assignment assignment: assignment. assignment parent replaceChild: assignment @@ -310,7 +309,7 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ (self isEffectiveReturnValue: aSendNode) ifFalse: [ ^ false ]. returningParent := self returningParentFor: aSendNode. - "necessary because of the inlining hack" + "necessary because in inlining we switched the send node with its replacement" self cleanInfo. self visit: returningParent tMethod parseTree. self transformDirectReturnFrom: returningParent. @@ -366,18 +365,20 @@ SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ ] { #category : 'transformation' } -SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode exitVar: exitVar [ +SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assignment [ "recursively go throught the ast to transfrom expression of assignment a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" parentNode children do: [ :child | - (self assigningParentFor: child) == exitVar + ((self isEffectiveAssignmentValue: child) and: [ + (self assigningParentFor: child) == assignment ]) ifTrue: [ child parent replaceChild: child with: (TAssignmentNode new - variable: exitVar; + variable: assignment variable; expression: child; yourself) ] - ifFalse: [ self transformToAssignmentFrom: child exitVar: exitVar ] ] + ifFalse: [ + self transformToAssignmentFrom: child assignment: assignment ] ] ] { #category : 'visiting' } From 8f03269303808ad48cb932b2b9f79b7e90ada6de Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 25 Jun 2025 16:21:03 +0200 Subject: [PATCH 12/86] rename constant --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 70208eb4173..999fea01452 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -86,7 +86,7 @@ SLNodeAnnotatorVisitor >> assigningParentFor: aNode [ { #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> assignmentString [ - ^ #exitVar + ^ #assignment ] { #category : 'helpers' } From 93cd99b8e314ef1c9dea3237f55f945e3618611d Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 25 Jun 2025 17:11:07 +0200 Subject: [PATCH 13/86] rename class --- smalltalksrc/Slang/CCodeGenerator.class.st | 2 +- smalltalksrc/Slang/SLInlinerStrategy.class.st | 2 +- ...ss.st => SLInlinerWithAnnotation.class.st} | 80 ++++++++++--------- .../VMMaker/SlangInliningTest.extension.st | 2 +- 4 files changed, 46 insertions(+), 40 deletions(-) rename smalltalksrc/Slang/{SLInliner.class.st => SLInlinerWithAnnotation.class.st} (91%) diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index d37da533749..8a5fd4becfa 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -1029,7 +1029,7 @@ CCodeGenerator >> doBasicInlining: inlineFlagOrSymbol [ | pass progress inliner | self collectInlineList: inlineFlagOrSymbol. - inliner := SLInlinerStrategy new + inliner := SLInlinerWithAnnotation new codeGenerator: self; yourself. pass := 0. diff --git a/smalltalksrc/Slang/SLInlinerStrategy.class.st b/smalltalksrc/Slang/SLInlinerStrategy.class.st index c710f37e813..37007b71da9 100644 --- a/smalltalksrc/Slang/SLInlinerStrategy.class.st +++ b/smalltalksrc/Slang/SLInlinerStrategy.class.st @@ -20,5 +20,5 @@ SLInlinerStrategy >> doInliningIn: aTMethod [ SLInlinerStrategy >> initialize [ super initialize. - sLInliner := SLInliner new + sLInliner := SLInlinerWithAnnotation new ] diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st similarity index 91% rename from smalltalksrc/Slang/SLInliner.class.st rename to smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index b57fd105204..23003ad5048 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -1,8 +1,7 @@ Class { - #name : 'SLInliner', - #superclass : 'Object', + #name : 'SLInlinerWithAnnotation', + #superclass : 'SLAbstractInlineStrategy', #instVars : [ - 'codeGenerator', 'currentMethod', 'sLNodeAnnotatorVisitor' ], @@ -11,7 +10,7 @@ Class { } { #category : 'testing' } -SLInliner >> checkForCompletenessFor: aTMethod [ +SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ "Set the complete flag if the parse tree contains no further candidates for inlining." | foundIncompleteSend | @@ -39,7 +38,7 @@ SLInliner >> checkForCompletenessFor: aTMethod [ ] { #category : 'testing' } -SLInliner >> checkForFlagIn: aTMethod [ +SLInlinerWithAnnotation >> checkForFlagIn: aTMethod [ (aTMethod parseTree sizeWithoutComments > 1 and: [ aTMethod parseTree firstNonCommentStatement isSend and: [ @@ -48,31 +47,38 @@ SLInliner >> checkForFlagIn: aTMethod [ ] { #category : 'accessing' } -SLInliner >> codeGenerator [ +SLInlinerWithAnnotation >> codeGenerator [ ^ codeGenerator ] { #category : 'accessing' } -SLInliner >> codeGenerator: aCodeGenerator [ +SLInlinerWithAnnotation >> codeGenerator: aCodeGenerator [ codeGenerator := aCodeGenerator ] { #category : 'accessing' } -SLInliner >> currentMethod [ +SLInlinerWithAnnotation >> currentMethod [ ^ currentMethod ] { #category : 'accessing' } -SLInliner >> currentMethod: aTMethod [ +SLInlinerWithAnnotation >> currentMethod: aTMethod [ currentMethod := aTMethod ] +{ #category : 'inlining' } +SLInlinerWithAnnotation >> doInliningIn: aTMethod [ + + self currentMethod: aTMethod. + ^ self tryToInlineMethodsInCurrentMethod +] + { #category : 'inlining-preparation' } -SLInliner >> ensureConditionalAssignmentsAreTransformedInCurrentMethod [ +SLInlinerWithAnnotation >> ensureConditionalAssignmentsAreTransformedInCurrentMethod [ "Make passes transforming foo := expr ifTrue: [a] ifFalse: [b] into @@ -97,14 +103,14 @@ SLInliner >> ensureConditionalAssignmentsAreTransformedInCurrentMethod [ ] { #category : 'initialization' } -SLInliner >> initialize [ +SLInlinerWithAnnotation >> initialize [ super initialize. sLNodeAnnotatorVisitor := SLNodeAnnotatorVisitor new ] { #category : 'inlining-support' } -SLInliner >> inlineBuiltin: aSendNode [ +SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ | sel meth inlinedReplacement | (aSendNode selector beginsWith: 'perform:') ifTrue: [ @@ -124,7 +130,7 @@ SLInliner >> inlineBuiltin: aSendNode [ ] { #category : 'inlining-support' } -SLInliner >> inlineCodeOrNilForStatement: aNode [ +SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." (aNode isReturn and: [ self isInlineableSend: aNode expression ]) @@ -139,7 +145,7 @@ SLInliner >> inlineCodeOrNilForStatement: aNode [ ] { #category : 'inlining-support-condtional' } -SLInliner >> inlineConditional: aSendNode [ +SLInlinerWithAnnotation >> inlineConditional: aSendNode [ "If possible answer the inlining of a conditional, otherwise answer nil. Currently the only pattern we support is aSend ifTrue:/ifFalse: [...] @@ -154,7 +160,7 @@ SLInliner >> inlineConditional: aSendNode [ ] { #category : 'inlining-support-condtional' } -SLInliner >> inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: isGuarding [ +SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: isGuarding [ "init data for inlining conditional" | data | @@ -183,7 +189,7 @@ SLInliner >> inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: i ] { #category : 'inlining-support' } -SLInliner >> inlineFunctionCall: aSendNode [ +SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ "Answer the body of the called function, substituting the actual parameters for the formal argument variables in the method body. Assume caller has established that: @@ -222,7 +228,7 @@ SLInliner >> inlineFunctionCall: aSendNode [ ] { #category : 'inlining-support-condtional' } -SLInliner >> inlineGuardingConditional: aSendNode [ +SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ "Inline aSend ifTrue:/ifFalse: [statements] where aSend is inlineable and always answers booleans. We convert @@ -277,7 +283,7 @@ SLInliner >> inlineGuardingConditional: aSendNode [ ] { #category : 'inlining-support-condtional' } -SLInliner >> inlineReturningConditional: aSendNode [ +SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ "Inline aSend ifTrue:/ifFalse: [^expr] where aSend is inlineable and always answers booleans. We inline ^expr @@ -333,7 +339,7 @@ SLInliner >> inlineReturningConditional: aSendNode [ ] { #category : 'inlining-support' } -SLInliner >> inlineSend: aSendNode [ +SLInlinerWithAnnotation >> inlineSend: aSendNode [ "Answer a collection of statements to replace the given send. directReturn indicates that the send is the expression in a return statement, so returns can be left in the body of the inlined method. If exitVar is nil, the value returned by the send is not @@ -426,13 +432,13 @@ SLInliner >> inlineSend: aSendNode [ ] { #category : 'transformation' } -SLInliner >> invert: aSendNode replacement: aTMethod [ +SLInlinerWithAnnotation >> invert: aSendNode replacement: aTMethod [ aSendNode parent replaceChild: aSendNode with: aTMethod parseTree ] { #category : 'inlining-decision' } -SLInliner >> isConditionalToBeTransformedForAssignment: aSend [ +SLInlinerWithAnnotation >> isConditionalToBeTransformedForAssignment: aSend [ "Answer if a send is of the form e1 ifTrue: [e2 ifTrue: [self m1] ifFalse: [self m2]] @@ -452,7 +458,7 @@ SLInliner >> isConditionalToBeTransformedForAssignment: aSend [ ] { #category : 'inlining-decision' } -SLInliner >> isInlineableConditional: aSendNode [ +SLInlinerWithAnnotation >> isInlineableConditional: aSendNode [ "Answer if the given send node is of the form aSend [ifTrue:|ifFalse:] [statements] where the method for aSend is marked as inline and all returns within it answer booleans." @@ -470,7 +476,7 @@ SLInliner >> isInlineableConditional: aSendNode [ ] { #category : 'inlining-decision' } -SLInliner >> isInlineableFunctionCall: aNode [ +SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode [ "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted." codeGenerator maybeBreakForTestToInline: aNode in: self. @@ -492,7 +498,7 @@ SLInliner >> isInlineableFunctionCall: aNode [ ] { #category : 'inlining-decision' } -SLInliner >> isInlineableSend: aNode [ +SLInlinerWithAnnotation >> isInlineableSend: aNode [ "Answer if the given expresssion node is a call to a method that can be inlined." | m | @@ -507,7 +513,7 @@ SLInliner >> isInlineableSend: aNode [ ] { #category : 'inlining-decision' } -SLInliner >> isSubstitutableNode: aNode intoMethod: targetMeth [ +SLInlinerWithAnnotation >> isSubstitutableNode: aNode intoMethod: targetMeth [ "Answer true if the given parameter node is either a constant, a local variable, or a formal parameter of the receiver. Such parameter nodes may be substituted directly into the body of the method during inlining. Note that global variables cannot be subsituted into methods with possible side effects (i.e., methods that may assign to global variables) because the inlined method might depend on having the value of the global variable captured when it is passed in as an argument." | var | @@ -549,7 +555,7 @@ SLInliner >> isSubstitutableNode: aNode intoMethod: targetMeth [ ] { #category : 'transformation' } -SLInliner >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ +SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ | needToResetData | "move returns to the lowest part of the ast including in aTMethodReplacement" @@ -570,7 +576,7 @@ SLInliner >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodRepl ] { #category : 'transformation' } -SLInliner >> propagateArgumentTypeArguments: calleeParameters in: callee from: aSendNode [ +SLInlinerWithAnnotation >> propagateArgumentTypeArguments: calleeParameters in: callee from: aSendNode [ "Propagate any unusual argument types to untyped argument variables" calleeParameters with: aSendNode arguments do: [ :formal :actual | @@ -586,7 +592,7 @@ SLInliner >> propagateArgumentTypeArguments: calleeParameters in: callee from: a ] { #category : 'transformation' } -SLInliner >> propagateReturnTypeDirectReturn: directReturn exitVar: exitVar callee: callee [ +SLInlinerWithAnnotation >> propagateReturnTypeDirectReturn: directReturn exitVar: exitVar callee: callee [ "Propagate the return type of an inlined method" | exitType | @@ -601,7 +607,7 @@ SLInliner >> propagateReturnTypeDirectReturn: directReturn exitVar: exitVar call ] { #category : 'inlining-support-condtional' } -SLInliner >> replaceNodeIn: aSendNode with: data [ +SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ | replacementTree method | replacementTree := data at: #replacementTree. @@ -616,7 +622,7 @@ SLInliner >> replaceNodeIn: aSendNode with: data [ ] { #category : 'transformation' } -SLInliner >> revert: aSendNode replacement: aTMethod [ +SLInlinerWithAnnotation >> revert: aSendNode replacement: aTMethod [ aTMethod parseTree parent replaceChild: aTMethod parseTree @@ -625,7 +631,7 @@ SLInliner >> revert: aSendNode replacement: aTMethod [ ] { #category : 'inlining-support' } -SLInliner >> setDirectReturnAndExitVar: aSendNode [ +SLInlinerWithAnnotation >> setDirectReturnAndExitVar: aSendNode [ | directReturn exitVar | sLNodeAnnotatorVisitor visit: currentMethod parseTree. @@ -640,7 +646,7 @@ SLInliner >> setDirectReturnAndExitVar: aSendNode [ ] { #category : 'inlining-preparation' } -SLInliner >> statementsListsForInliningInCurrentMethod [ +SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. Currently, we cannot inline into the argument blocks of and: and or: messages. We do not want to inline code strings within cCode:inSmalltalk: blocks (those with a @@ -700,7 +706,7 @@ SLInliner >> statementsListsForInliningInCurrentMethod [ ] { #category : 'inlining-preparation' } -SLInliner >> transformConditionalAssignment: node [ +SLInlinerWithAnnotation >> transformConditionalAssignment: node [ "If possible answer the transformation of code of the form var := e1 ifTrue: [e2 ifTrue: [self m1] ifFalse: [self m2]] @@ -724,7 +730,7 @@ SLInliner >> transformConditionalAssignment: node [ ] { #category : 'transformation' } -SLInliner >> trimArgumentsIn: aTMethod [ +SLInlinerWithAnnotation >> trimArgumentsIn: aTMethod [ | calleeParameters omittedParameters varNode | calleeParameters := aTMethod args. @@ -748,7 +754,7 @@ SLInliner >> trimArgumentsIn: aTMethod [ ] { #category : 'inlining' } -SLInliner >> tryToInlineMethodExpressionsInCurrentMethod [ +SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ "Expand any (complete) inline methods sent by this method as receivers or parameters. Answer if anything was inlined." @@ -774,7 +780,7 @@ SLInliner >> tryToInlineMethodExpressionsInCurrentMethod [ ] { #category : 'inlining' } -SLInliner >> tryToInlineMethodStatementsListsInCurrentMethod [ +SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ "Expand any (complete) inline methods sent by this method as top-level statements. Answer if anything was inlined." @@ -798,7 +804,7 @@ SLInliner >> tryToInlineMethodStatementsListsInCurrentMethod [ ] { #category : 'inlining' } -SLInliner >> tryToInlineMethodsInCurrentMethod [ +SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ "Expand any (complete) inline methods sent by this method. Set the complete flag when all inlining has been done. Answer if something was inlined." diff --git a/smalltalksrc/VMMaker/SlangInliningTest.extension.st b/smalltalksrc/VMMaker/SlangInliningTest.extension.st index c6b5def57e9..10a861a3a75 100644 --- a/smalltalksrc/VMMaker/SlangInliningTest.extension.st +++ b/smalltalksrc/VMMaker/SlangInliningTest.extension.st @@ -8,5 +8,5 @@ SlangInliningTest >> setUp [ "necessary to get the type of sqInt" SpurMemoryManager initBytesPerWord: 8. ccg inferTypes. - sLInliner := SLInliner new + sLInliner := SLInlinerWithAnnotation new ] From 2bdbad6bd86d3f9c3881a38777857d8291763a34 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 30 Jun 2025 10:56:59 +0200 Subject: [PATCH 14/86] suppress unused strategy --- smalltalksrc/Slang/SLInlinerStrategy.class.st | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 smalltalksrc/Slang/SLInlinerStrategy.class.st diff --git a/smalltalksrc/Slang/SLInlinerStrategy.class.st b/smalltalksrc/Slang/SLInlinerStrategy.class.st deleted file mode 100644 index 37007b71da9..00000000000 --- a/smalltalksrc/Slang/SLInlinerStrategy.class.st +++ /dev/null @@ -1,24 +0,0 @@ -Class { - #name : 'SLInlinerStrategy', - #superclass : 'SLAbstractInlineStrategy', - #instVars : [ - 'sLInliner' - ], - #category : 'Slang', - #package : 'Slang' -} - -{ #category : 'inlining' } -SLInlinerStrategy >> doInliningIn: aTMethod [ - - sLInliner currentMethod: aTMethod. - sLInliner codeGenerator: codeGenerator. - ^ sLInliner tryToInlineMethodsInCurrentMethod -] - -{ #category : 'initialization' } -SLInlinerStrategy >> initialize [ - - super initialize. - sLInliner := SLInlinerWithAnnotation new -] From e4cd42fea5039d43f850038ab20adbebd39b2a86 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 30 Jun 2025 11:02:06 +0200 Subject: [PATCH 15/86] add a third inliner + small cleaning --- smalltalksrc/Slang/SLInliner.class.st | 823 ++++++++++++++++++ .../Slang/SLInlinerWithAnnotation.class.st | 6 - 2 files changed, 823 insertions(+), 6 deletions(-) create mode 100644 smalltalksrc/Slang/SLInliner.class.st diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st new file mode 100644 index 00000000000..012807fb16b --- /dev/null +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -0,0 +1,823 @@ +Class { + #name : 'SLInliner', + #superclass : 'SLAbstractInlineStrategy', + #instVars : [ + 'currentMethod', + 'sLNodeAnnotatorVisitor' + ], + #category : 'Slang', + #package : 'Slang' +} + +{ #category : 'testing' } +SLInliner >> checkForCompletenessFor: aTMethod [ + "Set the complete flag if the parse tree contains no further candidates for inlining." + + | foundIncompleteSend | + codeGenerator maybeBreakForTestOfInliningOf: aTMethod selector. + + foundIncompleteSend := false. + + aTMethod parseTree + nodesDo: [ :node | + node isSend ifTrue: [ + (aTMethod + methodIsEffectivelyComplete: node selector + in: codeGenerator) + ifTrue: [ + (self isInlineableFunctionCall: node) ifTrue: [ + aTMethod complete: false. "more inlining to do" + ^ self ] ] + ifFalse: [ foundIncompleteSend := true ] ] ] + unless: [ :node | + node isSend and: [ + node selector == #cCode:inSmalltalk: or: [ + codeGenerator isAssertSelector: node selector ] ] ]. + + foundIncompleteSend ifFalse: [ aTMethod complete: true ] +] + +{ #category : 'testing' } +SLInliner >> checkForFlagIn: aTMethod [ + + (aTMethod parseTree sizeWithoutComments > 1 and: [ + aTMethod parseTree firstNonCommentStatement isSend and: [ + aTMethod parseTree firstNonCommentStatement selector == #flag: ] ]) + ifTrue: [ aTMethod parseTree removeFirst ] +] + +{ #category : 'accessing' } +SLInliner >> codeGenerator [ + + ^ codeGenerator +] + +{ #category : 'accessing' } +SLInliner >> currentMethod [ + + ^ currentMethod +] + +{ #category : 'accessing' } +SLInliner >> currentMethod: aTMethod [ + + currentMethod := aTMethod +] + +{ #category : 'inlining' } +SLInliner >> doInliningIn: aTMethod [ + + self currentMethod: aTMethod. + ^ self tryToInlineMethodsInCurrentMethod +] + +{ #category : 'inlining-preparation' } +SLInliner >> ensureConditionalAssignmentsAreTransformedInCurrentMethod [ + "Make passes transforming + foo := expr ifTrue: [a] ifFalse: [b] + into + expr ifTrue: [foo := a] ifFalse: [foo := b] + until no such instances exist in the tree. This is needed for correct inlining + given the limitations of inlineCodeOrNilForStatement:returningNodes:in:" + + | transformedAssignments | + [ + transformedAssignments := Dictionary new. + currentMethod parseTree + nodesDo: [ :node | + (self transformConditionalAssignment: node) ifNotNil: [ + :replacement | transformedAssignments at: node put: replacement ] ] + unless: [ :node | "Don't inline the arguments to asserts to keep the asserts readable" + node isSend and: [ + node selector == #cCode:inSmalltalk: or: [ + codeGenerator isAssertSelector: node selector ] ] ]. + transformedAssignments notEmpty and: [ + currentMethod replaceNodesIn: transformedAssignments. + true ] ] whileTrue +] + +{ #category : 'initialization' } +SLInliner >> initialize [ + + super initialize. + sLNodeAnnotatorVisitor := SLNodeAnnotatorVisitor new +] + +{ #category : 'inlining-support' } +SLInliner >> inlineBuiltin: aSendNode [ + + | sel meth inlinedReplacement | + (aSendNode selector beginsWith: 'perform:') ifTrue: [ + ^ self inlineFunctionCall: aSendNode asTransformedConstantPerform ]. + sel := aSendNode receiver selector. + meth := codeGenerator methodNamed: sel. + (meth notNil and: [ meth inline == true ]) ifFalse: [ ^ nil ]. + (meth isFunctionalIn: codeGenerator) ifTrue: [ + inlinedReplacement := self inlineFunctionCall: aSendNode receiver. + ^ TSendNode new + setSelector: aSendNode selector + receiver: inlinedReplacement + arguments: aSendNode arguments copy ]. + (self isInlineableConditional: aSendNode) ifTrue: [ + ^ self inlineConditional: aSendNode ]. + ^ nil +] + +{ #category : 'inlining-support' } +SLInliner >> inlineCodeOrNilForStatement: aNode [ + "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." + + (aNode isReturn and: [ self isInlineableSend: aNode expression ]) + ifTrue: [ ^ self inlineSend: aNode expression ]. + + (aNode isAssignment and: [ self isInlineableSend: aNode expression ]) + ifTrue: [ ^ self inlineSend: aNode expression ]. + + (aNode isSend and: [ self isInlineableSend: aNode ]) ifTrue: [ + ^ self inlineSend: aNode ]. + ^ nil +] + +{ #category : 'inlining-support-condtional' } +SLInliner >> inlineConditional: aSendNode [ + "If possible answer the inlining of a conditional, otherwise answer nil. + Currently the only pattern we support is + aSend ifTrue:/ifFalse: [...] + where aSend is marked inline and always answers booleans." + + self assert: (self isInlineableConditional: aSendNode). + self assert: aSendNode arguments first isStatementList. + ^ (aSendNode arguments first sizeWithoutComments = 1 and: [ + aSendNode arguments first lastNonCommentStatement isReturn ]) + ifTrue: [ self inlineReturningConditional: aSendNode ] + ifFalse: [ self inlineGuardingConditional: aSendNode ] +] + +{ #category : 'inlining-support-condtional' } +SLInliner >> inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: isGuarding [ + "init data for inlining conditional" + + | data | + self assert: currentMethod == codeGenerator currentMethod. + self assert: (self isInlineableConditional: aSendNode). + codeGenerator maybeBreakForInlineOf: aSendNode in: self. + + data := Dictionary new. + data at: #isIfTrue put: aSendNode selector = #ifTrue:. + + data + at: #method + put: (codeGenerator methodNamed: aSendNode receiver selector) copy. + + data + at: #replacementTree + put: (self inlineFunctionCall: aSendNode receiver). + + data at: #returnReplacement put: (isGuarding + ifTrue: [ + TLabeledCommentNode new setLabel: + (currentMethod unusedLabelForInlining: (data at: #method)) ] + ifFalse: [ aSendNode arguments first ]). + + ^ data +] + +{ #category : 'inlining-support' } +SLInliner >> inlineFunctionCall: aSendNode [ + "Answer the body of the called function, substituting the actual + parameters for the formal argument variables in the method body. + Assume caller has established that: + 1. the method arguments are all substitutable nodes, and + 2. the method to be inlined contains no additional embedded returns." + + | sel meth parametersToReplaceByArgument argsForInlining substitutionDict | + sel := aSendNode selector. + meth := (codeGenerator methodNamed: sel) copy. + meth ifNil: [ ^ self inlineBuiltin: aSendNode ]. + parametersToReplaceByArgument := Set withAll: currentMethod args. + argsForInlining := aSendNode argumentsForInliningCodeGenerator: + codeGenerator. + meth args with: argsForInlining do: [ :argName :exprNode | + exprNode isLeaf ifTrue: [ + parametersToReplaceByArgument add: argName ] ]. + self checkForFlagIn: meth. + meth + renameVarsForInliningInto: currentMethod + except: parametersToReplaceByArgument + in: codeGenerator. + meth renameLabelsForInliningInto: currentMethod. + currentMethod + addVarsDeclarationsAndLabelsOf: meth + except: parametersToReplaceByArgument. + substitutionDict := Dictionary new: meth args size * 2. + meth args with: argsForInlining do: [ :argName :exprNode | + substitutionDict at: argName put: exprNode. + (parametersToReplaceByArgument includes: argName) ifFalse: [ + currentMethod removeLocal: argName ] ]. + meth parseTree bindVariablesIn: substitutionDict. + meth parseTree parent: aSendNode parent. + ^ meth parseTree endsWithReturn + ifTrue: [ meth parseTree copyWithoutReturn ] + ifFalse: [ meth parseTree ] +] + +{ #category : 'inlining-support-condtional' } +SLInliner >> inlineGuardingConditional: aSendNode [ + "Inline + aSend ifTrue:/ifFalse: [statements] + where aSend is inlineable and always answers booleans. We convert + the boolean returns in aSend to jumps." + + | data evaluateIfTrue replacementTree map lastNode evaluateLabel skipLabel | + data := self + inlineConditionalGuardingOrConditionalInit: aSendNode + isGuarding: true. + evaluateIfTrue := data at: #isIfTrue. + replacementTree := data at: #replacementTree. + skipLabel := data at: #returnReplacement. + map := Dictionary new. + + (replacementTree lastNonCommentStatement isReturn and: [ + replacementTree lastNonCommentStatement expression value + = evaluateIfTrue ]) ifTrue: [ + lastNode := replacementTree lastNonCommentStatement ]. + + replacementTree nodesDo: [ :node | + | expr | + node isReturn ifTrue: [ + expr := node expression. + self assert: + (expr isConstant and: [ #( true false ) includes: expr value ]). + map at: node put: (expr value ~~ evaluateIfTrue + ifTrue: [ TGoToNode label: skipLabel label ] + ifFalse: [ + node == lastNode + ifTrue: [ + TLabeledCommentNode new setComment: + 'end ' , aSendNode receiver selector , '; fall through' ] + ifFalse: [ + evaluateLabel ifNil: [ + evaluateLabel := TLabeledCommentNode new setLabel: + (currentMethod unusedLabelForInlining: + (data at: #method)) ]. + TGoToNode label: evaluateLabel label ] ]) ] ]. + + data at: #map put: map. + self replaceNodeIn: aSendNode with: data. + + ^ TStatementListNode new + setArguments: #( ) + statements: (evaluateLabel + ifNil: [ + replacementTree statements + , aSendNode arguments first statements , { skipLabel } ] + ifNotNil: [ + replacementTree statements , { evaluateLabel } + , aSendNode arguments first statements , { skipLabel } ]) +] + +{ #category : 'inlining-support-condtional' } +SLInliner >> inlineReturningConditional: aSendNode [ + "Inline + aSend ifTrue:/ifFalse: [^expr] + where aSend is inlineable and always answers booleans. We inline ^expr + into aSend." + + | data returnIfTrue returnNode replacementTree map lastNode label | + data := self + inlineConditionalGuardingOrConditionalInit: aSendNode + isGuarding: false. + returnIfTrue := data at: #isIfTrue. + replacementTree := data at: #replacementTree. + returnNode := data at: #returnReplacement. + map := Dictionary new. + + "The last node is either a return or a boolean constant." + lastNode := replacementTree lastNonCommentStatement. + lastNode isReturn + ifTrue: [ + lastNode expression value == returnIfTrue ifTrue: [ + lastNode := nil "i.e. take the fall-through path and /don't/ return" ] ] + ifFalse: [ + self assert: + (lastNode isConstant and: [ + #( true false ) includes: lastNode value ]). + lastNode value == returnIfTrue ifTrue: [ "i.e. /do/ return" + map at: lastNode put: returnNode ] ]. + replacementTree nodesDo: [ :node | + | expr | + node isReturn ifTrue: [ + expr := node expression. + self assert: + (expr isConstant and: [ #( true false ) includes: expr value ]). + map at: node put: (expr value == returnIfTrue + ifTrue: [ returnNode ] + ifFalse: [ + node == lastNode + ifTrue: [ + TLabeledCommentNode new setComment: + 'end ' , aSendNode receiver selector , '; fall through' ] + ifFalse: [ + label ifNil: [ + label := TLabeledCommentNode new setLabel: + (currentMethod unusedLabelForInlining: (data at: #method)) ]. + TGoToNode label: label label ] ]) ] ]. + + data at: #map put: map. + self replaceNodeIn: aSendNode with: data. + + ^ label ifNil: [ replacementTree ] ifNotNil: [ + TStatementListNode new setArguments: #( ) statements: { + replacementTree. + label } ] +] + +{ #category : 'inlining-support' } +SLInliner >> inlineSend: aSendNode [ + "Answer a collection of statements to replace the given send. directReturn indicates + that the send is the expression in a return statement, so returns can be left in the + body of the inlined method. If exitVar is nil, the value returned by the send is not + used; thus, returns need not assign to the output variable. + + Types are propagated to as-yet-untyped variables when inlining a send that is assigned, + otherwise the assignee variable type must match the return type of the inlinee. Return + types are not propagated." + + | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn assignDirectReturnAndExitVarBlock | + sel := aSendNode selector. + callee := codeGenerator methodNamed: sel. + + assignDirectReturnAndExitVarBlock := [ :res | + directReturn := res first. + exitVar := res second ]. + + "convenient for debugging..." + codeGenerator maybeBreakForInlineOf: aSendNode in: self. + + [ :res | + omittedParameters := res first. + calleeParameters := res second ] value: + (self trimArgumentsIn: callee). + calleeParameters size = aSendNode arguments size ifFalse: [ ^ nil ]. + callee := callee copy. + + self checkForFlagIn: callee. + + assignDirectReturnAndExitVarBlock value: + (self setDirectReturnAndExitVar: aSendNode). + + self + propagateReturnTypeDirectReturn: directReturn + exitVar: exitVar + callee: callee. + self + propagateArgumentTypeArguments: calleeParameters + in: callee + from: aSendNode. + + callee + renameVarsForInliningInto: currentMethod + except: omittedParameters + in: codeGenerator. + callee renameLabelsForInliningInto: currentMethod. + currentMethod + addVarsDeclarationsAndLabelsOf: callee + except: omittedParameters. + + callee hasReturn ifTrue: [ + directReturn ifFalse: [ + exitLabel := currentMethod unusedLabelForInliningInto: + currentMethod. + (callee exitVar: nil label: exitLabel) + ifTrue: [ currentMethod labels add: exitLabel ] + ifFalse: [ "is label used?" exitLabel := nil ] ] ]. + + self + moveDownReturnsAndAssignmentsFor: aSendNode + including: callee + resetInfoBlock: assignDirectReturnAndExitVarBlock. + + + (inlineStmts := OrderedCollection new: + callee statements size + callee args size + 2) + add: (label := TLabeledCommentNode new setComment: 'begin ' , sel); + addAll: (currentMethod + argAssignmentsFor: callee + send: aSendNode + except: omittedParameters + in: codeGenerator); + addAll: callee statements. "method body" + exitLabel + ifNotNil: [ + inlineStmts add: + (TLabeledCommentNode new setLabel: exitLabel comment: 'end ' , sel) ] + ifNil: [ + inlineStmts add: (TLabeledCommentNode new setComment: 'end ' , sel) ]. + inlineStmts := TStatementListNode statements: inlineStmts. + + directReturn ifTrue: [ + callee endsWithReturn ifTrue: [ + callee parseTree parent: aSendNode parent. + exitVar ifNotNil: [ inlineStmts := inlineStmts copyWithoutReturn ] ] ]. + + inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" + inlineStmts statements: OrderedCollection new ]. + ^ inlineStmts +] + +{ #category : 'transformation' } +SLInliner >> invert: aSendNode replacement: aTMethod [ + + aSendNode parent replaceChild: aSendNode with: aTMethod parseTree +] + +{ #category : 'inlining-decision' } +SLInliner >> isConditionalToBeTransformedForAssignment: aSend [ + "Answer if a send is of the form + e1 + ifTrue: [e2 ifTrue: [self m1] ifFalse: [self m2]] + ifFalse: [self m3] + such that at least one of the sends mN may be inlined.." + + ^ (#( #ifTrue:ifFalse: #ifFalse:ifTrue: ) includes: aSend selector) + and: [ + aSend arguments anySatisfy: [ :arg | + | stmt | + self assert: arg isStatementList. + arg statements size > 1 or: [ + (stmt := arg firstNonCommentStatement) isSwitch or: [ + stmt isSend and: [ + (codeGenerator mayInline: stmt selector) or: [ + self isConditionalToBeTransformedForAssignment: stmt ] ] ] ] ] ] +] + +{ #category : 'inlining-decision' } +SLInliner >> isInlineableConditional: aSendNode [ + "Answer if the given send node is of the form aSend [ifTrue:|ifFalse:] [statements] + where the method for aSend is marked as inline and all returns within it answer booleans." + + | method | + ^ (#( ifTrue: ifFalse: ) includes: aSendNode selector) and: [ + aSendNode receiver isSend and: [ + (method := codeGenerator anyMethodNamed: + aSendNode receiver selector) notNil and: [ + method inline == true and: [ + method parseTree lastNonCommentStatement isReturn and: [ + method parseTree allSatisfy: [ :node | + node isReturn not or: [ + node expression isConstant and: [ + #( true false ) includes: node expression value ] ] ] ] ] ] ] ] +] + +{ #category : 'inlining-decision' } +SLInliner >> isInlineableFunctionCall: aNode [ + "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted." + + codeGenerator maybeBreakForTestToInline: aNode in: self. + aNode isSend ifFalse: [ ^ false ]. + ^ (codeGenerator methodNamed: aNode selector) + ifNil: [ + aNode asTransformedConstantPerform + ifNil: [ self isInlineableConditional: aNode ] + ifNotNil: [ :n | self isInlineableFunctionCall: n ] ] + ifNotNil: [ :m | + (m ~~ currentMethod and: [ + ((m isFunctionalIn: codeGenerator) or: [ + m mustBeInlined and: [ m isComplete ] ]) and: [ + m mayBeInlined and: [ + (codeGenerator mayInline: m selector) and: [ + aNode arguments allSatisfy: [ :a | + self isSubstitutableNode: a intoMethod: m ] ] ] ] ]) or: [ + m checkForRequiredInlinability ] ] +] + +{ #category : 'inlining-decision' } +SLInliner >> isInlineableSend: aNode [ + "Answer if the given expresssion node is a call to a method that can be inlined." + + | m | + codeGenerator maybeBreakForTestToInline: aNode in: self. + aNode isSend ifFalse: [ ^ false ]. + m := codeGenerator methodNamed: aNode selector. "nil if builtin or external function" + ^ m isNotNil and: [ + m ~~ self and: [ + m mayBeInlined and: [ + (m isComplete and: [ codeGenerator mayInline: m selector ]) or: [ + m checkForRequiredInlinability ] ] ] ] +] + +{ #category : 'inlining-decision' } +SLInliner >> isSubstitutableNode: aNode intoMethod: targetMeth [ + "Answer true if the given parameter node is either a constant, a local variable, or a formal parameter of the receiver. Such parameter nodes may be substituted directly into the body of the method during inlining. Note that global variables cannot be subsituted into methods with possible side effects (i.e., methods that may assign to global variables) because the inlined method might depend on having the value of the global variable captured when it is passed in as an argument." + + | var | + aNode isConstant ifTrue: [ ^ true ]. + + aNode isVariable ifTrue: [ + var := aNode name. + ((currentMethod locals includes: var) or: [ + currentMethod args includes: var ]) ifTrue: [ ^ true ]. + var = #self ifTrue: [ ^ true ]. + (targetMeth maySubstituteGlobal: var in: codeGenerator) ifTrue: [ + ^ true ] ]. + + "For now allow literal blocks to be substituted. They better be accessed only + with value[:value:*] messages though!" + aNode isStatementList ifTrue: [ ^ true ]. + + (aNode isSend and: [ + aNode isStructAccessorWithNoSideEffectIn: codeGenerator ]) ifTrue: [ + ^ true ]. + + "scan expression tree; must contain only constants, builtin ops, and inlineable vars" + aNode nodesDo: [ :node | + node isSend ifTrue: [ + ((codeGenerator isBuiltinSelector: node selector) or: [ + node isStructAccessorWithNoSideEffectIn: codeGenerator ]) + ifFalse: [ ^ false ] ]. + node isVariable ifTrue: [ + var := node name. + ((currentMethod locals includes: var) or: [ + (currentMethod args includes: var) or: [ + var = #self or: [ + targetMeth maySubstituteGlobal: var in: codeGenerator ] ] ]) + ifFalse: [ ^ false ] ]. + (node isConstant or: [ node isVariable or: [ node isSend ] ]) + ifFalse: [ ^ false ] ]. + + ^ true +] + +{ #category : 'transformation' } +SLInliner >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ + + | needToResetData | + "move returns to the lowest part of the ast including in aTMethodReplacement" + self invert: aSendNode replacement: aTMethodReplacement. + needToResetData := sLNodeAnnotatorVisitor removeReturningParentFrom: + aSendNode. + self revert: aSendNode replacement: aTMethodReplacement. + needToResetData ifTrue: [ + aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. + + "move assignments to the lowest part of the ast including in callee" + self invert: aSendNode replacement: aTMethodReplacement. + needToResetData := sLNodeAnnotatorVisitor removeAssigningParentFrom: + aSendNode. + self revert: aSendNode replacement: aTMethodReplacement. + needToResetData ifFalse: [ ^ self ]. + aBlock value: (self setDirectReturnAndExitVar: aSendNode) +] + +{ #category : 'transformation' } +SLInliner >> propagateArgumentTypeArguments: calleeParameters in: callee from: aSendNode [ + "Propagate any unusual argument types to untyped argument variables" + + calleeParameters with: aSendNode arguments do: [ :formal :actual | + (callee declarationAt: formal ifAbsent: nil) ifNil: [ + | type | + (actual isVariable and: [ + (type := currentMethod typeFor: actual name in: codeGenerator) + notNil ]) ifTrue: [ + type ~= #sqInt ifTrue: [ + callee declarationAt: formal put: (type last = $* + ifTrue: [ type , formal ] + ifFalse: [ type , ' ' , formal ]) ] ] ] ] +] + +{ #category : 'transformation' } +SLInliner >> propagateReturnTypeDirectReturn: directReturn exitVar: exitVar callee: callee [ + "Propagate the return type of an inlined method" + + | exitType | + (directReturn or: [ exitVar notNil ]) ifFalse: [ ^ self ]. + exitType := directReturn + ifTrue: [ currentMethod returnType ] + ifFalse: [ + (currentMethod typeFor: exitVar in: codeGenerator) + ifNil: [ #sqInt ] ]. + (exitType = #void or: [ exitType = callee returnType ]) ifFalse: [ + callee propagateReturnIn: codeGenerator ] +] + +{ #category : 'inlining-support-condtional' } +SLInliner >> replaceNodeIn: aSendNode with: data [ + + | replacementTree method | + replacementTree := data at: #replacementTree. + method := data at: #method. + + replacementTree replaceNodesIn: (data at: #map). + replacementTree comment: + { ('inline ' , aSendNode receiver selector) }. + currentMethod + addVarsDeclarationsAndLabelsOf: method + except: method args +] + +{ #category : 'transformation' } +SLInliner >> revert: aSendNode replacement: aTMethod [ + + aTMethod parseTree parent + replaceChild: aTMethod parseTree + with: aSendNode. + aTMethod parseTree parent: aTMethod +] + +{ #category : 'inlining-support' } +SLInliner >> setDirectReturnAndExitVar: aSendNode [ + + | directReturn exitVar | + sLNodeAnnotatorVisitor visit: currentMethod parseTree. + directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: + aSendNode. + exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) + ifNotNil: [ :assignment | assignment variable name ]. + ^ OrderedCollection new + add: directReturn; + add: exitVar; + yourself +] + +{ #category : 'inlining-preparation' } +SLInliner >> statementsListsForInliningInCurrentMethod [ + "Answer a collection of statement list nodes that are candidates for inlining. + Currently, we cannot inline into the argument blocks of and: and or: messages. + We do not want to inline code strings within cCode:inSmalltalk: blocks (those with a + proper block for the cCode: argument are inlined in MessageNode>>asTranslatorNodeIn:). + We do not want to inline code within assert: sends (because we want the assert to read nicely)." + + | stmtLists | + stmtLists := OrderedCollection new: 10. + + currentMethod parseTree + nodesDo: [ :node | + node isStatementList ifTrue: [ stmtLists add: node ] ] + unless: [ :node | + node isSend and: [ + node selector == #cCode:inSmalltalk: or: [ + node selector == #cCall: or: [ + node selector == #cCall:withArguments: or: [ + codeGenerator isAssertSelector: node selector ] ] ] ] ]. + currentMethod parseTree nodesDo: [ :node | + node isSend ifTrue: [ + node selector = #cCode:inSmalltalk: ifTrue: [ + node nodesDo: [ :ccisNode | + stmtLists remove: ccisNode ifAbsent: [ ] ] ]. + node selector = #cCall: ifTrue: [ + node nodesDo: [ :ccisNode | + stmtLists remove: ccisNode ifAbsent: [ ] ] ]. + node selector = #cCall:withArguments: ifTrue: [ + node nodesDo: [ :ccisNode | + stmtLists remove: ccisNode ifAbsent: [ ] ] ]. + (#( #cppIf:ifTrue:ifFalse: #cppIf:ifTrue: ) includes: node selector) + ifTrue: [ + node arguments first nodesDo: [ :inCondNode | + stmtLists remove: inCondNode ifAbsent: [ ] ] ]. + (#( #and: #or: ) includes: node selector) ifTrue: [ "Note: the PP 2.3 compiler produces two arg nodes for these selectors" + stmtLists remove: node arguments first ifAbsent: [ ]. + stmtLists remove: node arguments last ifAbsent: [ ] ]. + (#( #ifTrue: #ifFalse: #ifTrue:ifFalse: #ifFalse:ifTrue: + #ifNil: #ifNotNil: #ifNil:ifNotNil: #ifNotNil:ifNil: ) + includes: node selector) ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ] ]. + (#( whileTrue whileTrue: whilefalse whileFalse: ) includes: + node selector) ifTrue: [ "Allow inlining if it is a [...] whileTrue/whileFalse. + This is identified by having more than one statement in the + receiver block in which case the C code wouldn't work anyways" + node receiver sizeWithoutComments = 1 ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ] ] ]. + node selector = #to:do: ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ]. + stmtLists remove: node args first ifAbsent: [ ] ]. + node selector = #to:by:do: ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ]. + stmtLists remove: node arguments first ifAbsent: [ ]. + stmtLists remove: node arguments second ifAbsent: [ ] ] ]. + node isCaseStmt ifTrue: [ "don't inline cases" + node cases do: [ :case | stmtLists remove: case ifAbsent: [ ] ] ] ]. + ^ stmtLists +] + +{ #category : 'inlining-preparation' } +SLInliner >> transformConditionalAssignment: node [ + "If possible answer the transformation of code of the form + var := e1 + ifTrue: [e2 ifTrue: [self m1] ifFalse: [self m2]] + ifFalse: [self m3] + into + e1 + ifTrue: [e2 ifTrue: [var := self m1] ifFalse: [var := self m2]] + ifFalse: [var := self m3] + to allow inlining of m1, m2, et al. Otherwise answer nil." + + | expr | + ^ (node isAssignment and: [ + (expr := node expression) isSend and: [ + (#( #ifTrue:ifFalse: #ifFalse:ifTrue: ) includes: expr selector) + and: [ self isConditionalToBeTransformedForAssignment: expr ] ] ]) + ifTrue: [ + expr copy + arguments: (expr arguments collect: [ :stmtList | + stmtList copy assignLastExpressionTo: node variable ]); + yourself ] +] + +{ #category : 'transformation' } +SLInliner >> trimArgumentsIn: aTMethod [ + + | calleeParameters omittedParameters varNode | + calleeParameters := aTMethod args. + omittedParameters := #( ). + (calleeParameters notEmpty and: [ + calleeParameters first beginsWith: 'self_in_' ]) ifFalse: [ + ^ OrderedCollection new + add: omittedParameters; + add: calleeParameters; + yourself ]. + "If the first arg is not used we can and should elide it." + varNode := TVariableNode new setName: calleeParameters first. + (aTMethod parseTree noneSatisfy: [ :node | varNode isSameAs: node ]) + ifTrue: [ omittedParameters := { calleeParameters first } ]. + + calleeParameters := calleeParameters allButFirst. + ^ OrderedCollection new + add: omittedParameters; + add: calleeParameters; + yourself +] + +{ #category : 'inlining' } +SLInliner >> tryToInlineMethodExpressionsInCurrentMethod [ + "Expand any (complete) inline methods sent by this method as receivers or parameters. + Answer if anything was inlined." + + | sendsToInline | + sendsToInline := Dictionary new: 100. + sLNodeAnnotatorVisitor visit: currentMethod parseTree. + + codeGenerator pushScope: currentMethod while: [ + currentMethod parseTree + nodesDo: [ :node | + (self isInlineableFunctionCall: node) ifTrue: [ + (self inlineFunctionCall: node) ifNotNil: [ :replacement | + sendsToInline at: node put: replacement ] ] ] + unless: [ :node | "Don't inline the arguments to asserts to keep the asserts readable" + node isSend and: [ + node selector == #cCode:inSmalltalk: or: [ + codeGenerator isAssertSelector: node selector ] ] ] ]. + + sendsToInline isEmpty ifTrue: [ ^ false ]. + currentMethod replaceNodesIn: sendsToInline. + sLNodeAnnotatorVisitor cleanInfo. + ^ true +] + +{ #category : 'inlining' } +SLInliner >> tryToInlineMethodStatementsListsInCurrentMethod [ + "Expand any (complete) inline methods sent by this method as top-level statements. + Answer if anything was inlined." + + | stmtLists didSomething newStatements | + didSomething := false. + stmtLists := self statementsListsForInliningInCurrentMethod. + + stmtLists do: [ :stmtList | + newStatements := TStatementListNode statements: + (OrderedCollection new: stmtList statements size). + stmtList statements do: [ :stmt | + (self inlineCodeOrNilForStatement: stmt) + ifNil: [ newStatements addAllLast: { stmt } ] + ifNotNil: [ :inlinedStmts | + didSomething := true. + newStatements addAllLast: inlinedStmts statements ] ]. + stmtList statements: newStatements statements ]. + + sLNodeAnnotatorVisitor cleanInfo. + ^ didSomething +] + +{ #category : 'inlining' } +SLInliner >> tryToInlineMethodsInCurrentMethod [ + "Expand any (complete) inline methods sent by this method. + Set the complete flag when all inlining has been done. + Answer if something was inlined." + + | didSomething | + currentMethod definedAsMacro ifTrue: [ + currentMethod isComplete ifTrue: [ ^ false ]. + ^ currentMethod complete: true ]. + + self ensureConditionalAssignmentsAreTransformedInCurrentMethod. + didSomething := self tryToInlineMethodStatementsListsInCurrentMethod. + didSomething := self tryToInlineMethodExpressionsInCurrentMethod or: [ + didSomething ]. + + didSomething ifTrue: [ currentMethod writtenToGlobalVarsCache: nil ]. + + "marking a method complete is progress" + currentMethod isComplete ifFalse: [ + self checkForCompletenessFor: currentMethod. + currentMethod isComplete ifTrue: [ didSomething := true ] ]. "marking a method complete is progress" + ^ didSomething +] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 23003ad5048..c5c4aa9523a 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -52,12 +52,6 @@ SLInlinerWithAnnotation >> codeGenerator [ ^ codeGenerator ] -{ #category : 'accessing' } -SLInlinerWithAnnotation >> codeGenerator: aCodeGenerator [ - - codeGenerator := aCodeGenerator -] - { #category : 'accessing' } SLInlinerWithAnnotation >> currentMethod [ From 1ec9fbdec68a839e84cee37b96a06d183010cb0e Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 30 Jun 2025 11:16:05 +0200 Subject: [PATCH 16/86] clean up merge --- smalltalksrc/Slang/SLInliner.class.st | 50 +-------------------------- 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 94a00e42cd1..b59467071aa 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -363,9 +363,6 @@ SLInliner >> inlineSend: aSendNode directReturn: directReturn exitVar: exitVar [ self checkForFlagIn: callee. - assignDirectReturnAndExitVarBlock value: - (self setDirectReturnAndExitVar: aSendNode). - self propagateReturnTypeDirectReturn: directReturn exitVar: exitVar @@ -388,7 +385,7 @@ SLInliner >> inlineSend: aSendNode directReturn: directReturn exitVar: exitVar [ directReturn ifFalse: [ exitLabel := currentMethod unusedLabelForInliningInto: currentMethod. - (callee exitVar: nil label: exitLabel) + (callee exitVar: exitVar label: exitLabel) ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. @@ -546,27 +543,6 @@ SLInliner >> isSubstitutableNode: aNode intoMethod: targetMeth [ ^ true ] -{ #category : 'transformation' } -SLInliner >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ - - | needToResetData | - "move returns to the lowest part of the ast including in aTMethodReplacement" - self invert: aSendNode replacement: aTMethodReplacement. - needToResetData := sLNodeAnnotatorVisitor removeReturningParentFrom: - aSendNode. - self revert: aSendNode replacement: aTMethodReplacement. - needToResetData ifTrue: [ - aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. - - "move assignments to the lowest part of the ast including in callee" - self invert: aSendNode replacement: aTMethodReplacement. - needToResetData := sLNodeAnnotatorVisitor removeAssigningParentFrom: - aSendNode. - self revert: aSendNode replacement: aTMethodReplacement. - needToResetData ifFalse: [ ^ self ]. - aBlock value: (self setDirectReturnAndExitVar: aSendNode) -] - { #category : 'transformation' } SLInliner >> propagateArgumentTypeArguments: calleeParameters in: callee from: aSendNode [ "Propagate any unusual argument types to untyped argument variables" @@ -613,30 +589,6 @@ SLInliner >> replaceNodeIn: aSendNode with: data [ except: method args ] -{ #category : 'transformation' } -SLInliner >> revert: aSendNode replacement: aTMethod [ - - aTMethod parseTree parent - replaceChild: aTMethod parseTree - with: aSendNode. - aTMethod parseTree parent: aTMethod -] - -{ #category : 'inlining-support' } -SLInliner >> setDirectReturnAndExitVar: aSendNode [ - - | directReturn exitVar | - sLNodeAnnotatorVisitor visit: currentMethod parseTree. - directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: - aSendNode. - exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) - ifNotNil: [ :assignment | assignment variable name ]. - ^ OrderedCollection new - add: directReturn; - add: exitVar; - yourself -] - { #category : 'inlining-preparation' } SLInliner >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. From 13ec18040a90d28dcd32a207a9db48365091bfca Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 30 Jun 2025 12:03:48 +0200 Subject: [PATCH 17/86] add comments for goTo treatement --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 999fea01452..e9f739b974c 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -446,7 +446,15 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ currentShouldBePartiallyIgnored push: true. self annotateNodeState: aTGotoNode. - + + + "goTo only comes from inlining to keep the execution flow correct where a return was. + since inlining goes through multiple phases, static analysis may become flawed, for example : + 'a := [... methodWithReturn … b]' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... c . goTo l ... l ... a := b]' + 'a := methodeWithMultipleReturn' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... a := c . goTo l ... l ... a := b]' + so after an inlining phases we invalidate goTo generated so that they do not 'indicate an active exit point anymore'" + "the current implementation might be subject to bugs, the tests regardings goTo are all working properly but the AST should be invalidate AFTER an inlining phases and not during which is something we do + so the last line should be moved" aTGotoNode haveBeenMetInInlining ifTrue: [ ^ self ]. searchForStmtListValue pop. searchForStmtListValue push: true. From d20bc7733e6f86e359f8b780ec2cd0c5c2a02f14 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 30 Jun 2025 18:26:51 +0200 Subject: [PATCH 18/86] add a few tests --- .../SLAnnotatorVisitorTest.class.st | 106 +++++++++++++++--- .../SLNodeAnnotatorVisitorTestClass.class.st | 11 ++ 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 956d8b6f220..c824e63881f 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -600,17 +600,13 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ { #category : 'send' } SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ - | tMethod effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet | + | tMethod nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet constant1 firstSend secondSend selfs firstStmtList blockNode | tMethod := ccg methodNamed: #methodWithSendAsStatement. sLNodeAnnotatorVisitor visit: tMethod parseTree. self fillCollectionValue: tMethod. - effectiveConstantExpressionValueSet := self - getValueOfConstantNodeIn: - effectiveExpressionValueCollection. - nonEffectiveExpressionOrStatementSendSet := self getSendNodeIn: nonEffectiveExpressionOrStatementCollection. effectiveExpressionValueSendSet := self getSendNodeIn: @@ -621,20 +617,48 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ equals: 4. self assert: effectiveExpressionValueCollection size equals: 3. - self assert: (effectiveConstantExpressionValueSet includes: 1). + constant1 := (effectiveExpressionValueCollection select: [ :node | + node isConstant ]) first. + selfs := effectiveExpressionValueCollection select: [ :node | + node ~= constant1 ]. + self assert: constant1 value equals: 1. + self assert: selfs size equals: 2. + selfs do: [ :node | self assert: node name equals: 'self' ]. - self assert: nonEffectiveExpressionOrStatementSendSet size equals: 2. - self assert: (nonEffectiveExpressionOrStatementSendSet includes: - ((tMethod parseTree statements at: 1) statements at: 1)). - self assert: (nonEffectiveExpressionOrStatementSendSet includes: - (tMethod parseTree statements at: 2)). + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not + and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ] ] ]) ]. - self assert: effectiveExpressionValueSendSet isEmpty. + firstSend := (tMethod parseTree statements at: 1) statements at: 1. + secondSend := tMethod parseTree statements at: 2. + self assert: nonEffectiveExpressionOrStatementSendSet size equals: 2. + self assert: + (nonEffectiveExpressionOrStatementSendSet includes: firstSend). + self assert: + (nonEffectiveExpressionOrStatementSendSet includes: secondSend). + firstStmtList := tMethod parseTree. self assert: - (nonEffectiveExpressionOrStatementSendSet allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + (nonEffectiveExpressionOrStatementCollection includes: firstStmtList). + blockNode := nonEffectiveExpressionOrStatementCollection reject: [ + :node | + { + firstSend. + secondSend. + firstStmtList } includes: node ]. + self assert: blockNode size equals: 1. + blockNode := blockNode first. + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. self assert: self checkNonEffectiveExpressionOrStatementCollection. self assert: self checkEffectiveExpressionValueCollection @@ -848,3 +872,55 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchAsStatement [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ] ]) ] + +{ #category : 'switch' } +SLAnnotatorVisitorTest >> testMethodWithSwitchWithOtherwiseAsAssignmentExpression [ + + | tMethod effectiveConstantExpressionValueSet assignmentNode | + tMethod := ccg methodNamed: + #methodWithSwitchWithOtherwiseAsAssignmentExpression. + tMethod prepareMethodIn: ccg. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + self fillCollectionValue: tMethod. + + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. + + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 6. + self assert: effectiveExpressionValueCollection size equals: 7. + + self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: (effectiveConstantExpressionValueSet includes: 5). + self assert: (effectiveConstantExpressionValueSet includes: 6). + self assert: (effectiveConstantExpressionValueSet includes: 7). + self assert: (effectiveConstantExpressionValueSet includes: 8). + + assignmentNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]) first. + self assert: ((effectiveExpressionValueCollection select: [ :node | + node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNode ]). + self assert: ((effectiveExpressionValueCollection select: [ :node | + node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNode ]). + + effectiveExpressionValueCollection do: [ :node | + self + assert: (sLNodeAnnotatorVisitor assigningParentFor: node) + equals: assignmentNode ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection. + + self assert: ((effectiveExpressionValueCollection + addAll: nonEffectiveExpressionOrStatementCollection; + yourself) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]) +] diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index ca2c1d5fac7..92e8e650052 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -122,3 +122,14 @@ SLNodeAnnotatorVisitorTestClass >> methodWithSwitchAsStatement [ ([ 4 ] -> [ 6 ]). ([ 5 ] -> [ 7 ]) } ] + +{ #category : 'switch' } +SLNodeAnnotatorVisitorTestClass >> methodWithSwitchWithOtherwiseAsAssignmentExpression [ + + | x | + x := 3 + caseOf: { + ([ 4 ] -> [ 6 ]). + ([ 5 ] -> [ 7 ]) } + otherwise: [ 8 ] +] From 30a4302f85712b533e180b1e9d0686a729644708 Mon Sep 17 00:00:00 2001 From: Renaud Date: Wed, 2 Jul 2025 13:27:18 +0200 Subject: [PATCH 19/86] few more tests --- .../SLAnnotatorVisitorTest.class.st | 94 +++++++++++++++++-- .../SLNodeAnnotatorVisitorTestClass.class.st | 10 ++ 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index c824e63881f..2b6a51d2716 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -667,7 +667,7 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ { #category : 'statement-list' } SLAnnotatorVisitorTest >> testMethodWithStatementList [ - | tMethod nonEffectiveExpressionOrStatementConstantSet constant11 | + | tMethod nonEffectiveExpressionOrStatementConstantSet constant11 returnNode blockNodeInReturn constant10 | tMethod := ccg methodNamed: #methodWithStatementList. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -696,18 +696,30 @@ SLAnnotatorVisitorTest >> testMethodWithStatementList [ self assert: (nonEffectiveExpressionOrStatementConstantSet includes: 10). - self assert: - ((nonEffectiveExpressionOrStatementCollection select: [ :node | - node isConstant and: [ node value = 10 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ] ]). + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. + constant10 := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isConstant and: [ node value = 10 ] ]) + first. + blockNodeInReturn := returnNode expression. + self assert: (nonEffectiveExpressionOrStatementCollection includes: + blockNodeInReturn). + { + blockNodeInReturn. + constant10 } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ]) ]. self assert: ((nonEffectiveExpressionOrStatementCollection select: [ :node | - node isConstant and: [ node value ~= 10 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not ] ]). + node ~= blockNodeInReturn and: [ node ~= constant10 ] ]) + allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInReturn: node) not ]). + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ]) ]. constant11 := effectiveExpressionValueCollection first. self assert: constant11 value equals: 11. @@ -924,3 +936,65 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchWithOtherwiseAsAssignmentExpressio (sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ] ]) ] + +{ #category : 'switch' } +SLAnnotatorVisitorTest >> testMethodWithSwitchWithOtherwiseAsReturnExpression [ + + | tMethod nonEffectiveExpressionOrStatementConstantSet effectiveConstantExpressionValueSet firstStmtList | + tMethod := ccg methodNamed: + #methodWithSwitchWithOtherwiseAsReturnExpression. + tMethod prepareMethodIn: ccg. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + self fillCollectionValue: tMethod. + + nonEffectiveExpressionOrStatementConstantSet := self + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. + effectiveConstantExpressionValueSet := self + getValueOfConstantNodeIn: + effectiveExpressionValueCollection. + + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 6. + self assert: effectiveExpressionValueCollection size equals: 6. + + self assert: (effectiveConstantExpressionValueSet includes: 3). + self assert: (effectiveConstantExpressionValueSet includes: 4). + self assert: (effectiveConstantExpressionValueSet includes: 5). + self assert: (effectiveConstantExpressionValueSet includes: 6). + self assert: (effectiveConstantExpressionValueSet includes: 7). + self assert: (effectiveConstantExpressionValueSet includes: 8). + + self assert: ((effectiveExpressionValueCollection select: [ :node | + node isConstant and: [ node value ~= 3 ] ]) allSatisfy: [ :node | + sLNodeAnnotatorVisitor isInReturn: node "and: [ + sLNodeAnnotatorVisitor isEffectiveReturnValue: node ]" ]). + self assert: ((effectiveExpressionValueCollection select: [ :node | + node isConstant and: [ node value = 3 ] ]) allSatisfy: [ :node | + sLNodeAnnotatorVisitor isInReturn: node "and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not ]" ]). + + firstStmtList := tMethod parseTree. + + self assert: + ((nonEffectiveExpressionOrStatementCollection select: [ :node | + node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ + :node | sLNodeAnnotatorVisitor isInReturn: node ]). + + self assert: + ((nonEffectiveExpressionOrStatementCollection reject: [ :node | + node isReturn not and: node ~= firstStmtList ]) allSatisfy: [ + :node | (sLNodeAnnotatorVisitor isInReturn: node) not ]). + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection. + + self assert: ((effectiveExpressionValueCollection + addAll: nonEffectiveExpressionOrStatementCollection; + yourself) allSatisfy: [ :node | + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ]) +] diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index 92e8e650052..035ae7d8ce6 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -133,3 +133,13 @@ SLNodeAnnotatorVisitorTestClass >> methodWithSwitchWithOtherwiseAsAssignmentExpr ([ 5 ] -> [ 7 ]) } otherwise: [ 8 ] ] + +{ #category : 'switch' } +SLNodeAnnotatorVisitorTestClass >> methodWithSwitchWithOtherwiseAsReturnExpression [ + + ^ 3 + caseOf: { + ([ 4 ] -> [ 6 ]). + ([ 5 ] -> [ 7 ]) } + otherwise: [ 8 ] +] From d9e446148358501321e8c108cfda6b2a8ecd6ac5 Mon Sep 17 00:00:00 2001 From: Renaud Date: Wed, 2 Jul 2025 13:28:43 +0200 Subject: [PATCH 20/86] correct test --- smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 2b6a51d2716..df2ccf8a628 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -714,7 +714,8 @@ SLAnnotatorVisitorTest >> testMethodWithStatementList [ ((nonEffectiveExpressionOrStatementCollection select: [ :node | node ~= blockNodeInReturn and: [ node ~= constant10 ] ]) allSatisfy: [ :node | - (sLNodeAnnotatorVisitor isInReturn: node) not ]). + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]). nonEffectiveExpressionOrStatementCollection do: [ :node | self assert: From 78f37406e7bde5f0b5f10c321ff2378d36d080c9 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 4 Jul 2025 15:29:31 +0200 Subject: [PATCH 21/86] correct a potentiel bug with recursion + correcte documentation --- smalltalksrc/Slang/SLInliner.class.st | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index b59467071aa..4c51e677ce3 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -466,7 +466,8 @@ SLInliner >> isInlineableConditional: aSendNode [ { #category : 'inlining-decision' } SLInliner >> isInlineableFunctionCall: aNode [ - "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted." + "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted. + if a method is marked as always inlined and complete, the method also returns true" codeGenerator maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. @@ -495,7 +496,7 @@ SLInliner >> isInlineableSend: aNode [ aNode isSend ifFalse: [ ^ false ]. m := codeGenerator methodNamed: aNode selector. "nil if builtin or external function" ^ m isNotNil and: [ - m ~~ self and: [ + m ~~ currentMethod and: [ m mayBeInlined and: [ (m isComplete and: [ codeGenerator mayInline: m selector ]) or: [ m checkForRequiredInlinability ] ] ] ] From 4c9c55c0151bc402b7a7ee07446880c25889a4dd Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 4 Jul 2025 18:47:53 +0200 Subject: [PATCH 22/86] start to optimising the visitor usage --- .../SLAnnotatorVisitorTest.class.st | 9 + .../Slang/SLInlinerWithAnnotation.class.st | 79 ++++--- .../Slang/SLNodeAnnotatorVisitor.class.st | 218 ++++++++++++++---- smalltalksrc/Slang/TMethod.class.st | 34 ++- smalltalksrc/Slang/TParseNode.class.st | 18 ++ 5 files changed, 272 insertions(+), 86 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index df2ccf8a628..7bda3949330 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -625,6 +625,15 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsStatement [ self assert: selfs size equals: 2. selfs do: [ :node | self assert: node name equals: 'self' ]. + selfs := effectiveExpressionValueCollection select: [ :node | + node isVariable ]. + selfs do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInSend: node) ]. + + constant1 := (effectiveExpressionValueCollection select: [ :node | + node isConstant ]) first. + self assert: (sLNodeAnnotatorVisitor isInSend: constant1). + effectiveExpressionValueCollection do: [ :node | self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index c5c4aa9523a..1d8fbaaa680 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -9,6 +9,21 @@ Class { #package : 'Slang' } +{ #category : 'inlining-preparation' } +SLInlinerWithAnnotation >> annotateCurrentMethod [ + + sLNodeAnnotatorVisitor visit: currentMethod parseTree +] + +{ #category : 'inlining-decision' } +SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod [ + + ((sLNodeAnnotatorVisitor isInSend: aNode) and: [ + aTMethod hasMultipleReturns or: [ aTMethod hasGoTo ] ]) ifTrue: [ + ^ false ]. + ^ true +] + { #category : 'testing' } SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ "Set the complete flag if the parse tree contains no further candidates for inlining." @@ -17,7 +32,7 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ codeGenerator maybeBreakForTestOfInliningOf: aTMethod selector. foundIncompleteSend := false. - + self annotateCurrentMethod. aTMethod parseTree nodesDo: [ :node | node isSend ifTrue: [ @@ -33,7 +48,7 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ node isSend and: [ node selector == #cCode:inSmalltalk: or: [ codeGenerator isAssertSelector: node selector ] ] ]. - + sLNodeAnnotatorVisitor cleanInfo. foundIncompleteSend ifFalse: [ aTMethod complete: true ] ] @@ -43,7 +58,7 @@ SLInlinerWithAnnotation >> checkForFlagIn: aTMethod [ (aTMethod parseTree sizeWithoutComments > 1 and: [ aTMethod parseTree firstNonCommentStatement isSend and: [ aTMethod parseTree firstNonCommentStatement selector == #flag: ] ]) - ifTrue: [ aTMethod parseTree removeFirst ] + ifTrue: [ aTMethod parseTree removeFirstNonCommentStatement ] ] { #category : 'accessing' } @@ -127,6 +142,7 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." + self annotateCurrentMethod. (aNode isReturn and: [ self isInlineableSend: aNode expression ]) ifTrue: [ ^ self inlineSend: aNode expression ]. @@ -415,11 +431,6 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ inlineStmts add: (TLabeledCommentNode new setComment: 'end ' , sel) ]. inlineStmts := TStatementListNode statements: inlineStmts. - directReturn ifTrue: [ - callee endsWithReturn ifTrue: [ - callee parseTree parent: aSendNode parent. - exitVar ifNotNil: [ inlineStmts := inlineStmts copyWithoutReturn ] ] ]. - inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" inlineStmts statements: OrderedCollection new ]. ^ inlineStmts @@ -471,16 +482,32 @@ SLInlinerWithAnnotation >> isInlineableConditional: aSendNode [ { #category : 'inlining-decision' } SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode [ - "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted." + ^ self isInlineableFunctionCall: aNode transformedConstantFrom: nil +] + +{ #category : 'inlining-decision' } +SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFrom: anOtherNodeOrNil [ + "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted. + if a method is marked as always inlined and complete, the method also returns true" + + | nodeForVisitorInfo | codeGenerator maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. + nodeForVisitorInfo := anOtherNodeOrNil + ifNil: [ aNode ] + ifNotNil: [ anOtherNodeOrNil ]. ^ (codeGenerator methodNamed: aNode selector) ifNil: [ aNode asTransformedConstantPerform ifNil: [ self isInlineableConditional: aNode ] - ifNotNil: [ :n | self isInlineableFunctionCall: n ] ] + ifNotNil: [ :n | "when going through transformation, the new node n take the place of aNode but not in the visitor so we keep track of the old node " + self + isInlineableFunctionCall: n + transformedConstantFrom: nodeForVisitorInfo ] ] ifNotNil: [ :m | + (self canBeInlineInExpression: nodeForVisitorInfo replacement: m) + ifFalse: [ ^ false ]. (m ~~ currentMethod and: [ ((m isFunctionalIn: codeGenerator) or: [ m mustBeInlined and: [ m isComplete ] ]) and: [ @@ -499,11 +526,13 @@ SLInlinerWithAnnotation >> isInlineableSend: aNode [ codeGenerator maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. m := codeGenerator methodNamed: aNode selector. "nil if builtin or external function" - ^ m isNotNil and: [ - m ~~ self and: [ - m mayBeInlined and: [ - (m isComplete and: [ codeGenerator mayInline: m selector ]) or: [ - m checkForRequiredInlinability ] ] ] ] + m ifNil: [ ^ false ]. + (self canBeInlineInExpression: aNode replacement: m) ifFalse: [ + ^ false ]. + ^ m ~~ currentMethod and: [ + m mayBeInlined and: [ + (m isComplete and: [ codeGenerator mayInline: m selector ]) or: [ + m checkForRequiredInlinability ] ] ] ] { #category : 'inlining-decision' } @@ -526,15 +555,15 @@ SLInlinerWithAnnotation >> isSubstitutableNode: aNode intoMethod: targetMeth [ aNode isStatementList ifTrue: [ ^ true ]. (aNode isSend and: [ - aNode isStructAccessorWithNoSideEffectIn: codeGenerator ]) ifTrue: [ + codeGenerator isStructAccessorWithNoSideEffect: aNode ]) ifTrue: [ ^ true ]. "scan expression tree; must contain only constants, builtin ops, and inlineable vars" aNode nodesDo: [ :node | node isSend ifTrue: [ ((codeGenerator isBuiltinSelector: node selector) or: [ - node isStructAccessorWithNoSideEffectIn: codeGenerator ]) - ifFalse: [ ^ false ] ]. + codeGenerator isStructAccessorWithNoSideEffect: node ]) ifFalse: [ + ^ false ] ]. node isVariable ifTrue: [ var := node name. ((currentMethod locals includes: var) or: [ @@ -558,15 +587,14 @@ SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including aSendNode. self revert: aSendNode replacement: aTMethodReplacement. needToResetData ifTrue: [ + self annotateCurrentMethod. aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. "move assignments to the lowest part of the ast including in callee" self invert: aSendNode replacement: aTMethodReplacement. - needToResetData := sLNodeAnnotatorVisitor removeAssigningParentFrom: - aSendNode. - self revert: aSendNode replacement: aTMethodReplacement. - needToResetData ifFalse: [ ^ self ]. - aBlock value: (self setDirectReturnAndExitVar: aSendNode) + "the visitor is not re used after for in this call of inlineSend: so we do not revisit the AST" + sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode. + self revert: aSendNode replacement: aTMethodReplacement ] { #category : 'transformation' } @@ -628,7 +656,6 @@ SLInlinerWithAnnotation >> revert: aSendNode replacement: aTMethod [ SLInlinerWithAnnotation >> setDirectReturnAndExitVar: aSendNode [ | directReturn exitVar | - sLNodeAnnotatorVisitor visit: currentMethod parseTree. directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: aSendNode. exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) @@ -754,8 +781,8 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ | sendsToInline | sendsToInline := Dictionary new: 100. - sLNodeAnnotatorVisitor visit: currentMethod parseTree. + self annotateCurrentMethod. codeGenerator pushScope: currentMethod while: [ currentMethod parseTree nodesDo: [ :node | @@ -769,7 +796,7 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ sendsToInline isEmpty ifTrue: [ ^ false ]. currentMethod replaceNodesIn: sendsToInline. - sLNodeAnnotatorVisitor cleanInfo. + ^ true ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index e9f739b974c..d68c96a42d4 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -4,6 +4,7 @@ Class { #instVars : [ 'info', 'inExpressionStack', + 'inSendStack', 'searchForAssignValueStack', 'searchForReturnValueStack', 'searchForExpressionValueStack', @@ -14,7 +15,13 @@ Class { #package : 'Slang' } -{ #category : 'helpers-annotate' } +{ #category : 'helpers-stack-operation' } +SLNodeAnnotatorVisitor >> activateCurrentSouldBePartiallyIgnored [ + + currentShouldBePartiallyIgnored := true +] + +{ #category : 'annotate' } SLNodeAnnotatorVisitor >> addAssignmentInfoFor: aNode [ "when an assignment has been met, the effective exitVar is not all of the nodes in it, often it is just a single node (or more if conditionals) the nodes have a link to theirs assignment true or nil on the stack indicate if the node is the effective exitVar" @@ -25,7 +32,7 @@ SLNodeAnnotatorVisitor >> addAssignmentInfoFor: aNode [ constantString: self assignmentString ] -{ #category : 'helpers-annotate' } +{ #category : 'annotate' } SLNodeAnnotatorVisitor >> addReturnInfoFor: aNode [ "mark the given node has being inside a return expression, it doesn't mean that the node itself is the exit value true or false on the stack only indicate if the node is the effective value of the returning expression or not" @@ -36,45 +43,63 @@ SLNodeAnnotatorVisitor >> addReturnInfoFor: aNode [ constantString: self returnString ] -{ #category : 'helpers-annotate' } +{ #category : 'annotate' } SLNodeAnnotatorVisitor >> addToEffectiveExpressionOrNot: aNode [ | entry isAnEffectiveExpression | entry := info at: aNode. isAnEffectiveExpression := false. - ((self topOfBooleanStack: currentShouldBePartiallyIgnored) not and: [ - (self topOfBooleanStack: searchForExpressionValueStack) or: [ + ((currentShouldBePartiallyIgnored) not and: [ + (self topOfStack: searchForExpressionValueStack) or: [ self isEffectiveExpression: aNode ] ]) ifTrue: [ isAnEffectiveExpression := true ]. entry at: self expressionString put: isAnEffectiveExpression ] -{ #category : 'helpers-annotate' } +{ #category : 'annotate' } SLNodeAnnotatorVisitor >> addToInExpression: aNode [ - inExpressionStack isEmpty ifTrue: [ ^ self ]. - (info at: aNode) at: self inExpressionString put: true + self + addToInStack: aNode + stack: inExpressionStack + entry: self inExpressionString +] + +{ #category : 'annotate' } +SLNodeAnnotatorVisitor >> addToInSend: aNode [ + + self addToInStack: aNode stack: inSendStack entry: self inSendString ] { #category : 'helpers-annotate' } +SLNodeAnnotatorVisitor >> addToInStack: aNode stack: aStack entry: aString [ + + aStack isEmpty ifTrue: [ ^ self ]. + (info at: aNode) at: aString put: true +] + +{ #category : 'annotate' } SLNodeAnnotatorVisitor >> annotateNodeState: aNode [ + | top | self ensureEntryInInfoDictFor: aNode. self addToInExpression: aNode. + self addToInSend: aNode. self addReturnInfoFor: aNode. self addAssignmentInfoFor: aNode. self addToEffectiveExpressionOrNot: aNode. - (self topOfBooleanStack: currentShouldBePartiallyIgnored) ifTrue: [ - currentShouldBePartiallyIgnored pop. + currentShouldBePartiallyIgnored ifTrue: [ + self deActivateCurrentSouldBePartiallyIgnored. ^ self ]. - (self topOfBooleanStack: searchForStmtListValue) ifFalse: [ ^ self ]. - searchForStmtListValue pop. - searchForStmtListValue push: false + (self topOfStack: searchForStmtListValue) value ifFalse: [ + ^ self ]. + top := searchForStmtListValue pop. + searchForStmtListValue push: false -> top value ] { #category : 'accessing' } @@ -89,7 +114,7 @@ SLNodeAnnotatorVisitor >> assignmentString [ ^ #assignment ] -{ #category : 'helpers' } +{ #category : 'helpers-accessing-info' } SLNodeAnnotatorVisitor >> associationKeyInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -98,7 +123,7 @@ SLNodeAnnotatorVisitor >> associationKeyInfoFor: aNode entry: aString [ ifAbsent: [ false ] ] -{ #category : 'helpers' } +{ #category : 'helpers-accessing-info' } SLNodeAnnotatorVisitor >> associationValueInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -107,7 +132,7 @@ SLNodeAnnotatorVisitor >> associationValueInfoFor: aNode entry: aString [ ifAbsent: [ nil ] ] -{ #category : 'helpers' } +{ #category : 'helpers-accessing-info' } SLNodeAnnotatorVisitor >> booleanInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -116,12 +141,25 @@ SLNodeAnnotatorVisitor >> booleanInfoFor: aNode entry: aString [ ifAbsent: [ false ] ] +{ #category : 'cleanup' } +SLNodeAnnotatorVisitor >> cleanBranchInfoFrom: aNode [ + + aNode children do: [ :child | + child nodesDo: [ :node | info at: node put: IdentityDictionary new ] ] +] + { #category : 'cleanup' } SLNodeAnnotatorVisitor >> cleanInfo [ self initialize ] +{ #category : 'helpers-stack-operation' } +SLNodeAnnotatorVisitor >> deActivateCurrentSouldBePartiallyIgnored [ + + currentShouldBePartiallyIgnored := false +] + { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ @@ -134,6 +172,12 @@ SLNodeAnnotatorVisitor >> expressionString [ ^ #expression ] +{ #category : 'helpers-string-constant' } +SLNodeAnnotatorVisitor >> fromStatementListString [ + + ^ #fromStatementList +] + { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ "re give the informations for each branch after using it" @@ -155,7 +199,7 @@ SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ "conditionals are control-flow node and thus treated differently from the other sends" - currentShouldBePartiallyIgnored push: true. + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. self pushInExpressionStack. @@ -176,6 +220,12 @@ SLNodeAnnotatorVisitor >> inExpressionString [ ^ #inExpression ] +{ #category : 'helpers-string-constant' } +SLNodeAnnotatorVisitor >> inSendString [ + + ^ #inSend +] + { #category : 'initialization' } SLNodeAnnotatorVisitor >> initialize [ @@ -183,21 +233,25 @@ SLNodeAnnotatorVisitor >> initialize [ "empty, true or false on top depending of if the parent needs an expression as a dependance" searchForExpressionValueStack := Stack new. - "either empty or have somethin on top depending if the children are in the expression of one of their parents" + "either empty or have something on top depending if the children are in the expression of one of their parents" inExpressionStack := Stack new. - "empty or true/false -> returningParent on top depending of if the parent has found its return value or not" + "similar to inExpression but specific to send" + inSendStack := Stack new. + + "empty or true/false -> returningParent on top depending on if the parent has found its return value or not" searchForReturnValueStack := Stack new. - "empty or true/false -> assigningParent on top depending of if the parent has found its assigning value or not" + "empty or true/false -> assigningParent on top depending on if the parent has found its assigning value or not" searchForAssignValueStack := Stack new. - "StmtList throught jump can end up having multiple value point, this stack is re updated when crossing goToNode and is a boolean stack" + "StmtList through jump can end up having multiple value point, this stack is re updated when crossing goToNode. + it has true/false -> true/false on top depending on if we are currently searching for one of the values the second boolean indicate if the value of the stmtList is use in the first place so we do not re update the stack if not necessary" searchForStmtListValue := Stack new. "when searching for the deepest node that give the expression value, some nodes can be pass through like conditionals and stmtLists or statement only node (goTos, comments or labels) inactivate the search... stack" - currentShouldBePartiallyIgnored := Stack new. + currentShouldBePartiallyIgnored := false. "collect all the informations gathered for a given node" info := IdentityDictionary new @@ -241,7 +295,13 @@ SLNodeAnnotatorVisitor >> isInReturn: aNode [ ^ self presenceInfoFor: aNode entry: self returnString ] -{ #category : 'helpers' } +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> isInSend: aNode [ + + ^ self presenceInfoFor: aNode entry: self inSendString +] + +{ #category : 'helpers-accessing-info' } SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -259,10 +319,10 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri assoc := aStack top copy. entry := info at: aNode. - (self topOfBooleanStack: searchForStmtListValue) ifTrue: [ + (self topOfStack: searchForStmtListValue) key ifTrue: [ assoc := true -> assoc value ]. - (self topOfBooleanStack: currentShouldBePartiallyIgnored) + currentShouldBePartiallyIgnored ifTrue: [ assoc := false -> assoc value ] ifFalse: [ aStack pop. @@ -271,13 +331,20 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri entry at: aString put: assoc ] -{ #category : 'helpers' } +{ #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> pushInExpressionStack [ "value doesn't matter for this stack " inExpressionStack push: true ] +{ #category : 'helpers-stack-operation' } +SLNodeAnnotatorVisitor >> pushInSendStack [ + "value doesn't matter for this stack " + + inSendStack push: true +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated @@ -288,8 +355,7 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ assignment ifNil: [ ^ false ]. "necessary because in inlining we switched the send node with its replacement" - self cleanInfo. - self visit: assignment tMethod parseTree. + self visitFromBranchStartingAt: aNode. self transformToAssignmentFrom: assignment assignment: assignment. assignment parent @@ -301,27 +367,60 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ ] { #category : 'transformation' } -SLNodeAnnotatorVisitor >> removeReturningParentFrom: aSendNode [ +SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ "use in inlining for redondant return : return {return exp} -> {return exp}, clean all the info as a side effect, it needs to be recalculated answer if anything was done" | returningParent | - (self isEffectiveReturnValue: aSendNode) ifFalse: [ ^ false ]. + returningParent := self returningParentFor: aNode. + returningParent ifNil: [ ^ false ]. - returningParent := self returningParentFor: aSendNode. "necessary because in inlining we switched the send node with its replacement" - self cleanInfo. - self visit: returningParent tMethod parseTree. + self visitFromBranchStartingAt: aNode. self transformDirectReturnFrom: returningParent. returningParent parent replaceChild: returningParent with: returningParent expression. - self cleanInfo. + self visitFromBranchStartingAt: returningParent parent. ^ true ] +{ #category : 'helpers-stack-restoration' } +SLNodeAnnotatorVisitor >> restoreInSomeThingStacksFrom: aNode [ + + (self presenceInfoFor: aNode entry: self inExpressionString) ifTrue: [ + self pushInExpressionStack ]. + + (self presenceInfoFor: aNode entry: self inSendString) ifTrue: [ + self pushInSendStack ] +] + +{ #category : 'helpers-stack-restoration' } +SLNodeAnnotatorVisitor >> restoreSearchForSimpleInfoStacksFrom: aNode [ + + (self isInReturn: aNode) ifTrue: [ + searchForReturnValueStack push: + (self isEffectiveReturnValue: aNode) + -> (self returningParentFor: aNode) ]. + + (self isInAssignment: aNode) ifTrue: [ + searchForReturnValueStack push: + (self isEffectiveAssignmentValue: aNode) + -> (self assigningParentFor: aNode) ] +] + +{ #category : 'visiting-main-API-helpers' } +SLNodeAnnotatorVisitor >> restoreStackStateFrom: aNode [ + "to just revisit a branch and not the whole AST, we need to restore the different stacks using the info dictionary" + + self restoreInSomeThingStacksFrom: aNode. + self restoreSearchForSimpleInfoStacksFrom: aNode. + self searchSomeThingStacksCollection. + 1 halt +] + { #category : 'helpers-string-constant' } SLNodeAnnotatorVisitor >> returnString [ @@ -334,6 +433,17 @@ SLNodeAnnotatorVisitor >> returningParentFor: aNode [ ^ self associationValueInfoFor: aNode entry: self returnString ] +{ #category : 'helpers-stack-restoration' } +SLNodeAnnotatorVisitor >> searchSomeThingStacksCollection [ + + | stackCollection | + stackCollection := OrderedCollection new. + stackCollection + add: searchForAssignValueStack -> self assignmentString; + add: searchForReturnValueStack -> self returnString. + ^ stackCollection +] + { #category : 'accessing' } SLNodeAnnotatorVisitor >> stacksForSpecificValueHolder [ @@ -342,8 +452,8 @@ SLNodeAnnotatorVisitor >> stacksForSpecificValueHolder [ searchForReturnValueStack } ] -{ #category : 'helpers' } -SLNodeAnnotatorVisitor >> topOfBooleanStack: aStack [ +{ #category : 'helpers-stack-operation' } +SLNodeAnnotatorVisitor >> topOfStack: aStack [ "return the top value of a boolean stack" ^ aStack isNotEmpty and: [ aStack top ] @@ -381,7 +491,7 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assi self transformToAssignmentFrom: child assignment: assignment ] ] ] -{ #category : 'visiting' } +{ #category : 'visiting-main-API' } SLNodeAnnotatorVisitor >> visit: aNode [ aNode accept: self. @@ -422,7 +532,7 @@ SLNodeAnnotatorVisitor >> visitBraceCaseNode: aTBraceCaseNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ - currentShouldBePartiallyIgnored push: true. + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aCaseStmtNode. searchForExpressionValueStack push: true. @@ -441,13 +551,21 @@ SLNodeAnnotatorVisitor >> visitConstantNode: aConstantNode [ self annotateNodeState: aConstantNode ] +{ #category : 'visiting-main-API' } +SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ + + self cleanBranchInfoFrom: aNode. + self restoreStackStateFrom: aNode. + self visit: aNode +] + { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ - currentShouldBePartiallyIgnored push: true. + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aTGotoNode. - - + + "goTo only comes from inlining to keep the execution flow correct where a return was. since inlining goes through multiple phases, static analysis may become flawed, for example : 'a := [... methodWithReturn … b]' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... c . goTo l ... l ... a := b]' @@ -456,8 +574,8 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ "the current implementation might be subject to bugs, the tests regardings goTo are all working properly but the AST should be invalidate AFTER an inlining phases and not during which is something we do so the last line should be moved" aTGotoNode haveBeenMetInInlining ifTrue: [ ^ self ]. - searchForStmtListValue pop. - searchForStmtListValue push: true. + searchForStmtListValue top value ifTrue: [ + searchForStmtListValue push: true -> true ]. aTGotoNode metInInlining ] @@ -472,7 +590,7 @@ SLNodeAnnotatorVisitor >> visitInlineNode: anInlineNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitLabeledCommentNode: aLabeledCommentNode [ - currentShouldBePartiallyIgnored push: true. + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aLabeledCommentNode ] @@ -504,12 +622,14 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ searchForExpressionValueStack push: true. self pushInExpressionStack. + self pushInSendStack. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. searchForExpressionValueStack pop. - inExpressionStack pop + inExpressionStack pop. + inSendStack pop ] { #category : 'visiting' } @@ -517,17 +637,17 @@ SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ | stmts needExitValue | stmts := aStatementsListNode statements. - needExitValue := self topOfBooleanStack: + needExitValue := self topOfStack: searchForExpressionValueStack. searchForExpressionValueStack push: false. - searchForStmtListValue push: needExitValue. + searchForStmtListValue push: needExitValue -> needExitValue. stmts reverse do: [ :stmt | stmt accept: self ]. searchForExpressionValueStack pop. searchForStmtListValue pop. - currentShouldBePartiallyIgnored push: true. + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aStatementsListNode ] @@ -535,7 +655,7 @@ SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ | caseStatementsAndOtherWiseOrNilCollection | - currentShouldBePartiallyIgnored push: true. + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSwitchStatementNode. caseStatementsAndOtherWiseOrNilCollection := aSwitchStatementNode diff --git a/smalltalksrc/Slang/TMethod.class.st b/smalltalksrc/Slang/TMethod.class.st index 292786bee69..18b672cade8 100644 --- a/smalltalksrc/Slang/TMethod.class.st +++ b/smalltalksrc/Slang/TMethod.class.st @@ -1275,6 +1275,18 @@ TMethod >> functionAttributes: aString [ functionAttributes := aString ] +{ #category : 'testing' } +TMethod >> hasGoTo [ + + ^ self parseTree hasGoTo +] + +{ #category : 'testing' } +TMethod >> hasMultipleReturns [ + + ^ self parseTree hasMultipleReturns +] + { #category : 'testing' } TMethod >> hasProperties [ ^properties notNil and: [properties notEmpty] @@ -1698,24 +1710,24 @@ TMethod >> inlineSend: aSendNode directReturn: directReturn exitVar: exitVar in: { #category : 'inlining' } TMethod >> inlineableFunctionCall: aNode in: aCodeGen [ - - "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted." + "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted. + if a method is marked as always inlined and complete, the method also returns true" aCodeGen maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. ^ (aCodeGen methodNamed: aNode selector) - ifNil: [ + ifNil: [ aNode asTransformedConstantPerform ifNil: [ self isInlineableConditional: aNode in: aCodeGen ] ifNotNil: [ :n | self inlineableFunctionCall: n in: aCodeGen ] ] - ifNotNil: [ :m | - (m ~~ self and: [ - ((m isFunctionalIn: aCodeGen) or: [ - m mustBeInlined and: [ m isComplete ] ]) and: [ - m mayBeInlined and: [ - (aCodeGen mayInline: m selector) and: [ - aNode arguments allSatisfy: [ :a | - self isSubstitutableNode: a intoMethod: m in: aCodeGen ] ] ] ] ]) + ifNotNil: [ :m | + (m ~~ self and: [ + ((m isFunctionalIn: aCodeGen) or: [ + m mustBeInlined and: [ m isComplete ] ]) and: [ + m mayBeInlined and: [ + (aCodeGen mayInline: m selector) and: [ + aNode arguments allSatisfy: [ :a | + self isSubstitutableNode: a intoMethod: m in: aCodeGen ] ] ] ] ]) or: [ m checkForRequiredInlinability ] ] ] diff --git a/smalltalksrc/Slang/TParseNode.class.st b/smalltalksrc/Slang/TParseNode.class.st index dd87a4fb172..a528fbe0e05 100644 --- a/smalltalksrc/Slang/TParseNode.class.st +++ b/smalltalksrc/Slang/TParseNode.class.st @@ -209,6 +209,24 @@ TParseNode >> hasExplicitReturn [ ^false ] +{ #category : 'utilities' } +TParseNode >> hasGoTo [ + + self nodesDo: [ :node | node isGoTo ifTrue: [ ^ true ] ]. + ^ false +] + +{ #category : 'utilities' } +TParseNode >> hasMultipleReturns [ + + | returnCount | + returnCount := 0. + self nodesDo: [ :node | + node isReturn ifTrue: [ returnCount := returnCount + 1 ]. + returnCount > 1 ifTrue: [ ^ true ] ]. + ^ false +] + { #category : 'testing' } TParseNode >> hasSideEffect [ "Answer if the parse tree rooted at this node has a side-effect or not. By default assume it has. Nodes that don't override." From 22e3bd6545a89cce92ddf18abf3c113f451779b3 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 7 Jul 2025 17:31:55 +0200 Subject: [PATCH 23/86] fix a bug and advance in optimizing the visitor --- smalltalksrc/Slang/CCodeGenerator.class.st | 2 +- .../Slang/SLInlinerWithAnnotation.class.st | 15 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 174 +++++++++++++----- 3 files changed, 141 insertions(+), 50 deletions(-) diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index 2ccd39926d9..92b9ed73910 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -1029,7 +1029,7 @@ CCodeGenerator >> doBasicInlining: inlineFlagOrSymbol [ | pass progress inliner | self collectInlineList: inlineFlagOrSymbol. - inliner := SLInliner new + inliner := SLInlinerWithAnnotation new codeGenerator: self; yourself. pass := 0. diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 1d8fbaaa680..bd9db8a3099 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -506,15 +506,16 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr isInlineableFunctionCall: n transformedConstantFrom: nodeForVisitorInfo ] ] ifNotNil: [ :m | - (self canBeInlineInExpression: nodeForVisitorInfo replacement: m) - ifFalse: [ ^ false ]. (m ~~ currentMethod and: [ ((m isFunctionalIn: codeGenerator) or: [ m mustBeInlined and: [ m isComplete ] ]) and: [ m mayBeInlined and: [ (codeGenerator mayInline: m selector) and: [ aNode arguments allSatisfy: [ :a | - self isSubstitutableNode: a intoMethod: m ] ] ] ] ]) or: [ + (self isSubstitutableNode: a intoMethod: m) and: [ + self + canBeInlineInExpression: nodeForVisitorInfo + replacement: m ] ] ] ] ] ]) or: [ m checkForRequiredInlinability ] ] ] @@ -527,11 +528,11 @@ SLInlinerWithAnnotation >> isInlineableSend: aNode [ aNode isSend ifFalse: [ ^ false ]. m := codeGenerator methodNamed: aNode selector. "nil if builtin or external function" m ifNil: [ ^ false ]. - (self canBeInlineInExpression: aNode replacement: m) ifFalse: [ - ^ false ]. ^ m ~~ currentMethod and: [ m mayBeInlined and: [ - (m isComplete and: [ codeGenerator mayInline: m selector ]) or: [ + (m isComplete and: [ + (codeGenerator mayInline: m selector) and: [ + self canBeInlineInExpression: aNode replacement: m ] ]) or: [ m checkForRequiredInlinability ] ] ] ] @@ -592,7 +593,7 @@ SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including "move assignments to the lowest part of the ast including in callee" self invert: aSendNode replacement: aTMethodReplacement. - "the visitor is not re used after for in this call of inlineSend: so we do not revisit the AST" + "the visitor is not re used after for in this call of inlineSend: so we do not set aBlock value" sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode. self revert: aSendNode replacement: aTMethodReplacement ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index d68c96a42d4..dfa741ad062 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -46,18 +46,40 @@ SLNodeAnnotatorVisitor >> addReturnInfoFor: aNode [ { #category : 'annotate' } SLNodeAnnotatorVisitor >> addToEffectiveExpressionOrNot: aNode [ - | entry isAnEffectiveExpression | + | entry isAnEffectiveExpression isEffectivelyIgnored | entry := info at: aNode. isAnEffectiveExpression := false. + isEffectivelyIgnored := false. - ((currentShouldBePartiallyIgnored) not and: [ + (currentShouldBePartiallyIgnored not and: [ (self topOfStack: searchForExpressionValueStack) or: [ self isEffectiveExpression: aNode ] ]) ifTrue: [ isAnEffectiveExpression := true ]. + (searchForStmtListValue isNotEmpty and: [ + searchForStmtListValue top key ]) ifTrue: [ + isAnEffectiveExpression := true ]. + + "for stacks restoration" + (currentShouldBePartiallyIgnored and: [ + self topOfStack: searchForExpressionValueStack ]) ifTrue: [ + isEffectivelyIgnored := true ]. + + entry at: self isEffectivelyIgnoredString put: isEffectivelyIgnored. entry at: self expressionString put: isAnEffectiveExpression ] +{ #category : 'annotate' } +SLNodeAnnotatorVisitor >> addToFromSearchStmtList: aNode [ + "for stacks restoration only, indicate if we are in a statement list and if we are indicate if the statement list was a used expression" + + (info at: aNode) + at: self fromSearchStmtListString + put: (searchForStmtListValue isEmpty + ifTrue: [ nil ] + ifFalse: [ searchForStmtListValue top value ]) +] + { #category : 'annotate' } SLNodeAnnotatorVisitor >> addToInExpression: aNode [ @@ -93,15 +115,28 @@ SLNodeAnnotatorVisitor >> annotateNodeState: aNode [ self addAssignmentInfoFor: aNode. self addToEffectiveExpressionOrNot: aNode. + self addToFromSearchStmtList: aNode. + currentShouldBePartiallyIgnored ifTrue: [ self deActivateCurrentSouldBePartiallyIgnored. ^ self ]. - (self topOfStack: searchForStmtListValue) value ifFalse: [ - ^ self ]. + + searchForStmtListValue isEmpty ifTrue: [ ^ self ]. top := searchForStmtListValue pop. searchForStmtListValue push: false -> top value ] +{ #category : 'visiting-main-API-helpers' } +SLNodeAnnotatorVisitor >> assertStacksAreEmpty [ + + self assert: searchForAssignValueStack isEmpty. + self assert: searchForReturnValueStack isEmpty. + self assert: searchForExpressionValueStack isEmpty. + self assert: inExpressionStack isEmpty. + self assert: inSendStack isEmpty. + self assert: searchForStmtListValue isEmpty +] + { #category : 'accessing' } SLNodeAnnotatorVisitor >> assigningParentFor: aNode [ @@ -154,6 +189,12 @@ SLNodeAnnotatorVisitor >> cleanInfo [ self initialize ] +{ #category : 'cleanup' } +SLNodeAnnotatorVisitor >> cleanInfoFrom: aNode [ + + info at: aNode put: IdentityDictionary new +] + { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> deActivateCurrentSouldBePartiallyIgnored [ @@ -173,9 +214,10 @@ SLNodeAnnotatorVisitor >> expressionString [ ] { #category : 'helpers-string-constant' } -SLNodeAnnotatorVisitor >> fromStatementListString [ +SLNodeAnnotatorVisitor >> fromSearchStmtListString [ + "use for restoration only" - ^ #fromStatementList + ^ #fromSearchStmtList ] { #category : 'helpers-visiting' } @@ -277,6 +319,19 @@ SLNodeAnnotatorVisitor >> isEffectiveReturnValue: aNode [ ^ self associationKeyInfoFor: aNode entry: self returnString ] +{ #category : 'testing-stack-restoration' } +SLNodeAnnotatorVisitor >> isEffectivelyIgnored: aNode [ + "for stack restoration" + + ^ self booleanInfoFor: aNode entry: self isEffectivelyIgnoredString +] + +{ #category : 'helpers-string-constant' } +SLNodeAnnotatorVisitor >> isEffectivelyIgnoredString [ + + ^ #isEffectivelyIgnored +] + { #category : 'testing' } SLNodeAnnotatorVisitor >> isInAssignment: aNode [ @@ -319,7 +374,8 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri assoc := aStack top copy. entry := info at: aNode. - (self topOfStack: searchForStmtListValue) key ifTrue: [ + (searchForStmtListValue isNotEmpty and: [ + searchForStmtListValue top key ]) ifTrue: [ assoc := true -> assoc value ]. currentShouldBePartiallyIgnored @@ -353,16 +409,18 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ | assignment | assignment := self assigningParentFor: aNode. assignment ifNil: [ ^ false ]. + 1 haltIf: [ aNode tMethod selector = #methodAReturnBlockAssignment ]. "necessary because in inlining we switched the send node with its replacement" - self visitFromBranchStartingAt: aNode. + self visitFromBranchStartingAt: aNode parent. self transformToAssignmentFrom: assignment assignment: assignment. assignment parent replaceChild: assignment with: assignment expression. + self cleanInfoFrom: assignment. - self cleanInfo. + self visitFromBranchStartingAt: assignment parent. ^ true ] @@ -374,15 +432,16 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ | returningParent | returningParent := self returningParentFor: aNode. returningParent ifNil: [ ^ false ]. - + 1 haltIf: [ aNode tMethod selector = #methodAReturnBlockAssignment ]. "necessary because in inlining we switched the send node with its replacement" - self visitFromBranchStartingAt: aNode. + self visitFromBranchStartingAt: aNode parent. self transformDirectReturnFrom: returningParent. returningParent parent replaceChild: returningParent with: returningParent expression. - + self cleanInfoFrom: returningParent. + self visitFromBranchStartingAt: returningParent parent. ^ true ] @@ -390,35 +449,74 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ { #category : 'helpers-stack-restoration' } SLNodeAnnotatorVisitor >> restoreInSomeThingStacksFrom: aNode [ + | restoredStacks | + restoredStacks := OrderedCollection new. (self presenceInfoFor: aNode entry: self inExpressionString) ifTrue: [ - self pushInExpressionStack ]. + self pushInExpressionStack. + restoredStacks add: inExpressionStack ]. (self presenceInfoFor: aNode entry: self inSendString) ifTrue: [ - self pushInSendStack ] + self pushInSendStack. + restoredStacks add: inSendStack ]. + + ^ restoredStacks ] { #category : 'helpers-stack-restoration' } SLNodeAnnotatorVisitor >> restoreSearchForSimpleInfoStacksFrom: aNode [ + | restoredStacks | + restoredStacks := OrderedCollection new. (self isInReturn: aNode) ifTrue: [ searchForReturnValueStack push: (self isEffectiveReturnValue: aNode) - -> (self returningParentFor: aNode) ]. + -> (self returningParentFor: aNode). + restoredStacks add: searchForReturnValueStack ]. (self isInAssignment: aNode) ifTrue: [ - searchForReturnValueStack push: + searchForAssignValueStack push: (self isEffectiveAssignmentValue: aNode) - -> (self assigningParentFor: aNode) ] + -> (self assigningParentFor: aNode). + restoredStacks add: searchForAssignValueStack ]. + + ^ restoredStacks +] + +{ #category : 'helpers-stack-restoration' } +SLNodeAnnotatorVisitor >> restoreSearchSomeThingStacksFrom: aNode [ + + | entry restoredStacks | + restoredStacks := OrderedCollection new. + ((self isEffectiveExpression: aNode) or: [ + self isEffectivelyIgnored: aNode ]) ifTrue: [ + searchForExpressionValueStack push: true. + restoredStacks add: searchForExpressionValueStack ]. + + entry := (info at: aNode) at: self fromSearchStmtListString. + entry ifNil: [ ^ restoredStacks ]. + searchForStmtListValue push: + (self topOfStack: searchForExpressionValueStack) -> entry. + searchForExpressionValueStack isNotEmpty + ifTrue: [ searchForExpressionValueStack pop ] + ifFalse: [ restoredStacks add: searchForExpressionValueStack ]. + searchForExpressionValueStack push: false. + restoredStacks add: searchForStmtListValue. + + ^ restoredStacks ] { #category : 'visiting-main-API-helpers' } SLNodeAnnotatorVisitor >> restoreStackStateFrom: aNode [ "to just revisit a branch and not the whole AST, we need to restore the different stacks using the info dictionary" - self restoreInSomeThingStacksFrom: aNode. - self restoreSearchForSimpleInfoStacksFrom: aNode. - self searchSomeThingStacksCollection. - 1 halt + | restoredStacks | + restoredStacks := OrderedCollection new. + restoredStacks addAll: (self restoreInSomeThingStacksFrom: aNode). + restoredStacks addAll: + (self restoreSearchForSimpleInfoStacksFrom: aNode). + restoredStacks addAll: (self restoreSearchSomeThingStacksFrom: aNode). +" 1 halt." + ^ restoredStacks ] { #category : 'helpers-string-constant' } @@ -433,17 +531,6 @@ SLNodeAnnotatorVisitor >> returningParentFor: aNode [ ^ self associationValueInfoFor: aNode entry: self returnString ] -{ #category : 'helpers-stack-restoration' } -SLNodeAnnotatorVisitor >> searchSomeThingStacksCollection [ - - | stackCollection | - stackCollection := OrderedCollection new. - stackCollection - add: searchForAssignValueStack -> self assignmentString; - add: searchForReturnValueStack -> self returnString. - ^ stackCollection -] - { #category : 'accessing' } SLNodeAnnotatorVisitor >> stacksForSpecificValueHolder [ @@ -467,7 +554,9 @@ SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ parentNode children do: [ :child | (self isEffectiveReturnValue: child) ifTrue: [ - child parent replaceChild: child with: child asReturnNode ] + 1 haltIf: [ + child tMethod selector = #methodAReturnBlockAssignment ]. + child parent replaceChild: child with: child asReturnNode ] ifFalse: [ child isReturn ifTrue: [ "already got a return for this branch" ^ self ]. @@ -495,11 +584,7 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assi SLNodeAnnotatorVisitor >> visit: aNode [ aNode accept: self. - self assert: searchForAssignValueStack isEmpty. - self assert: searchForReturnValueStack isEmpty. - self assert: searchForExpressionValueStack isEmpty. - self assert: inExpressionStack isEmpty. - self assert: searchForStmtListValue isEmpty + self assertStacksAreEmpty ] { #category : 'visiting' } @@ -554,9 +639,13 @@ SLNodeAnnotatorVisitor >> visitConstantNode: aConstantNode [ { #category : 'visiting-main-API' } SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ + | restoredStacks | self cleanBranchInfoFrom: aNode. - self restoreStackStateFrom: aNode. - self visit: aNode + restoredStacks := self restoreStackStateFrom: aNode. + self cleanInfoFrom: aNode. + aNode accept: self. + restoredStacks do: [ :stack | stack pop ]. + self assertStacksAreEmpty ] { #category : 'visiting' } @@ -664,11 +753,12 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ aSwitchStatementNode otherwiseOrNil ifNotNil: [ :o | caseStatementsAndOtherWiseOrNilCollection add: o ]. - searchForExpressionValueStack push: true. - self pushInExpressionStack. self giveStacksInfoForMultipleBranches: caseStatementsAndOtherWiseOrNilCollection. + searchForExpressionValueStack push: true. + self pushInExpressionStack. + aSwitchStatementNode cases do: [ :case | | caseLabels | caseLabels := case first. From 5f5a3b98942fa8a99e7487a85bf320c54f39ed5a Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 8 Jul 2025 11:44:15 +0200 Subject: [PATCH 24/86] reuse stable version + advance --- smalltalksrc/Slang/CCodeGenerator.class.st | 2 +- .../Slang/SLInlinerWithAnnotation.class.st | 8 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 127 +++++++++++++----- 3 files changed, 99 insertions(+), 38 deletions(-) diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index 92b9ed73910..2ccd39926d9 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -1029,7 +1029,7 @@ CCodeGenerator >> doBasicInlining: inlineFlagOrSymbol [ | pass progress inliner | self collectInlineList: inlineFlagOrSymbol. - inliner := SLInlinerWithAnnotation new + inliner := SLInliner new codeGenerator: self; yourself. pass := 0. diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index bd9db8a3099..9457fbc9dec 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -582,19 +582,19 @@ SLInlinerWithAnnotation >> isSubstitutableNode: aNode intoMethod: targetMeth [ SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ | needToResetData | - "move returns to the lowest part of the ast including in aTMethodReplacement" + "move assignments to the lowest part of the ast including in callee" self invert: aSendNode replacement: aTMethodReplacement. - needToResetData := sLNodeAnnotatorVisitor removeReturningParentFrom: + needToResetData := sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode. self revert: aSendNode replacement: aTMethodReplacement. needToResetData ifTrue: [ self annotateCurrentMethod. aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. - "move assignments to the lowest part of the ast including in callee" + "move returns to the lowest part of the ast including in aTMethodReplacement" self invert: aSendNode replacement: aTMethodReplacement. "the visitor is not re used after for in this call of inlineSend: so we do not set aBlock value" - sLNodeAnnotatorVisitor removeAssigningParentFrom: aSendNode. + sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode. self revert: aSendNode replacement: aTMethodReplacement ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index dfa741ad062..b97189e0a3c 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -30,6 +30,7 @@ SLNodeAnnotatorVisitor >> addAssignmentInfoFor: aNode [ processValueSourceFor: aNode from: searchForAssignValueStack constantString: self assignmentString + ignoredString: self isEffectivelyIgnoredInAssignmentString ] { #category : 'annotate' } @@ -41,6 +42,7 @@ SLNodeAnnotatorVisitor >> addReturnInfoFor: aNode [ processValueSourceFor: aNode from: searchForReturnValueStack constantString: self returnString + ignoredString: self isEffectivelyIgnoredInReturnString ] { #category : 'annotate' } @@ -143,7 +145,7 @@ SLNodeAnnotatorVisitor >> assigningParentFor: aNode [ ^ self associationValueInfoFor: aNode entry: self assignmentString ] -{ #category : 'helpers-string-constant' } +{ #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> assignmentString [ ^ #assignment @@ -192,7 +194,7 @@ SLNodeAnnotatorVisitor >> cleanInfo [ { #category : 'cleanup' } SLNodeAnnotatorVisitor >> cleanInfoFrom: aNode [ - info at: aNode put: IdentityDictionary new + info removeKey: aNode ] { #category : 'helpers-stack-operation' } @@ -207,13 +209,13 @@ SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ info at: aNode ifAbsentPut: [ IdentityDictionary new ] ] -{ #category : 'helpers-string-constant' } +{ #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> expressionString [ ^ #expression ] -{ #category : 'helpers-string-constant' } +{ #category : 'helpers-info-entry-stack-restoration' } SLNodeAnnotatorVisitor >> fromSearchStmtListString [ "use for restoration only" @@ -255,14 +257,14 @@ SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ inExpressionStack pop ] -{ #category : 'helpers-string-constant' } +{ #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> inExpressionString [ "value doesn't actually matter, but the stack need elements" ^ #inExpression ] -{ #category : 'helpers-string-constant' } +{ #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> inSendString [ ^ #inSend @@ -326,7 +328,37 @@ SLNodeAnnotatorVisitor >> isEffectivelyIgnored: aNode [ ^ self booleanInfoFor: aNode entry: self isEffectivelyIgnoredString ] -{ #category : 'helpers-string-constant' } +{ #category : 'testing-stack-restoration' } +SLNodeAnnotatorVisitor >> isEffectivelyIgnoredInAssignment: aNode [ + "for stack restoration" + + ^ self + booleanInfoFor: aNode + entry: self isEffectivelyIgnoredInAssignmentString +] + +{ #category : 'helpers-info-entry-stack-restoration' } +SLNodeAnnotatorVisitor >> isEffectivelyIgnoredInAssignmentString [ + + ^ #isEffectivelyIgnoredInAssignment +] + +{ #category : 'testing-stack-restoration' } +SLNodeAnnotatorVisitor >> isEffectivelyIgnoredInReturn: aNode [ + "for stack restoration" + + ^ self + booleanInfoFor: aNode + entry: self isEffectivelyIgnoredInReturnString +] + +{ #category : 'helpers-info-entry-stack-restoration' } +SLNodeAnnotatorVisitor >> isEffectivelyIgnoredInReturnString [ + + ^ #isEffectivelyIgnoredInReturn +] + +{ #category : 'helpers-info-entry-stack-restoration' } SLNodeAnnotatorVisitor >> isEffectivelyIgnoredString [ ^ #isEffectivelyIgnored @@ -387,6 +419,33 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri entry at: aString put: assoc ] +{ #category : 'helpers-annotate' } +SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantString: constantString ignoredString: ignoredString [ + + | entry assoc isEffectivelyIgnored | + aStack isEmpty ifTrue: [ ^ self ]. + + assoc := aStack top copy. + entry := info at: aNode. + isEffectivelyIgnored := false. + + (searchForStmtListValue isNotEmpty and: [ + searchForStmtListValue top key ]) ifTrue: [ + assoc := true -> assoc value ]. + + currentShouldBePartiallyIgnored + ifTrue: [ + assoc key ifTrue: [ + assoc := false -> assoc value. + isEffectivelyIgnored := true ] ] + ifFalse: [ + aStack pop. + aStack push: false -> assoc value ]. + + entry at: ignoredString put: isEffectivelyIgnored. + entry at: constantString put: assoc +] + { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> pushInExpressionStack [ "value doesn't matter for this stack " @@ -447,7 +506,7 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ ] { #category : 'helpers-stack-restoration' } -SLNodeAnnotatorVisitor >> restoreInSomeThingStacksFrom: aNode [ +SLNodeAnnotatorVisitor >> restoreInStacksFrom: aNode [ | restoredStacks | restoredStacks := OrderedCollection new. @@ -463,27 +522,7 @@ SLNodeAnnotatorVisitor >> restoreInSomeThingStacksFrom: aNode [ ] { #category : 'helpers-stack-restoration' } -SLNodeAnnotatorVisitor >> restoreSearchForSimpleInfoStacksFrom: aNode [ - - | restoredStacks | - restoredStacks := OrderedCollection new. - (self isInReturn: aNode) ifTrue: [ - searchForReturnValueStack push: - (self isEffectiveReturnValue: aNode) - -> (self returningParentFor: aNode). - restoredStacks add: searchForReturnValueStack ]. - - (self isInAssignment: aNode) ifTrue: [ - searchForAssignValueStack push: - (self isEffectiveAssignmentValue: aNode) - -> (self assigningParentFor: aNode). - restoredStacks add: searchForAssignValueStack ]. - - ^ restoredStacks -] - -{ #category : 'helpers-stack-restoration' } -SLNodeAnnotatorVisitor >> restoreSearchSomeThingStacksFrom: aNode [ +SLNodeAnnotatorVisitor >> restoreSearchFoGeneralValueStacksFrom: aNode [ | entry restoredStacks | restoredStacks := OrderedCollection new. @@ -505,21 +544,43 @@ SLNodeAnnotatorVisitor >> restoreSearchSomeThingStacksFrom: aNode [ ^ restoredStacks ] +{ #category : 'helpers-stack-restoration' } +SLNodeAnnotatorVisitor >> restoreSearchForSpecificValueStacksFrom: aNode [ + + | restoredStacks | + restoredStacks := OrderedCollection new. + (self isInReturn: aNode) ifTrue: [ + searchForReturnValueStack push: + ((self isEffectiveReturnValue: aNode) or: [ + self isEffectivelyIgnoredInReturn: aNode ]) + -> (self returningParentFor: aNode). + restoredStacks add: searchForReturnValueStack ]. + + ((self isInAssignment: aNode) or: [ + self isEffectivelyIgnoredInAssignment: aNode ]) ifTrue: [ + searchForAssignValueStack push: + (self isEffectiveAssignmentValue: aNode) + -> (self assigningParentFor: aNode). + restoredStacks add: searchForAssignValueStack ]. + + ^ restoredStacks +] + { #category : 'visiting-main-API-helpers' } SLNodeAnnotatorVisitor >> restoreStackStateFrom: aNode [ "to just revisit a branch and not the whole AST, we need to restore the different stacks using the info dictionary" | restoredStacks | restoredStacks := OrderedCollection new. - restoredStacks addAll: (self restoreInSomeThingStacksFrom: aNode). + restoredStacks addAll: (self restoreInStacksFrom: aNode). restoredStacks addAll: - (self restoreSearchForSimpleInfoStacksFrom: aNode). - restoredStacks addAll: (self restoreSearchSomeThingStacksFrom: aNode). + (self restoreSearchForSpecificValueStacksFrom: aNode). + restoredStacks addAll: (self restoreSearchFoGeneralValueStacksFrom: aNode). " 1 halt." ^ restoredStacks ] -{ #category : 'helpers-string-constant' } +{ #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> returnString [ ^ #return From f18143092e54a9e85ff0b951cecbf2b1552e2bbd Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 8 Jul 2025 17:01:44 +0200 Subject: [PATCH 25/86] optimizing the visitor almost done --- .../Slang/SLInlinerWithAnnotation.class.st | 6 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 110 +++++++++--------- 2 files changed, 57 insertions(+), 59 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 9457fbc9dec..fa3189b54e4 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -588,13 +588,15 @@ SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including aSendNode. self revert: aSendNode replacement: aTMethodReplacement. needToResetData ifTrue: [ - self annotateCurrentMethod. + sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode parent. aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. "move returns to the lowest part of the ast including in aTMethodReplacement" self invert: aSendNode replacement: aTMethodReplacement. "the visitor is not re used after for in this call of inlineSend: so we do not set aBlock value" sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode. + sLNodeAnnotatorVisitor cleanBranchInfoFrom: + aTMethodReplacement parseTree parent. self revert: aSendNode replacement: aTMethodReplacement ] @@ -821,6 +823,8 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ newStatements addAllLast: inlinedStmts statements ] ]. stmtList statements: newStatements statements ]. + currentMethod parseTree nodesDo: [ :node | + node isGoTo ifTrue: [ node metInInlining ] ]. sLNodeAnnotatorVisitor cleanInfo. ^ didSomething ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index b97189e0a3c..dc52e5eb52b 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -182,7 +182,8 @@ SLNodeAnnotatorVisitor >> booleanInfoFor: aNode entry: aString [ SLNodeAnnotatorVisitor >> cleanBranchInfoFrom: aNode [ aNode children do: [ :child | - child nodesDo: [ :node | info at: node put: IdentityDictionary new ] ] + child nodesDo: [ :node | + info at: node ifPresent: [ info removeKey: node ] ] ] ] { #category : 'cleanup' } @@ -397,28 +398,6 @@ SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ ifAbsent: [ false ] ] -{ #category : 'helpers-annotate' } -SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantString: aString [ - - | entry assoc | - aStack isEmpty ifTrue: [ ^ self ]. - - assoc := aStack top copy. - entry := info at: aNode. - - (searchForStmtListValue isNotEmpty and: [ - searchForStmtListValue top key ]) ifTrue: [ - assoc := true -> assoc value ]. - - currentShouldBePartiallyIgnored - ifTrue: [ assoc := false -> assoc value ] - ifFalse: [ - aStack pop. - aStack push: false -> assoc value ]. - - entry at: aString put: assoc -] - { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantString: constantString ignoredString: ignoredString [ @@ -429,12 +408,19 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri entry := info at: aNode. isEffectivelyIgnored := false. - (searchForStmtListValue isNotEmpty and: [ - searchForStmtListValue top key ]) ifTrue: [ - assoc := true -> assoc value ]. + + searchForStmtListValue isNotEmpty ifTrue: [ + searchForStmtListValue top key ifTrue: [ + assoc := true -> assoc value ]. + searchForStmtListValue top value ifTrue: [ + isEffectivelyIgnored := true ] ]. currentShouldBePartiallyIgnored ifTrue: [ + "1 haltIf: [ + aNode tMethod isNotNil and: [ + aNode tMethod selector + = #methodAInlineIfFalseReturningIfTrueInAssignement ] ]". assoc key ifTrue: [ assoc := false -> assoc value. isEffectivelyIgnored := true ] ] @@ -468,8 +454,9 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ | assignment | assignment := self assigningParentFor: aNode. assignment ifNil: [ ^ false ]. - 1 haltIf: [ aNode tMethod selector = #methodAReturnBlockAssignment ]. - +" 1 haltIf: [ + assignment tMethod selector + = #methodAInlineIfFalseReturningIfTrueInAssignement ]." "necessary because in inlining we switched the send node with its replacement" self visitFromBranchStartingAt: aNode parent. self transformToAssignmentFrom: assignment assignment: assignment. @@ -477,9 +464,9 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ assignment parent replaceChild: assignment with: assignment expression. + self cleanInfoFrom: assignment. - self visitFromBranchStartingAt: assignment parent. ^ true ] @@ -491,7 +478,7 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ | returningParent | returningParent := self returningParentFor: aNode. returningParent ifNil: [ ^ false ]. - 1 haltIf: [ aNode tMethod selector = #methodAReturnBlockAssignment ]. + "necessary because in inlining we switched the send node with its replacement" self visitFromBranchStartingAt: aNode parent. self transformDirectReturnFrom: returningParent. @@ -499,9 +486,9 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ returningParent parent replaceChild: returningParent with: returningParent expression. + self cleanInfoFrom: returningParent. - - self visitFromBranchStartingAt: returningParent parent. + ^ true ] @@ -533,12 +520,15 @@ SLNodeAnnotatorVisitor >> restoreSearchFoGeneralValueStacksFrom: aNode [ entry := (info at: aNode) at: self fromSearchStmtListString. entry ifNil: [ ^ restoredStacks ]. - searchForStmtListValue push: - (self topOfStack: searchForExpressionValueStack) -> entry. - searchForExpressionValueStack isNotEmpty - ifTrue: [ searchForExpressionValueStack pop ] - ifFalse: [ restoredStacks add: searchForExpressionValueStack ]. - searchForExpressionValueStack push: false. + entry + ifFalse: [ searchForStmtListValue push: false -> entry ] + ifTrue: [ + searchForStmtListValue push: + (self topOfStack: searchForExpressionValueStack) -> entry. + searchForExpressionValueStack isNotEmpty + ifTrue: [ searchForExpressionValueStack pop ] + ifFalse: [ restoredStacks add: searchForExpressionValueStack ]. + searchForExpressionValueStack push: false ]. restoredStacks add: searchForStmtListValue. ^ restoredStacks @@ -556,10 +546,10 @@ SLNodeAnnotatorVisitor >> restoreSearchForSpecificValueStacksFrom: aNode [ -> (self returningParentFor: aNode). restoredStacks add: searchForReturnValueStack ]. - ((self isInAssignment: aNode) or: [ - self isEffectivelyIgnoredInAssignment: aNode ]) ifTrue: [ + (self isInAssignment: aNode) ifTrue: [ searchForAssignValueStack push: - (self isEffectiveAssignmentValue: aNode) + ((self isEffectiveAssignmentValue: aNode) or: [ + self isEffectivelyIgnoredInAssignment: aNode ]) -> (self assigningParentFor: aNode). restoredStacks add: searchForAssignValueStack ]. @@ -615,9 +605,7 @@ SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ parentNode children do: [ :child | (self isEffectiveReturnValue: child) ifTrue: [ - 1 haltIf: [ - child tMethod selector = #methodAReturnBlockAssignment ]. - child parent replaceChild: child with: child asReturnNode ] + child parent replaceChild: child with: child asReturnNode ] ifFalse: [ child isReturn ifTrue: [ "already got a return for this branch" ^ self ]. @@ -721,12 +709,11 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ 'a := [... methodWithReturn … b]' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... c . goTo l ... l ... a := b]' 'a := methodeWithMultipleReturn' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... a := c . goTo l ... l ... a := b]' so after an inlining phases we invalidate goTo generated so that they do not 'indicate an active exit point anymore'" - "the current implementation might be subject to bugs, the tests regardings goTo are all working properly but the AST should be invalidate AFTER an inlining phases and not during which is something we do - so the last line should be moved" - aTGotoNode haveBeenMetInInlining ifTrue: [ ^ self ]. - searchForStmtListValue top value ifTrue: [ - searchForStmtListValue push: true -> true ]. - aTGotoNode metInInlining + (aTGotoNode haveBeenMetInInlining or: [ + searchForStmtListValue top value not ]) ifTrue: [ ^ self ]. + + searchForStmtListValue pop. + searchForStmtListValue push: true -> true ] { #category : 'visiting' } @@ -785,20 +772,27 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitStatementListNode: aStatementsListNode [ - | stmts needExitValue | + | stmts searchForValue searchForStmtListValueEntry inUsedStmtList | + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aStatementsListNode. + stmts := aStatementsListNode statements. - needExitValue := self topOfStack: - searchForExpressionValueStack. + + searchForStmtListValueEntry := false -> false. + searchForStmtListValue isNotEmpty ifTrue: [ + searchForStmtListValueEntry := searchForStmtListValue top ]. + searchForValue := (self topOfStack: searchForExpressionValueStack) + or: [ searchForStmtListValueEntry key ]. + "if we are going to met a goTo" + inUsedStmtList := searchForStmtListValueEntry value or: + searchForValue. searchForExpressionValueStack push: false. - searchForStmtListValue push: needExitValue -> needExitValue. + searchForStmtListValue push: searchForValue -> inUsedStmtList. stmts reverse do: [ :stmt | stmt accept: self ]. searchForExpressionValueStack pop. - searchForStmtListValue pop. - - self activateCurrentSouldBePartiallyIgnored. - self annotateNodeState: aStatementsListNode + searchForStmtListValue pop ] { #category : 'visiting' } From 3c59090f89021b6448794ee9033d915b44c5cd11 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 8 Jul 2025 17:58:25 +0200 Subject: [PATCH 26/86] remove halts --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index dc52e5eb52b..da24774a803 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -417,10 +417,6 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri currentShouldBePartiallyIgnored ifTrue: [ - "1 haltIf: [ - aNode tMethod isNotNil and: [ - aNode tMethod selector - = #methodAInlineIfFalseReturningIfTrueInAssignement ] ]". assoc key ifTrue: [ assoc := false -> assoc value. isEffectivelyIgnored := true ] ] @@ -454,9 +450,7 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ | assignment | assignment := self assigningParentFor: aNode. assignment ifNil: [ ^ false ]. -" 1 haltIf: [ - assignment tMethod selector - = #methodAInlineIfFalseReturningIfTrueInAssignement ]." + "necessary because in inlining we switched the send node with its replacement" self visitFromBranchStartingAt: aNode parent. self transformToAssignmentFrom: assignment assignment: assignment. From a35d190be39e695175bfba5dd2476e48d07d2870 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 9 Jul 2025 17:55:11 +0200 Subject: [PATCH 27/86] finished optimizing the visitor --- .../SLAnnotatorVisitorTest.class.st | 66 +++++++++- .../SLMockInliningTestClass.class.st | 45 +++++-- .../SLNodeAnnotatorVisitorTestClass.class.st | 6 + .../Slang-Tests/SlangInliningTest.class.st | 118 ++++++++++++++--- smalltalksrc/Slang/SLInliner.class.st | 6 - .../Slang/SLInlinerWithAnnotation.class.st | 83 ++++++------ .../Slang/SLNodeAnnotatorVisitor.class.st | 120 ++++++++++++------ smalltalksrc/Slang/TMethod.class.st | 12 -- smalltalksrc/Slang/TParseNode.class.st | 21 +-- smalltalksrc/Slang/TSendNode.class.st | 6 + 10 files changed, 337 insertions(+), 146 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 7bda3949330..eb46814310b 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -217,7 +217,8 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ (sLNodeAnnotatorVisitor assigningParentFor: node) - = assignmentNode ] ]) ]. + = assignmentNode and: [ + (sLNodeAnnotatorVisitor isInSend: node) not ] ] ]) ]. firstStmtList := tMethod parseTree. @@ -228,7 +229,8 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ (sLNodeAnnotatorVisitor isInAssignment: node) and: [ (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNode and: [ - sLNodeAnnotatorVisitor isInExpression: node ] ] ]). + (sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor isInSend: node) not ] ] ] ]). { firstStmtList. assignmentNode } do: [ :node | @@ -338,6 +340,63 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ self assert: self checkEffectiveExpressionValueCollection ] +{ #category : 'iterative' } +SLAnnotatorVisitorTest >> testMethodWithDo [ + "for this method, the AST is modified by asTranslatorNodeIn: for assignment which push down the assignment inside a block " + + | tMethod nonEffectiveExpressionOrStatementSendSet effectiveExpressionValueSendSet doSend blockNode eVariable doReceiver | + tMethod := ccg methodNamed: #methodWithDo. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + self fillCollectionValue: tMethod. + + nonEffectiveExpressionOrStatementSendSet := self getSendNodeIn: + nonEffectiveExpressionOrStatementCollection. + effectiveExpressionValueSendSet := self getSendNodeIn: + effectiveExpressionValueCollection. + + doSend := tMethod parseTree statements first. + blockNode := doSend arguments first. + eVariable := blockNode statements first. + doReceiver := doSend receiver. + + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 3. + self assert: effectiveExpressionValueCollection size equals: 2. + + self assert: effectiveExpressionValueSendSet isEmpty. + + self assert: nonEffectiveExpressionOrStatementSendSet size equals: 1. + self assert: + (nonEffectiveExpressionOrStatementSendSet includes: doSend). + + + { + doReceiver. + eVariable } do: [ :node | + self assert: (effectiveExpressionValueCollection includes: node) ]. + + self assert: (sLNodeAnnotatorVisitor isInSend: blockNode). + + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]) ]. + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]) ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection +] + { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturn [ @@ -581,7 +640,8 @@ SLAnnotatorVisitorTest >> testMethodWithSendAsExpression [ (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) ]. self assert: - (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: secondSend) not. + ((sLNodeAnnotatorVisitor isEffectiveAssignmentValue: secondSend) not + and: [ sLNodeAnnotatorVisitor isInSend: secondSend ]). effectiveExpressionValueCollection do: [ :node | self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index b18ab910e98..b66c8904eb1 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -130,14 +130,6 @@ SLMockInliningTestClass >> methodAMultipleReturn [ ^ 1 + 1 ] -{ #category : 'inlining-jump' } -SLMockInliningTestClass >> methodAMultipleReturnAlwaysInlined [ - - - self methodBMultipleReturn. - ^ 1 + 1 -] - { #category : 'inlining-jump' } SLMockInliningTestClass >> methodAMultipleReturnAsAssignmentExpression [ @@ -164,6 +156,13 @@ SLMockInliningTestClass >> methodAMultipleReturnExpression [ ^ 1 + 1 ] +{ #category : 'inlining-jump' } +SLMockInliningTestClass >> methodAMultipleReturnInlined [ + + self methodBMultipleReturn. + ^ 1 + 1 +] + { #category : 'inlining-assignment' } SLMockInliningTestClass >> methodAReturnAssignment [ @@ -208,9 +207,27 @@ SLMockInliningTestClass >> methodAWithReturningSendArgumentsInlined [ ] { #category : 'inlining-returning-arguments' } -SLMockInliningTestClass >> methodAWithReturningSendReturningArgumentsInlined [ +SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined [ + + ^ self methodB: self methodAMultipleReturnInlined +] + +{ #category : 'inlining-returning-arguments' } +SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ + + ^ self methodB: {self methodAMultipleReturnInlined} +] + +{ #category : 'inlining-returning-arguments' } +SLMockInliningTestClass >> methodAWithReturningSendWithReturningIfInArgumentsInlined [ - ^ self methodB: self methodAMultipleReturnAlwaysInlined + ^ self methodB: self methodBReturningIfTrueIfFalseInSend +] + +{ #category : 'inlining-returning-arguments' } +SLMockInliningTestClass >> methodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ + + ^ self methodB: self methodBReturningIfTrueIfFalseInSend ] { #category : 'inlining-arguments' } @@ -297,6 +314,14 @@ SLMockInliningTestClass >> methodBReturningIfTrueIfFalse [ ^ self methodB ifTrue: [ 1 ] ifFalse: [ 2 ] ] +{ #category : 'inlining-returning-arguments-helpers' } +SLMockInliningTestClass >> methodBReturningIfTrueIfFalseInSend [ + + ^ self methodB: (self methodB + ifTrue: [ 1 ] + ifFalse: [ 2 ]) +] + { #category : 'inlining-returning-conditional-helpers' } SLMockInliningTestClass >> methodBReturningInlinedIfTrue [ diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index 035ae7d8ce6..1a854e907e0 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -51,6 +51,12 @@ SLNodeAnnotatorVisitorTestClass >> methodWithConstant [ ^ 6 ] +{ #category : 'iterative' } +SLNodeAnnotatorVisitorTestClass >> methodWithDo [ + + #( ) do: [ :e | e ] +] + { #category : 'return' } SLNodeAnnotatorVisitorTestClass >> methodWithReturn [ diff --git a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st index 81828a4e2f9..594881800a6 100644 --- a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st @@ -178,29 +178,19 @@ SlangInliningTest >> test2ChainInliningMethodAWithReturningSendReturningArgument ccg doBasicInlining: true. method := ccg methodNamed: - #methodAWithReturningSendReturningArgumentsInlined. + #methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined. translation := self translate: method. translation := translation trimBoth. - self assert: translation equals: 'incorrect? - /* SLMockInliningTestClass>>#methodAWithReturningSendReturningArgumentsInlined */ + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined */ static sqInt -methodAWithReturningSendReturningArgumentsInlined(void) +methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined(void) { - return methodB((/* begin methodBMultipleReturn */ ((methodB()) - ? (goto l3; -) - : 0), /* begin methodCMultipleReturn */ ((methodC()) - ? (goto l2; -) - : 0), 3 + 3, l2: - ; - /* end methodCMultipleReturn */ -, 2 + 2, l3: - ; - /* end methodBMultipleReturn */ -, 1 + 1)); + return methodB(methodAMultipleReturnInlined()); }' ] @@ -905,6 +895,100 @@ methodAReturnBlockAssignment(void) }' ] +{ #category : 'inlining-returning-arguments' } +SlangInliningTest >> testMethodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined */ +static sqInt +methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined(void) +{ + sqInt a; + + /* begin methodAMultipleReturnInlined */ + /* begin methodBMultipleReturn */ + if (methodB()) { + goto l3; + } + /* begin methodCMultipleReturn */ + if (methodC()) { + goto l2; + } + 3 + 3; + l2: + ; + /* end methodCMultipleReturn */ + 2 + 2; + l3: + ; + /* end methodBMultipleReturn */ + a = 1 + 1; + /* end methodAMultipleReturnInlined */ + return 0; +}' +] + +{ #category : 'inlining-returning-arguments' } +SlangInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAWithReturningSendWithReturningIfInArgumentsInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAWithReturningSendWithReturningIfInArgumentsInlined */ +static sqInt +methodAWithReturningSendWithReturningIfInArgumentsInlined(void) +{ + return methodB(((methodB()) + ? 1 + : 2)); +}' +] + +{ #category : 'inlining-returning-arguments' } +SlangInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAWithReturningSendWithReturningIfInSendInArgumentsInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAWithReturningSendWithReturningIfInSendInArgumentsInlined */ +static sqInt +methodAWithReturningSendWithReturningIfInSendInArgumentsInlined(void) +{ + return methodB(methodB(((methodB()) + ? 1 + : 2))); +}' +] + { #category : 'inlining-simple' } SlangInliningTest >> testSimpleInlining [ diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 4c51e677ce3..77d5992e40a 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -420,12 +420,6 @@ SLInliner >> inlineSend: aSendNode directReturn: directReturn exitVar: exitVar [ ^ inlineStmts ] -{ #category : 'transformation' } -SLInliner >> invert: aSendNode replacement: aTMethod [ - - aSendNode parent replaceChild: aSendNode with: aTMethod parseTree -] - { #category : 'inlining-decision' } SLInliner >> isConditionalToBeTransformedForAssignment: aSend [ "Answer if a send is of the form diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index fa3189b54e4..da413647de1 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -18,9 +18,13 @@ SLInlinerWithAnnotation >> annotateCurrentMethod [ { #category : 'inlining-decision' } SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod [ - ((sLNodeAnnotatorVisitor isInSend: aNode) and: [ - aTMethod hasMultipleReturns or: [ aTMethod hasGoTo ] ]) ifTrue: [ - ^ false ]. + | returnCount | + (sLNodeAnnotatorVisitor isInSend: aNode) ifFalse: [ ^ true ]. + returnCount := 0. + aTMethod parseTree nodesDo: [ :node | + (node isGoTo or: [ returnCount > 1 ]) ifTrue: [ ^ false ]. + node isReturn ifTrue: [ returnCount := returnCount + 1 ] ]. + ^ true ] @@ -142,12 +146,15 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." - self annotateCurrentMethod. (aNode isReturn and: [ self isInlineableSend: aNode expression ]) ifTrue: [ ^ self inlineSend: aNode expression ]. (aNode isAssignment and: [ self isInlineableSend: aNode expression ]) - ifTrue: [ ^ self inlineSend: aNode expression ]. + ifTrue: [ + 1 haltIf: [ + aNode variable name = #_numBytes and: + aNode expression selector = #smallObjectBytesForSlots: ]. + ^ self inlineSend: aNode expression ]. (aNode isSend and: [ self isInlineableSend: aNode ]) ifTrue: [ ^ self inlineSend: aNode ]. @@ -232,6 +239,11 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ currentMethod removeLocal: argName ] ]. meth parseTree bindVariablesIn: substitutionDict. meth parseTree parent: aSendNode parent. + 1 haltIf: [ + sel = #smallObjectBytesForSlots: and: [ + aSendNode parent isAssignment and: [ + aSendNode parent parent isStatementList and: [ + aSendNode parent parent statements size = 9 ] ] ] ]. ^ meth parseTree endsWithReturn ifTrue: [ meth parseTree copyWithoutReturn ] ifFalse: [ meth parseTree ] @@ -408,11 +420,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. - self - moveDownReturnsAndAssignmentsFor: aSendNode - including: callee - resetInfoBlock: assignDirectReturnAndExitVarBlock. - + self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. (inlineStmts := OrderedCollection new: callee statements size + callee args size + 2) @@ -437,9 +445,9 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ ] { #category : 'transformation' } -SLInlinerWithAnnotation >> invert: aSendNode replacement: aTMethod [ +SLInlinerWithAnnotation >> invert: aSendNode replacement: aNode [ - aSendNode parent replaceChild: aSendNode with: aTMethod parseTree + aSendNode parent replaceChild: aSendNode with: aNode ] { #category : 'inlining-decision' } @@ -511,11 +519,11 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr m mustBeInlined and: [ m isComplete ] ]) and: [ m mayBeInlined and: [ (codeGenerator mayInline: m selector) and: [ - aNode arguments allSatisfy: [ :a | - (self isSubstitutableNode: a intoMethod: m) and: [ - self - canBeInlineInExpression: nodeForVisitorInfo - replacement: m ] ] ] ] ] ]) or: [ + (aNode arguments allSatisfy: [ :a | + self isSubstitutableNode: a intoMethod: m ]) and: [ + self + canBeInlineInExpression: nodeForVisitorInfo + replacement: m ] ] ] ] ]) or: [ m checkForRequiredInlinability ] ] ] @@ -532,8 +540,9 @@ SLInlinerWithAnnotation >> isInlineableSend: aNode [ m mayBeInlined and: [ (m isComplete and: [ (codeGenerator mayInline: m selector) and: [ - self canBeInlineInExpression: aNode replacement: m ] ]) or: [ - m checkForRequiredInlinability ] ] ] + self + canBeInlineInExpression: aNode + replacement: m ] ]) or: [ m checkForRequiredInlinability ] ] ] ] { #category : 'inlining-decision' } @@ -579,24 +588,23 @@ SLInlinerWithAnnotation >> isSubstitutableNode: aNode intoMethod: targetMeth [ ] { #category : 'transformation' } -SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement resetInfoBlock: aBlock [ - - | needToResetData | - "move assignments to the lowest part of the ast including in callee" - self invert: aSendNode replacement: aTMethodReplacement. - needToResetData := sLNodeAnnotatorVisitor removeAssigningParentFrom: - aSendNode. - self revert: aSendNode replacement: aTMethodReplacement. - needToResetData ifTrue: [ - sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode parent. - aBlock value: (self setDirectReturnAndExitVar: aSendNode) ]. - - "move returns to the lowest part of the ast including in aTMethodReplacement" - self invert: aSendNode replacement: aTMethodReplacement. +SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement [ + + | replacementParseTree | + replacementParseTree := aTMethodReplacement parseTree. + + self invert: aSendNode replacement: replacementParseTree. + sLNodeAnnotatorVisitor + visitFromBranchStartingAt: aSendNode + replacement: replacementParseTree. + + sLNodeAnnotatorVisitor removeAssigningParentFrom: + replacementParseTree. + sLNodeAnnotatorVisitor removeReturningParentFrom: + replacementParseTree. + "the visitor is not re used after for in this call of inlineSend: so we do not set aBlock value" - sLNodeAnnotatorVisitor removeReturningParentFrom: aSendNode. - sLNodeAnnotatorVisitor cleanBranchInfoFrom: - aTMethodReplacement parseTree parent. + sLNodeAnnotatorVisitor cleanBranchInfoFrom: replacementParseTree. self revert: aSendNode replacement: aTMethodReplacement ] @@ -799,7 +807,7 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ sendsToInline isEmpty ifTrue: [ ^ false ]. currentMethod replaceNodesIn: sendsToInline. - + sLNodeAnnotatorVisitor cleanInfo. ^ true ] @@ -812,6 +820,7 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ didSomething := false. stmtLists := self statementsListsForInliningInCurrentMethod. + self annotateCurrentMethod. stmtLists do: [ :stmtList | newStatements := TStatementListNode statements: (OrderedCollection new: stmtList statements size). diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index da24774a803..7140da637a9 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -240,24 +240,6 @@ SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ assoc value push: assoc key ] ] ] ] -{ #category : 'helpers-visiting' } -SLNodeAnnotatorVisitor >> handleConditionalSendVisit: aSendNode [ - "conditionals are control-flow node and thus treated differently from the other sends" - - self activateCurrentSouldBePartiallyIgnored. - self annotateNodeState: aSendNode. - - self pushInExpressionStack. - - self giveStacksInfoForMultipleBranches: aSendNode arguments. - - searchForExpressionValueStack push: true. - aSendNode receiver accept: self. - searchForExpressionValueStack pop. - - inExpressionStack pop -] - { #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> inExpressionString [ "value doesn't actually matter, but the stack need elements" @@ -449,19 +431,17 @@ SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ | assignment | assignment := self assigningParentFor: aNode. - assignment ifNil: [ ^ false ]. + assignment ifNil: [ ^ self ]. - "necessary because in inlining we switched the send node with its replacement" - self visitFromBranchStartingAt: aNode parent. self transformToAssignmentFrom: assignment assignment: assignment. assignment parent replaceChild: assignment with: assignment expression. - self cleanInfoFrom: assignment. - - ^ true + self + visitFromBranchStartingAt: assignment + replacement: assignment expression ] { #category : 'transformation' } @@ -471,19 +451,19 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ | returningParent | returningParent := self returningParentFor: aNode. - returningParent ifNil: [ ^ false ]. + returningParent ifNil: [ ^ self ]. - "necessary because in inlining we switched the send node with its replacement" - self visitFromBranchStartingAt: aNode parent. - self transformDirectReturnFrom: returningParent. + self + transformDirectReturnFrom: returningParent + return: returningParent. returningParent parent replaceChild: returningParent with: returningParent expression. - self cleanInfoFrom: returningParent. - - ^ true + self + visitFromBranchStartingAt: returningParent + replacement: returningParent expression ] { #category : 'helpers-stack-restoration' } @@ -592,35 +572,36 @@ SLNodeAnnotatorVisitor >> topOfStack: aStack [ ] { #category : 'transformation' } -SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode [ +SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode return: returnNode [ "recursively go throught the ast to transfrom directReturn ^ exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [^exp] ifFalse [^exp]" parentNode children do: [ :child | - (self isEffectiveReturnValue: child) + ((self isEffectiveReturnValue: child) and: [ + (self returningParentFor: child) = returnNode ]) ifTrue: [ child parent replaceChild: child with: child asReturnNode ] ifFalse: [ child isReturn ifTrue: [ "already got a return for this branch" ^ self ]. - self transformDirectReturnFrom: child ] ] + self transformDirectReturnFrom: child return: returnNode ] ] ] { #category : 'transformation' } -SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assignment [ +SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assignmentNode [ "recursively go throught the ast to transfrom expression of assignment a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" parentNode children do: [ :child | ((self isEffectiveAssignmentValue: child) and: [ - (self assigningParentFor: child) == assignment ]) + (self assigningParentFor: child) == assignmentNode ]) ifTrue: [ child parent replaceChild: child with: (TAssignmentNode new - variable: assignment variable; + variable: assignmentNode variable; expression: child; yourself) ] ifFalse: [ - self transformToAssignmentFrom: child assignment: assignment ] ] + self transformToAssignmentFrom: child assignment: assignmentNode ] ] ] { #category : 'visiting-main-API' } @@ -673,12 +654,51 @@ SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ searchForExpressionValueStack pop ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitConditionalSendVisit: aSendNode [ + "conditionals are control-flow node and thus treated differently from the other sends" + + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self pushInExpressionStack. + + self giveStacksInfoForMultipleBranches: aSendNode arguments. + + searchForExpressionValueStack push: true. + aSendNode receiver accept: self. + searchForExpressionValueStack pop. + + inExpressionStack pop +] + { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitConstantNode: aConstantNode [ self annotateNodeState: aConstantNode ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitCppConditional: aSendNode [ + "conditionals are control-flow node and thus treated differently from the other sends" + + | arguments | + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self pushInExpressionStack. + + arguments := aSendNode arguments. + self giveStacksInfoForMultipleBranches: arguments allButFirst. + + searchForExpressionValueStack push: true. + aSendNode receiver accept: self. + arguments first accept: self. + searchForExpressionValueStack pop. + + inExpressionStack pop +] + { #category : 'visiting-main-API' } SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ @@ -691,6 +711,18 @@ SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ self assertStacksAreEmpty ] +{ #category : 'visiting-main-API' } +SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode replacement: aReplacementNode [ + + | restoredStacks | + self cleanBranchInfoFrom: aNode. + restoredStacks := self restoreStackStateFrom: aNode. + self cleanInfoFrom: aNode. + aReplacementNode accept: self. + restoredStacks do: [ :stack | stack pop ]. + self assertStacksAreEmpty +] + { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ @@ -745,22 +777,28 @@ SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ + | inSend | aSendNode isConditionalSend ifTrue: [ - self handleConditionalSendVisit: aSendNode. + self visitConditionalSendVisit: aSendNode. + ^ self ]. + + aSendNode isCppConditional ifTrue: [ + self visitCppConditional: aSendNode. ^ self ]. self annotateNodeState: aSendNode. searchForExpressionValueStack push: true. self pushInExpressionStack. - self pushInSendStack. + inSend := aSendNode isIterativeSend. + inSend ifFalse: [ self pushInSendStack ]. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. searchForExpressionValueStack pop. inExpressionStack pop. - inSendStack pop + inSend ifFalse: [ inSendStack pop ] ] { #category : 'visiting' } diff --git a/smalltalksrc/Slang/TMethod.class.st b/smalltalksrc/Slang/TMethod.class.st index 18b672cade8..81db076a2d8 100644 --- a/smalltalksrc/Slang/TMethod.class.st +++ b/smalltalksrc/Slang/TMethod.class.st @@ -1275,18 +1275,6 @@ TMethod >> functionAttributes: aString [ functionAttributes := aString ] -{ #category : 'testing' } -TMethod >> hasGoTo [ - - ^ self parseTree hasGoTo -] - -{ #category : 'testing' } -TMethod >> hasMultipleReturns [ - - ^ self parseTree hasMultipleReturns -] - { #category : 'testing' } TMethod >> hasProperties [ ^properties notNil and: [properties notEmpty] diff --git a/smalltalksrc/Slang/TParseNode.class.st b/smalltalksrc/Slang/TParseNode.class.st index a528fbe0e05..c10e8b14f63 100644 --- a/smalltalksrc/Slang/TParseNode.class.st +++ b/smalltalksrc/Slang/TParseNode.class.st @@ -6,8 +6,7 @@ Class { #superclass : 'Object', #instVars : [ 'parent', - 'comment', - 'isExpression' + 'comment' ], #category : 'Slang-AST', #package : 'Slang', @@ -209,24 +208,6 @@ TParseNode >> hasExplicitReturn [ ^false ] -{ #category : 'utilities' } -TParseNode >> hasGoTo [ - - self nodesDo: [ :node | node isGoTo ifTrue: [ ^ true ] ]. - ^ false -] - -{ #category : 'utilities' } -TParseNode >> hasMultipleReturns [ - - | returnCount | - returnCount := 0. - self nodesDo: [ :node | - node isReturn ifTrue: [ returnCount := returnCount + 1 ]. - returnCount > 1 ifTrue: [ ^ true ] ]. - ^ false -] - { #category : 'testing' } TParseNode >> hasSideEffect [ "Answer if the parse tree rooted at this node has a side-effect or not. By default assume it has. Nodes that don't override." diff --git a/smalltalksrc/Slang/TSendNode.class.st b/smalltalksrc/Slang/TSendNode.class.st index 2403b280daf..e67ee5c9e6b 100644 --- a/smalltalksrc/Slang/TSendNode.class.st +++ b/smalltalksrc/Slang/TSendNode.class.st @@ -437,6 +437,12 @@ TSendNode >> isConditionalSend [ ifNil:ifNotNil: ifNotNil:ifNil: ifNil: ifNotNil:) includes: selector ] +{ #category : 'testing' } +TSendNode >> isCppConditional [ + + ^ selector beginsWith: #cppIf: +] + { #category : 'dead-code-elimination' } TSendNode >> isDoIterativeSend [ From 52621bb684c9248c8ee19020b3ec39360d8d62e8 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 9 Jul 2025 18:30:16 +0200 Subject: [PATCH 28/86] advance in fixing bug with goTo --- .../SLMockInliningTestClass.class.st | 2 +- .../Slang-Tests/SlangInliningTest.class.st | 2 +- .../Slang/SLInlinerWithAnnotation.class.st | 103 +++++++++++++++--- .../Slang/SLNodeAnnotatorVisitor.class.st | 3 +- 4 files changed, 94 insertions(+), 16 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index b66c8904eb1..6eeb8d55937 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -215,7 +215,7 @@ SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnI { #category : 'inlining-returning-arguments' } SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ - ^ self methodB: {self methodAMultipleReturnInlined} + ^ self methodB: self methodAMultipleReturnInlined ] { #category : 'inlining-returning-arguments' } diff --git a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st index 594881800a6..7084bc093a6 100644 --- a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SlangInliningTest.class.st @@ -910,7 +910,7 @@ SlangInliningTest >> testMethodAWithReturningSendWithMethodWithMultipleReturnInB self assert: translation equals: - '/* SLMockInliningTestClass>>#methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined */ + 'a fix /* SLMockInliningTestClass>>#methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined */ static sqInt methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined(void) { diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index da413647de1..3b809d17d97 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -115,6 +115,87 @@ SLInlinerWithAnnotation >> ensureConditionalAssignmentsAreTransformedInCurrentMe true ] ] whileTrue ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> exitVar: exitVar label: exitLabel for: aTMethod [ + "Replace each return statement in this method with an assignment to the + exit variable followed by either a return or a goto to the given label. + Answer if a goto was generated." + + "Optimization: If exitVar is nil, the return value of the inlined method is not being used, so don't add the assignment statement." + + | labelUsed map elisions eliminateReturnSelfs definingClass | + labelUsed := false. + map := Dictionary new. + elisions := Set new. + "Conceivably one might ^self from a struct class and mean it. In most cases though + ^self means `get me outta here, fast'. So unless this method is from a VMStruct class, + elide any ^self's" + definingClass := aTMethod definingClass. + eliminateReturnSelfs := ((definingClass inheritsFrom: SlangClass) + and: [ definingClass isStructClass ]) not + and: [ + #( #void #sqInt ) includes: + aTMethod returnType ]. + + aTMethod parseTree nodesDo: [ :node | + | replacement | + node isReturn ifTrue: [ + aTMethod + transformReturnSubExpression: node + toAssignmentOf: exitVar + andGoto: exitLabel + unless: eliminateReturnSelfs + into: [ :rep :labelWasUsed | + replacement := rep. + labelWasUsed ifTrue: [ labelUsed := true ] ]. + "replaceNodesIn: is strictly top-down, so any replacement for ^expr ifTrue: [...^fu...] ifFalse: [...^bar...] + will prevent replacement of either ^fu or ^bar. The corollary is that ^expr ifTrue: [foo] ifFalse: [^bar] + must be transformed into expr ifTrue: [^foo] ifFalse: [^bar]" + (node expression isConditionalSend and: [ + node expression hasExplicitReturn ]) + ifTrue: [ + elisions add: node. + (node expression arguments reject: [ :arg | arg endsWithReturn ]) + do: [ :nodeNeedingReturn | + aTMethod + transformReturnSubExpression: + nodeNeedingReturn lastNonCommentStatement + toAssignmentOf: exitVar + andGoto: exitLabel + unless: eliminateReturnSelfs + into: [ :rep :labelWasUsed | + replacement := rep. + labelWasUsed ifTrue: [ labelUsed := true ] ]. + map + at: nodeNeedingReturn lastNonCommentStatement + put: replacement ] ] + ifFalse: [ + map at: node put: (replacement ifNil: [ + TLabeledCommentNode new setComment: + 'return ' , node expression printString ]) ] ] ]. + map isEmpty ifTrue: [ + aTMethod deny: labelUsed. + ^ false ]. + "Now do a top-down replacement for all returns that should be mapped to assignments and gotos" + aTMethod parseTree replaceNodesIn: map. + "Now it is safe to eliminate the returning ifs..." + elisions isEmpty ifFalse: [ + | elisionMap | + elisionMap := Dictionary new. + elisions do: [ :returnNode | + elisionMap at: returnNode put: returnNode expression ]. + aTMethod parseTree replaceNodesIn: elisionMap ]. + "Now flatten any new statement lists..." + aTMethod parseTree nodesDo: [ :node | + | list | + (node isStatementList and: [ + node statements notEmpty and: [ + node lastNonCommentStatement isStatementList ] ]) ifTrue: [ + list := node lastNonCommentStatement statements. + node replaceChild: node lastNonCommentStatement withList: list ] ]. + ^ labelUsed +] + { #category : 'initialization' } SLInlinerWithAnnotation >> initialize [ @@ -150,11 +231,7 @@ SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ ifTrue: [ ^ self inlineSend: aNode expression ]. (aNode isAssignment and: [ self isInlineableSend: aNode expression ]) - ifTrue: [ - 1 haltIf: [ - aNode variable name = #_numBytes and: - aNode expression selector = #smallObjectBytesForSlots: ]. - ^ self inlineSend: aNode expression ]. + ifTrue: [ ^ self inlineSend: aNode expression ]. (aNode isSend and: [ self isInlineableSend: aNode ]) ifTrue: [ ^ self inlineSend: aNode ]. @@ -239,11 +316,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ currentMethod removeLocal: argName ] ]. meth parseTree bindVariablesIn: substitutionDict. meth parseTree parent: aSendNode parent. - 1 haltIf: [ - sel = #smallObjectBytesForSlots: and: [ - aSendNode parent isAssignment and: [ - aSendNode parent parent isStatementList and: [ - aSendNode parent parent statements size = 9 ] ] ] ]. + ^ meth parseTree endsWithReturn ifTrue: [ meth parseTree copyWithoutReturn ] ifFalse: [ meth parseTree ] @@ -412,16 +485,16 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ addVarsDeclarationsAndLabelsOf: callee except: omittedParameters. + self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. + callee hasReturn ifTrue: [ directReturn ifFalse: [ exitLabel := currentMethod unusedLabelForInliningInto: currentMethod. - (callee exitVar: nil label: exitLabel) + (self exitVar: nil label: exitLabel for: callee) ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. - self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. - (inlineStmts := OrderedCollection new: callee statements size + callee args size + 2) add: (label := TLabeledCommentNode new setComment: 'begin ' , sel); @@ -860,5 +933,9 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. currentMethod isComplete ifTrue: [ didSomething := true ] ]. "marking a method complete is progress" + 1 haltIf: [ + didSomething and: [ + currentMethod selector + = #eeInstantiateSmallClassIndex:format:numSlots: ] ]. ^ didSomething ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 7140da637a9..10991e4b5ce 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -760,8 +760,9 @@ SLNodeAnnotatorVisitor >> visitLabeledCommentNode: aLabeledCommentNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ + self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aReturnNode. - + "line might be unecessary" searchForExpressionValueStack push: true. searchForReturnValueStack push: true -> aReturnNode. From 19978146d9cce6d947e23bedc349fae3073a2017 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 11 Jul 2025 12:19:44 +0200 Subject: [PATCH 29/86] Use parametrized tests for inlining strategies --- ...gTest.class.st => SLInliningTest.class.st} | 108 ++++++++++++------ .../SLMockInliningTestClass.class.st | 2 +- .../SlangAbstractTestCase.class.st | 2 +- smalltalksrc/Slang/CCodeGenerator.class.st | 20 +++- ...tension.st => SLInliningTest.extension.st} | 12 +- 5 files changed, 96 insertions(+), 48 deletions(-) rename smalltalksrc/Slang-Tests/{SlangInliningTest.class.st => SLInliningTest.class.st} (86%) rename smalltalksrc/VMMaker/{SlangInliningTest.extension.st => SLInliningTest.extension.st} (55%) diff --git a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st similarity index 86% rename from smalltalksrc/Slang-Tests/SlangInliningTest.class.st rename to smalltalksrc/Slang-Tests/SLInliningTest.class.st index 7084bc093a6..fc8798932fc 100644 --- a/smalltalksrc/Slang-Tests/SlangInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1,15 +1,33 @@ Class { - #name : 'SlangInliningTest', + #name : 'SLInliningTest', #superclass : 'SLAbstractTranslationTestCase', #instVars : [ - 'sLInliner' + 'sLInliner', + 'inliningStrategy', + 'inliner' ], #category : 'Slang-Tests', #package : 'Slang-Tests' } +{ #category : 'building suites' } +SLInliningTest class >> testParameters [ + + ^ ParametrizedTestMatrix new + forSelector: #inliningStrategy + addOptions: + { #useOldInlining. #useExtractedInlining. #useNewInlining }; + yourself +] + +{ #category : 'accessing' } +SLInliningTest >> inliningStrategy: aString [ + + inliningStrategy := aString +] + { #category : 'inlining-simple' } -SlangInliningTest >> test2ChainInlining [ +SLInliningTest >> test2ChainInlining [ | method translation | ccg doBasicInlining: true. @@ -37,7 +55,7 @@ methodC(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> test2ChainInliningAssignOnReturn [ +SLInliningTest >> test2ChainInliningAssignOnReturn [ | method translation | ccg doBasicInlining: true. @@ -65,7 +83,7 @@ methodCAssignOnReturn(void) ] { #category : 'inline-arguments' } -SlangInliningTest >> test2ChainInliningEmptyMethodAWithArgumentsInlined [ +SLInliningTest >> test2ChainInliningEmptyMethodAWithArgumentsInlined [ "a bug present since old version, vm works but it would be nice to fix it one day" | method translation | @@ -101,7 +119,7 @@ emptyMethodAWithArgumentsInlined(void) ] { #category : 'inline-arguments' } -SlangInliningTest >> test2ChainInliningEmptyMethodAWithSimpleArgumentsInlined [ +SLInliningTest >> test2ChainInliningEmptyMethodAWithSimpleArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -128,7 +146,7 @@ emptyMethodAWithSimpleArgumentsInlined(void) ] { #category : 'inline-arguments' } -SlangInliningTest >> test2ChainInliningMethodAWithArgumentsInlined [ +SLInliningTest >> test2ChainInliningMethodAWithArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -150,7 +168,7 @@ methodAWithArgumentsInlined(void) ] { #category : 'inline-arguments' } -SlangInliningTest >> test2ChainInliningMethodAWithReturningSendArgumentsInlined [ +SLInliningTest >> test2ChainInliningMethodAWithReturningSendArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -172,7 +190,7 @@ methodAWithReturningSendArgumentsInlined(void) ] { #category : 'inlining-returning-arguments' } -SlangInliningTest >> test2ChainInliningMethodAWithReturningSendReturningArgumentsInlined [ +SLInliningTest >> test2ChainInliningMethodAWithReturningSendReturningArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -195,7 +213,7 @@ methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined(void) ] { #category : 'inline-arguments' } -SlangInliningTest >> test2ChainInliningMethodAWithSimpleArgumentsInlined [ +SLInliningTest >> test2ChainInliningMethodAWithSimpleArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -218,7 +236,7 @@ methodAWithSimpleArgumentsInlined(void) ] { #category : 'inlining-jump' } -SlangInliningTest >> test2ChainInliningMultipleReturn [ +SLInliningTest >> test2ChainInliningMultipleReturn [ | method translation | ccg doBasicInlining: true. @@ -255,7 +273,7 @@ methodAMultipleReturn(void) ] { #category : 'inlining-jump' } -SlangInliningTest >> test2ChainInliningMultipleReturnAsAssignmentExpression [ +SLInliningTest >> test2ChainInliningMultipleReturnAsAssignmentExpression [ | method translation | ccg doBasicInlining: true. @@ -297,7 +315,7 @@ methodAMultipleReturnAsAssignmentExpression(void) ] { #category : 'inlining-jump' } -SlangInliningTest >> test2ChainInliningMultipleReturnAsReturnExpression [ +SLInliningTest >> test2ChainInliningMultipleReturnAsReturnExpression [ | method translation | ccg doBasicInlining: true. @@ -332,7 +350,7 @@ methodAMultipleReturnAsReturnExpression(void) ] { #category : 'inlining-jump' } -SlangInliningTest >> test2ChainInliningMultipleReturnAsReturnExpressionAndStatement [ +SLInliningTest >> test2ChainInliningMultipleReturnAsReturnExpressionAndStatement [ | method translation | ccg doBasicInlining: true. @@ -364,7 +382,7 @@ methodAMultipleReturnAsReturnExpressionAndStatement(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> test2ChainInliningReturnOnAssignment [ +SLInliningTest >> test2ChainInliningReturnOnAssignment [ | method translation | ccg doBasicInlining: true. @@ -396,7 +414,7 @@ methodCReturnOnAssignment(void) ] { #category : 'inlining-jump' } -SlangInliningTest >> test2ChainInliningSimpleReturn [ +SLInliningTest >> test2ChainInliningSimpleReturn [ | method translation | ccg doBasicInlining: true. @@ -421,7 +439,7 @@ methodASimpleReturn(void) ] { #category : 'collect-statements-for-inlining' } -SlangInliningTest >> testCollectStatementsForInliningInMethodWithAvoidedSelector [ +SLInliningTest >> testCollectStatementsForInliningInMethodWithAvoidedSelector [ "this method collect statements list for inlining, some specific selectors have special comportment regarding inlining strategy like avoiding inlining he receiver or the some arguments" | method statementsForInlining | @@ -435,7 +453,7 @@ SlangInliningTest >> testCollectStatementsForInliningInMethodWithAvoidedSelector ] { #category : 'inlining-simple' } -SlangInliningTest >> testInlineInSwitchRemovesReturnStatement [ +SLInliningTest >> testInlineInSwitchRemovesReturnStatement [ | method codeGenerator methodToBeInlined theSwitch firstCaseConstantExpression firstCase firstCaseLabels | method := (Spur64BitMemoryManager lookupSelector: @@ -462,7 +480,7 @@ SlangInliningTest >> testInlineInSwitchRemovesReturnStatement [ ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAInlineIfFalseReturningIfTrueInAssignement [ +SLInliningTest >> testMethodAInlineIfFalseReturningIfTrueInAssignement [ | method translation | ccg doBasicInlining: true. @@ -497,7 +515,7 @@ methodAInlineIfFalseReturningIfTrueInAssignement(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInAssignment [ +SLInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInAssignment [ | method translation | ccg doBasicInlining: true. @@ -546,7 +564,7 @@ methodAInlineMultipleIfFalseReturningIfTrueInAssignment(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInReturn [ +SLInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInReturn [ | method translation | ccg doBasicInlining: true. @@ -586,7 +604,7 @@ methodAInlineMultipleIfFalseReturningIfTrueInReturn(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineReturningIfTrue [ +SLInliningTest >> testMethodAInlineReturningIfTrue [ | method translation | ccg doBasicInlining: true. @@ -610,7 +628,7 @@ methodAInlineReturningIfTrueInReturn(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAInlineReturningIfTrueIfFalseInAssignment [ +SLInliningTest >> testMethodAInlineReturningIfTrueIfFalseInAssignment [ | method translation | ccg doBasicInlining: true. @@ -638,7 +656,7 @@ methodAInlineReturningIfTrueIfFalseInAssignment(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineReturningIfTrueIfFalseInReturn [ +SLInliningTest >> testMethodAInlineReturningIfTrueIfFalseInReturn [ | method translation | ccg doBasicInlining: true. @@ -663,7 +681,7 @@ methodAInlineReturningIfTrueIfFalseInReturn(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAInlineReturningIfTrueInAssignment [ +SLInliningTest >> testMethodAInlineReturningIfTrueInAssignment [ | method translation | ccg doBasicInlining: true. @@ -691,7 +709,7 @@ methodAInlineReturningIfTrueInAssignment(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInAssignment [ +SLInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInAssignment [ | method translation | ccg doBasicInlining: true. @@ -728,7 +746,7 @@ methodAInlineReturningInlinedIfTrueIfFalseInAssignment(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInReturn [ +SLInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInReturn [ | method translation | ccg doBasicInlining: true. @@ -762,7 +780,7 @@ methodAInlineReturningInlinedIfTrueIfFalseInReturn(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueInAssignment [ +SLInliningTest >> testMethodAInlineReturningInlinedIfTrueInAssignment [ | method translation | ccg doBasicInlining: true. @@ -792,7 +810,7 @@ methodAInlineReturningInlinedIfTrueInAssignment(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineReturningInlinedIfTrueInReturn [ +SLInliningTest >> testMethodAInlineReturningInlinedIfTrueInReturn [ | method translation | ccg doBasicInlining: true. @@ -820,7 +838,7 @@ methodAInlineReturningInlinedIfTrueInReturn(void) ] { #category : 'inlining-returning-conditional' } -SlangInliningTest >> testMethodAInlineifFalseReturningIfTrueInReturn [ +SLInliningTest >> testMethodAInlineifFalseReturningIfTrueInReturn [ | method translation | ccg doBasicInlining: true. @@ -849,7 +867,7 @@ methodAInlineIfFalseReturningIfTrueInReturn(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAReturnAssignment [ +SLInliningTest >> testMethodAReturnAssignment [ | method translation | ccg doBasicInlining: true. @@ -872,7 +890,7 @@ methodAReturnAssignment(void) ] { #category : 'inlining-assignment' } -SlangInliningTest >> testMethodAReturnBlockAssignment [ +SLInliningTest >> testMethodAReturnBlockAssignment [ | method translation | ccg doBasicInlining: true. @@ -896,7 +914,7 @@ methodAReturnBlockAssignment(void) ] { #category : 'inlining-returning-arguments' } -SlangInliningTest >> testMethodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ +SLInliningTest >> testMethodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -940,7 +958,7 @@ methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined(void ] { #category : 'inlining-returning-arguments' } -SlangInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined [ +SLInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -965,7 +983,7 @@ methodAWithReturningSendWithReturningIfInArgumentsInlined(void) ] { #category : 'inlining-returning-arguments' } -SlangInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ +SLInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ | method translation | ccg doBasicInlining: true. @@ -990,7 +1008,7 @@ methodAWithReturningSendWithReturningIfInSendInArgumentsInlined(void) ] { #category : 'inlining-simple' } -SlangInliningTest >> testSimpleInlining [ +SLInliningTest >> testSimpleInlining [ | method sendStatements | ccg doBasicInlining: true. @@ -1004,3 +1022,21 @@ SlangInliningTest >> testSimpleInlining [ ] + +{ #category : 'parameters' } +SLInliningTest >> useExtractedInlining [ + + inliner := SLInliner new +] + +{ #category : 'parameters' } +SLInliningTest >> useNewInlining [ + + inliner := SLInlinerWithAnnotation new +] + +{ #category : 'parameters' } +SLInliningTest >> useOldInlining [ + + inliner := SLOldInlineStrategy new +] diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 6eeb8d55937..9dc1f897b39 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -221,7 +221,7 @@ SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnI { #category : 'inlining-returning-arguments' } SLMockInliningTestClass >> methodAWithReturningSendWithReturningIfInArgumentsInlined [ - ^ self methodB: self methodBReturningIfTrueIfFalseInSend + ^ self methodB: self methodBReturningIfTrueIfFalse ] { #category : 'inlining-returning-arguments' } diff --git a/smalltalksrc/Slang-Tests/SlangAbstractTestCase.class.st b/smalltalksrc/Slang-Tests/SlangAbstractTestCase.class.st index 113b1f1ff23..32c305805c4 100644 --- a/smalltalksrc/Slang-Tests/SlangAbstractTestCase.class.st +++ b/smalltalksrc/Slang-Tests/SlangAbstractTestCase.class.st @@ -1,6 +1,6 @@ Class { #name : 'SlangAbstractTestCase', - #superclass : 'TestCase', + #superclass : 'ParametrizedTestCase', #instVars : [ 'ccg' ], diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index 2ccd39926d9..1b289b8ea7e 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -44,7 +44,8 @@ Class { 'castAsArgumentTranslationDict', 'wordSize', 'structInstanceVariableTranslations', - 'inlineStrategy' + 'inlineStrategy', + 'inliner' ], #classVars : [ 'NoRegParmsInAssertVMs' @@ -1027,11 +1028,9 @@ CCodeGenerator >> doBasicInlining: inlineFlagOrSymbol [ "Inline the bodies of all methods that are suitable for inlining. This method does only the basic inlining suitable for both the core VM and plugins - no bytecode inlining etc" - | pass progress inliner | + | pass progress | self collectInlineList: inlineFlagOrSymbol. - inliner := SLInliner new - codeGenerator: self; - yourself. + pass := 0. progress := true. [ progress ] whileTrue: [ "repeatedly attempt to inline methods until no further progress is made" @@ -3202,7 +3201,11 @@ CCodeGenerator >> initialize [ breakDestInlineSelectors := IdentitySet new. stopOnErrors := false. - wordSize := 4 + wordSize := 4. + + inliner := SLInliner new + codeGenerator: self; + yourself. ] { #category : 'C translation support' } @@ -3450,6 +3453,11 @@ CCodeGenerator >> inlineStrategy: aSLInlineStrategy [ inlineStrategy := aSLInlineStrategy ] +{ #category : 'accessing' } +CCodeGenerator >> inliner: anInlineStrategy [ + inliner := anInlineStrategy +] + { #category : 'utilities' } CCodeGenerator >> instVarNamesForClass: aClass [ ^ aClass instVarNames diff --git a/smalltalksrc/VMMaker/SlangInliningTest.extension.st b/smalltalksrc/VMMaker/SLInliningTest.extension.st similarity index 55% rename from smalltalksrc/VMMaker/SlangInliningTest.extension.st rename to smalltalksrc/VMMaker/SLInliningTest.extension.st index 10a861a3a75..91016faeb37 100644 --- a/smalltalksrc/VMMaker/SlangInliningTest.extension.st +++ b/smalltalksrc/VMMaker/SLInliningTest.extension.st @@ -1,9 +1,13 @@ -Extension { #name : 'SlangInliningTest' } +Extension { #name : 'SLInliningTest' } { #category : '*VMMaker' } -SlangInliningTest >> setUp [ - - super setUp. +SLInliningTest >> setUp [ + super setUp. + + self perform: inliningStrategy. + inliner codeGenerator: ccg. + ccg inliner: inliner. + ccg addClass: SLMockInliningTestClass. "necessary to get the type of sqInt" SpurMemoryManager initBytesPerWord: 8. From 91c9381e5942c81d2cd9fc41ccba57430555e7de Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 11 Jul 2025 18:32:05 +0200 Subject: [PATCH 30/86] advance in fixing bug --- .../SLAnnotatorVisitorTest.class.st | 82 ++++++++++ .../Slang-Tests/SLInliningTest.class.st | 2 +- .../SLNodeAnnotatorVisitorTestClass.class.st | 8 + .../Slang/SLInlinerWithAnnotation.class.st | 150 ++++++++---------- .../Slang/SLNodeAnnotatorVisitor.class.st | 44 ++++- .../SLAnnotatorVisitorTest.extension.st | 2 + 6 files changed, 197 insertions(+), 91 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index eb46814310b..f42f6127457 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -177,6 +177,88 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ self assert: self checkEffectiveExpressionValueCollection ] +{ #category : 'return' } +SLAnnotatorVisitorTest >> testMethodWithBlockwithExpressionReturn [ + + | tMethod constant1 returnNode constant2And3 firstStmtList blockNode effectiveExpressionValueConstantSet constant1And2 constant2 selfVariableNode sendNode | + tMethod := ccg methodNamed: #methodWithBlockwithExpressionReturn. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + self fillCollectionValue: tMethod. + + effectiveExpressionValueConstantSet := self + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. + + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 4. + self assert: effectiveExpressionValueCollection size equals: 3. + + + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. + constant1And2 := effectiveExpressionValueCollection select: [ :node | + node isConstant ]. + + selfVariableNode := (effectiveExpressionValueCollection select: [ + :node | node isVariable ]) first. + + constant1 := (constant1And2 select: [ :node | node value = 1 ]) first. + constant2 := (constant1And2 select: [ :node | node value = 2 ]) first. + + { + selfVariableNode. + constant1. + constant2 } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode + and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. + + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ + (sLNodeAnnotatorVisitor isInSend: constant2) not ]). + + { + selfVariableNode. + constant1 } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + sLNodeAnnotatorVisitor isInSend: node ]) ]. + + sendNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isSend ]) first. + + + firstStmtList := tMethod parseTree. + { + firstStmtList. + returnNode } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + + constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isConstant ]. + blockNode := nonEffectiveExpressionOrStatementCollection select: [ + :node | + node isStatementList and: [ node ~= firstStmtList ] ]. + constant2And3 addAll: blockNode. + constant2And3 do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection +] + { #category : 'assignment' } SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index fc8798932fc..7d1633afd91 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -16,7 +16,7 @@ SLInliningTest class >> testParameters [ ^ ParametrizedTestMatrix new forSelector: #inliningStrategy addOptions: - { #useOldInlining. #useExtractedInlining. #useNewInlining }; + { "#useOldInlining. #useExtractedInlining." #useNewInlining }; yourself ] diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index 1a854e907e0..0da02433107 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -30,6 +30,14 @@ SLNodeAnnotatorVisitorTestClass >> methodWithBlockReturn [ 4 ] ] +{ #category : 'return' } +SLNodeAnnotatorVisitorTestClass >> methodWithBlockwithExpressionReturn [ + + ^ [ + self emptyMethod: 1. + 2 ] +] + { #category : 'assignment' } SLNodeAnnotatorVisitorTestClass >> methodWithConditionalAssignment [ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 3b809d17d97..095efee3b27 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -115,87 +115,6 @@ SLInlinerWithAnnotation >> ensureConditionalAssignmentsAreTransformedInCurrentMe true ] ] whileTrue ] -{ #category : 'transformation' } -SLInlinerWithAnnotation >> exitVar: exitVar label: exitLabel for: aTMethod [ - "Replace each return statement in this method with an assignment to the - exit variable followed by either a return or a goto to the given label. - Answer if a goto was generated." - - "Optimization: If exitVar is nil, the return value of the inlined method is not being used, so don't add the assignment statement." - - | labelUsed map elisions eliminateReturnSelfs definingClass | - labelUsed := false. - map := Dictionary new. - elisions := Set new. - "Conceivably one might ^self from a struct class and mean it. In most cases though - ^self means `get me outta here, fast'. So unless this method is from a VMStruct class, - elide any ^self's" - definingClass := aTMethod definingClass. - eliminateReturnSelfs := ((definingClass inheritsFrom: SlangClass) - and: [ definingClass isStructClass ]) not - and: [ - #( #void #sqInt ) includes: - aTMethod returnType ]. - - aTMethod parseTree nodesDo: [ :node | - | replacement | - node isReturn ifTrue: [ - aTMethod - transformReturnSubExpression: node - toAssignmentOf: exitVar - andGoto: exitLabel - unless: eliminateReturnSelfs - into: [ :rep :labelWasUsed | - replacement := rep. - labelWasUsed ifTrue: [ labelUsed := true ] ]. - "replaceNodesIn: is strictly top-down, so any replacement for ^expr ifTrue: [...^fu...] ifFalse: [...^bar...] - will prevent replacement of either ^fu or ^bar. The corollary is that ^expr ifTrue: [foo] ifFalse: [^bar] - must be transformed into expr ifTrue: [^foo] ifFalse: [^bar]" - (node expression isConditionalSend and: [ - node expression hasExplicitReturn ]) - ifTrue: [ - elisions add: node. - (node expression arguments reject: [ :arg | arg endsWithReturn ]) - do: [ :nodeNeedingReturn | - aTMethod - transformReturnSubExpression: - nodeNeedingReturn lastNonCommentStatement - toAssignmentOf: exitVar - andGoto: exitLabel - unless: eliminateReturnSelfs - into: [ :rep :labelWasUsed | - replacement := rep. - labelWasUsed ifTrue: [ labelUsed := true ] ]. - map - at: nodeNeedingReturn lastNonCommentStatement - put: replacement ] ] - ifFalse: [ - map at: node put: (replacement ifNil: [ - TLabeledCommentNode new setComment: - 'return ' , node expression printString ]) ] ] ]. - map isEmpty ifTrue: [ - aTMethod deny: labelUsed. - ^ false ]. - "Now do a top-down replacement for all returns that should be mapped to assignments and gotos" - aTMethod parseTree replaceNodesIn: map. - "Now it is safe to eliminate the returning ifs..." - elisions isEmpty ifFalse: [ - | elisionMap | - elisionMap := Dictionary new. - elisions do: [ :returnNode | - elisionMap at: returnNode put: returnNode expression ]. - aTMethod parseTree replaceNodesIn: elisionMap ]. - "Now flatten any new statement lists..." - aTMethod parseTree nodesDo: [ :node | - | list | - (node isStatementList and: [ - node statements notEmpty and: [ - node lastNonCommentStatement isStatementList ] ]) ifTrue: [ - list := node lastNonCommentStatement statements. - node replaceChild: node lastNonCommentStatement withList: list ] ]. - ^ labelUsed -] - { #category : 'initialization' } SLInlinerWithAnnotation >> initialize [ @@ -485,16 +404,20 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ addVarsDeclarationsAndLabelsOf: callee except: omittedParameters. - self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. - callee hasReturn ifTrue: [ directReturn ifFalse: [ exitLabel := currentMethod unusedLabelForInliningInto: currentMethod. - (self exitVar: nil label: exitLabel for: callee) + (self label: exitLabel for: callee) ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. + 1 haltIf: [ + currentMethod selector + = #eeInstantiateSmallClassIndex:format:numSlots: and: [ + sel = #allocateSmallNewSpaceSlots:format:classIndex: ] ]. + self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. + (inlineStmts := OrderedCollection new: callee statements size + callee args size + 2) add: (label := TLabeledCommentNode new setComment: 'begin ' , sel); @@ -660,6 +583,60 @@ SLInlinerWithAnnotation >> isSubstitutableNode: aNode intoMethod: targetMeth [ ^ true ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> label: exitLabel for: aTMethod [ + "Replace each return statement in this method with an assignment to the + exit variable followed by either a return or a goto to the given label. + Answer if a goto was generated." + + "Optimization: If exitVar is nil, the return value of the inlined method is not being used, so don't add the assignment statement." + + | labelUsed map eliminateReturnSelfs definingClass | + labelUsed := false. + map := Dictionary new. + + "Conceivably one might ^self from a struct class and mean it. In most cases though + ^self means `get me outta here, fast'. So unless this method is from a VMStruct class, + elide any ^self's" + definingClass := aTMethod definingClass. + eliminateReturnSelfs := ((definingClass inheritsFrom: SlangClass) + and: [ definingClass isStructClass ]) not + and: [ + #( #void #sqInt ) includes: + aTMethod returnType ]. + + aTMethod parseTree nodesDo: [ :node | + | replacement | + node isReturn ifTrue: [ + aTMethod + transformReturnSubExpression: node + toAssignmentOf: nil + andGoto: exitLabel + unless: eliminateReturnSelfs + into: [ :rep :labelWasUsed | + replacement := rep. + labelWasUsed ifTrue: [ labelUsed := true ] ]. + + map at: node put: (replacement ifNil: [ + TLabeledCommentNode new setComment: + 'return ' , node expression printString ]) ] ]. + map isEmpty ifTrue: [ + aTMethod deny: labelUsed. + ^ false ]. + "Now do a top-down replacement for all returns that should be mapped to assignments and gotos" + aTMethod parseTree replaceNodesIn: map. + + "Now flatten any new statement lists..." + aTMethod parseTree nodesDo: [ :node | + | list | + (node isStatementList and: [ + node statements notEmpty and: [ + node lastNonCommentStatement isStatementList ] ]) ifTrue: [ + list := node lastNonCommentStatement statements. + node replaceChild: node lastNonCommentStatement withList: list ] ]. + ^ labelUsed +] + { #category : 'transformation' } SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement [ @@ -933,9 +910,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. currentMethod isComplete ifTrue: [ didSomething := true ] ]. "marking a method complete is progress" - 1 haltIf: [ - didSomething and: [ - currentMethod selector - = #eeInstantiateSmallClassIndex:format:numSlots: ] ]. + ^ didSomething ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 10991e4b5ce..5cea331dfa3 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -284,6 +284,27 @@ SLNodeAnnotatorVisitor >> initialize [ info := IdentityDictionary new ] +{ #category : 'helpers-stack-operation' } +SLNodeAnnotatorVisitor >> invalidateSearchStacksAfterReturn [ + "in an expression like a := exp1 ifTrue: [^ exp2] ifFalse: [exp3], the return node must not be simply ignored, it must correct the stacks so the exploration of exp2 does not mark nodes as used expression for the node a := ... + the stack for return and general expression are already being given new elements from the return node so it is not necessary to modify them" + + | top | + searchForAssignValueStack isNotEmpty ifTrue: [ + top := searchForAssignValueStack pop. + searchForAssignValueStack push: false -> top value ]. + + searchForReturnValueStack isNotEmpty ifTrue: [ + top := searchForReturnValueStack pop. + searchForReturnValueStack push: false -> top value ]. + + searchForStmtListValue isNotEmpty ifFalse: [ ^ false ]. + top := searchForStmtListValue pop. + searchForStmtListValue push: false -> false. + + ^ top value +] + { #category : 'testing' } SLNodeAnnotatorVisitor >> isEffectiveAssignmentValue: aNode [ @@ -466,6 +487,17 @@ SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ replacement: returningParent expression ] +{ #category : 'helpers-stack-operation' } +SLNodeAnnotatorVisitor >> resetSearchStacksAfterReturn: hasInvalidateAStmtSearch [ + "unlike other nodes, search in statementList are from bottom to top, so after invalidating the stacks for a return exploration, we need to undo the modification done, meaning we might repush a false -> true on the stmtList stack, true indicating an active search, false indicating that the next node is not an used expression (we're waiting for a jump)" + + | top | + hasInvalidateAStmtSearch ifFalse: [ ^ self ]. + + top := searchForStmtListValue pop. + searchForStmtListValue push: false -> true +] + { #category : 'helpers-stack-restoration' } SLNodeAnnotatorVisitor >> restoreInStacksFrom: aNode [ @@ -658,6 +690,10 @@ SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ SLNodeAnnotatorVisitor >> visitConditionalSendVisit: aSendNode [ "conditionals are control-flow node and thus treated differently from the other sends" + 1 haltIf: [ + aSendNode tMethod selector + = #eeInstantiateSmallClassIndex:format:numSlots: and: [ + aSendNode selector = #ifTrue:ifFalse: ] ]. self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. @@ -760,10 +796,13 @@ SLNodeAnnotatorVisitor >> visitLabeledCommentNode: aLabeledCommentNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ + | hasInvalidateAStmtSearch | self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aReturnNode. - "line might be unecessary" + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. + searchForAssignValueStack isNotEmpty ifTrue: [ ]. + searchForExpressionValueStack push: true. searchForReturnValueStack push: true -> aReturnNode. self pushInExpressionStack. @@ -772,7 +811,8 @@ SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ inExpressionStack pop. searchForExpressionValueStack pop. - searchForReturnValueStack pop + searchForReturnValueStack pop. + self resetSearchStacksAfterReturn: hasInvalidateAStmtSearch ] { #category : 'visiting' } diff --git a/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st b/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st index 3b0234f30ea..38461cd3887 100644 --- a/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st +++ b/smalltalksrc/VMMaker/SLAnnotatorVisitorTest.extension.st @@ -8,5 +8,7 @@ SLAnnotatorVisitorTest >> setUp [ ccg addClass: SLNodeAnnotatorVisitorTestClass. nonEffectiveExpressionOrStatementCollection := OrderedCollection new. effectiveExpressionValueCollection := OrderedCollection new. + "necessary to get the type of sqInt" + SpurMemoryManager initBytesPerWord: 8. ccg inferTypes ] From 9e246d2e0e72b7994c8f66145fb3d2dc6693fe27 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 15 Jul 2025 15:37:29 +0200 Subject: [PATCH 31/86] add support for iterative + add tests stackVM compile --- .../SLAnnotatorVisitorTest.class.st | 2 +- .../Slang-Tests/SLInliningTest.class.st | 205 +++++++++++++++--- .../SLMockInliningTestClass.class.st | 73 ++++++- .../SLNodeAnnotatorVisitorTestClass.class.st | 5 +- smalltalksrc/Slang/CCodeGenerator.class.st | 13 +- .../Slang/SLInlinerWithAnnotation.class.st | 4 - .../Slang/SLNodeAnnotatorVisitor.class.st | 144 ++++++++++-- 7 files changed, 374 insertions(+), 72 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index f42f6127457..73a3241457e 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -232,7 +232,7 @@ SLAnnotatorVisitorTest >> testMethodWithBlockwithExpressionReturn [ sendNode := (nonEffectiveExpressionOrStatementCollection select: [ :node | node isSend ]) first. - + 1 halt. firstStmtList := tMethod parseTree. { firstStmtList. diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 7d1633afd91..51a8cea9fc9 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -914,13 +914,38 @@ methodAReturnBlockAssignment(void) ] { #category : 'inlining-returning-arguments' } -SLInliningTest >> testMethodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ +SLInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodAWithReturningSendWithReturningIfInArgumentsInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodAWithReturningSendWithReturningIfInArgumentsInlined */ +static sqInt +methodAWithReturningSendWithReturningIfInArgumentsInlined(void) +{ + return methodB(((methodB()) + ? 1 + : 2)); +}' +] + +{ #category : 'inlining-returning-arguments' } +SLInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ | method translation | ccg doBasicInlining: true. method := ccg methodNamed: - #methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined. + #methodAWithReturningSendWithReturningIfInSendInArgumentsInlined. translation := self translate: method. translation := translation trimBoth. @@ -928,43 +953,132 @@ SLInliningTest >> testMethodAWithReturningSendWithMethodWithMultipleReturnInBloc self assert: translation equals: - 'a fix /* SLMockInliningTestClass>>#methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined */ + '/* SLMockInliningTestClass>>#methodAWithReturningSendWithReturningIfInSendInArgumentsInlined */ static sqInt -methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined(void) +methodAWithReturningSendWithReturningIfInSendInArgumentsInlined(void) +{ + return methodB(methodB(((methodB()) + ? 1 + : 2))); +}' +] + +{ #category : 'inlining-iterative' } +SLInliningTest >> testMethodWithAssignmentOnRepeatInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodWithAssignmentOnRepeatInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithAssignmentOnRepeatInlined */ +static sqInt +methodWithAssignmentOnRepeatInlined(void) { sqInt a; - /* begin methodAMultipleReturnInlined */ - /* begin methodBMultipleReturn */ - if (methodB()) { - goto l3; + /* begin methodWithRepeat */ + while (1) { + if (methodB()) { + a = 5; + goto l2; + } } - /* begin methodCMultipleReturn */ - if (methodC()) { - goto l2; + l2: + ; + /* end methodWithRepeat */ + return 0; +}' +] + +{ #category : 'inlining-iterative' } +SLInliningTest >> testMethodWithAssignmentOnToByDoInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodWithAssignmentOnToByDoInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithAssignmentOnToByDoInlined */ +static sqInt +methodWithAssignmentOnToByDoInlined(void) +{ + sqInt a; + sqInt i; + + /* begin methodWithToByDo */ + for (i = 1; i <= 4; i += 2) { + if (methodB(i)) { + /* begin methodB */ + a = 2 + 2; + goto l2; + /* end methodB */ + } } - 3 + 3; l2: ; - /* end methodCMultipleReturn */ - 2 + 2; - l3: + /* end methodWithToByDo */ + return 0; +}' +] + +{ #category : 'inlining-iterative' } +SLInliningTest >> testMethodWithAssignmentOnToDoInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodWithAssignmentOnToDoInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithAssignmentOnToDoInlined */ +static sqInt +methodWithAssignmentOnToDoInlined(void) +{ + sqInt a; + sqInt i; + + /* begin methodWithToDo */ + for (i = 1; i <= 2; i += 1) { + if (methodB(i)) { + /* begin methodB */ + a = 2 + 2; + goto l2; + /* end methodB */ + } + } + l2: ; - /* end methodBMultipleReturn */ - a = 1 + 1; - /* end methodAMultipleReturnInlined */ + /* end methodWithToDo */ return 0; }' ] -{ #category : 'inlining-returning-arguments' } -SLInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined [ +{ #category : 'inlining-iterative' } +SLInliningTest >> testMethodWithAssignmentOnWhileTrueBinaryInlined [ | method translation | ccg doBasicInlining: true. method := ccg methodNamed: - #methodAWithReturningSendWithReturningIfInArgumentsInlined. + #methodWithAssignmentOnWhileTrueBinaryInlined. translation := self translate: method. translation := translation trimBoth. @@ -972,24 +1086,35 @@ SLInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined self assert: translation equals: - '/* SLMockInliningTestClass>>#methodAWithReturningSendWithReturningIfInArgumentsInlined */ + '/* SLMockInliningTestClass>>#methodWithAssignmentOnWhileTrueBinaryInlined */ static sqInt -methodAWithReturningSendWithReturningIfInArgumentsInlined(void) +methodWithAssignmentOnWhileTrueBinaryInlined(void) { - return methodB(((methodB()) - ? 1 - : 2)); + sqInt a; + + /* begin methodWithWhileTrueBinary */ + while (1) { + if (methodB()) { + /* begin methodB */ + a = 2 + 2; + goto l2; + /* end methodB */ + } + } + l2: + ; + /* end methodWithWhileTrueBinary */ + return 0; }' ] -{ #category : 'inlining-returning-arguments' } -SLInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ +{ #category : 'inlining-iterative' } +SLInliningTest >> testMethodWithAssignmentOnWhileTrueInlined [ | method translation | ccg doBasicInlining: true. - method := ccg methodNamed: - #methodAWithReturningSendWithReturningIfInSendInArgumentsInlined. + method := ccg methodNamed: #methodWithAssignmentOnWhileTrueInlined. translation := self translate: method. translation := translation trimBoth. @@ -997,13 +1122,23 @@ SLInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsIn self assert: translation equals: - '/* SLMockInliningTestClass>>#methodAWithReturningSendWithReturningIfInSendInArgumentsInlined */ + '/* SLMockInliningTestClass>>#methodWithAssignmentOnWhileTrueInlined */ static sqInt -methodAWithReturningSendWithReturningIfInSendInArgumentsInlined(void) +methodWithAssignmentOnWhileTrueInlined(void) { - return methodB(methodB(((methodB()) - ? 1 - : 2))); + sqInt a; + + /* begin methodWithWhileTrue */ + do { + if (methodB()) { + a = 5; + goto l2; + } + } while (1); + l2: + ; + /* end methodWithWhileTrue */ + return 0; }' ] diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 9dc1f897b39..034f7f31aa7 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -212,12 +212,6 @@ SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnI ^ self methodB: self methodAMultipleReturnInlined ] -{ #category : 'inlining-returning-arguments' } -SLMockInliningTestClass >> methodAWithReturningSendWithMethodWithMultipleReturnInBlockArgumentsInlined [ - - ^ self methodB: self methodAMultipleReturnInlined -] - { #category : 'inlining-returning-arguments' } SLMockInliningTestClass >> methodAWithReturningSendWithReturningIfInArgumentsInlined [ @@ -384,6 +378,41 @@ SLMockInliningTestClass >> methodCSimpleReturn [ ^ 3 + 3 ] +{ #category : 'inlining-iterative' } +SLMockInliningTestClass >> methodWithAssignmentOnRepeatInlined [ + + | a | + a := self methodWithRepeat +] + +{ #category : 'inlining-iterative' } +SLMockInliningTestClass >> methodWithAssignmentOnToByDoInlined [ + + | a | + a := self methodWithToByDo +] + +{ #category : 'inlining-iterative' } +SLMockInliningTestClass >> methodWithAssignmentOnToDoInlined [ + + | a | + a := self methodWithToDo +] + +{ #category : 'inlining-iterative' } +SLMockInliningTestClass >> methodWithAssignmentOnWhileTrueBinaryInlined [ + + | a | + a := self methodWithWhileTrueBinary +] + +{ #category : 'inlining-iterative' } +SLMockInliningTestClass >> methodWithAssignmentOnWhileTrueInlined [ + + | a | + a := self methodWithWhileTrue +] + { #category : 'collect-statements-for-inlining' } SLMockInliningTestClass >> methodWithAvoidedSelectors [ @@ -395,3 +424,35 @@ SLMockInliningTestClass >> methodWithAvoidedSelectors [ [ 'or receiver' . false ] or: [ 'or argument'. true ]. [ 'ifTrue receiver'. false ] ifTrue: ['ifTrue argument' . true ] ] + +{ #category : 'inlining-iterative-helpers' } +SLMockInliningTestClass >> methodWithRepeat [ + + [ self methodB ifTrue: [ ^ 5 ] ] repeat +] + +{ #category : 'inlining-iterative-helpers' } +SLMockInliningTestClass >> methodWithToByDo [ + + 1 to: 4 by: 2 do: [:i | (self methodB: i)ifTrue: [ ^ self methodB ] ] +] + +{ #category : 'inlining-iterative-helpers' } +SLMockInliningTestClass >> methodWithToDo [ + + 1 to: 2 do: [:i | (self methodB: i) ifTrue: [ ^ self methodB ] ] +] + +{ #category : 'inlining-iterative-helpers' } +SLMockInliningTestClass >> methodWithWhileTrue [ + + [ + self methodB ifTrue: [ ^ 5 ]. + true ] whileTrue +] + +{ #category : 'inlining-iterative-helpers' } +SLMockInliningTestClass >> methodWithWhileTrueBinary [ + + [ true ] whileTrue: [ self methodB ifTrue: [ ^ self methodB ] ] +] diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index 0da02433107..a7e3718ff26 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -33,8 +33,11 @@ SLNodeAnnotatorVisitorTestClass >> methodWithBlockReturn [ { #category : 'return' } SLNodeAnnotatorVisitorTestClass >> methodWithBlockwithExpressionReturn [ + | a | ^ [ - self emptyMethod: 1. + a := true + ifTrue: [ 3 ] + ifFalse: [ 4 ]. 2 ] ] diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index 1b289b8ea7e..07294833292 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -3177,6 +3177,7 @@ CCodeGenerator >> initAutoLocalizationOfVariablesIn: selector [ { #category : 'initialize-release' } CCodeGenerator >> initialize [ + translationDict := Dictionary new. inlineList := Array new. constants := Dictionary new: 100. @@ -3190,7 +3191,9 @@ CCodeGenerator >> initialize [ useSymbolicConstants := true. generateDeadCode := true. scopeStack := OrderedCollection new. - logger := (ProvideAnswerNotification new tag: #logger; signal) ifNil: [Transcript]. + logger := (ProvideAnswerNotification new + tag: #logger; + signal) ifNil: [ Transcript ]. pools := IdentitySet new. selectorTranslations := IdentityDictionary new. structInstanceVariableTranslations := IdentityDictionary new. @@ -3199,13 +3202,13 @@ CCodeGenerator >> initialize [ previousCommenter := nil. breakSrcInlineSelectors := IdentitySet new. breakDestInlineSelectors := IdentitySet new. - + stopOnErrors := false. wordSize := 4. - - inliner := SLInliner new + + inliner := SLInlinerWithAnnotation new codeGenerator: self; - yourself. + yourself ] { #category : 'C translation support' } diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 095efee3b27..8d6965d9af9 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -412,10 +412,6 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ ifTrue: [ currentMethod labels add: exitLabel ] ifFalse: [ "is label used?" exitLabel := nil ] ] ]. - 1 haltIf: [ - currentMethod selector - = #eeInstantiateSmallClassIndex:format:numSlots: and: [ - sel = #allocateSmallNewSpaceSlots:format:classIndex: ] ]. self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. (inlineStmts := OrderedCollection new: diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 5cea331dfa3..9bf03b41421 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -412,11 +412,11 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri isEffectivelyIgnored := false. - searchForStmtListValue isNotEmpty ifTrue: [ + "searchForStmtListValue isNotEmpty ifTrue: [ searchForStmtListValue top key ifTrue: [ assoc := true -> assoc value ]. searchForStmtListValue top value ifTrue: [ - isEffectivelyIgnored := true ] ]. + isEffectivelyIgnored := true ] ]." currentShouldBePartiallyIgnored ifTrue: [ @@ -662,6 +662,23 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ searchForAssignValueStack pop ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitBinaryIterativeSend: aSendNode [ + "in Slang, iterative are not considered as expression but rather as statement to be closer to C" + + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self pushInExpressionStack. + searchForExpressionValueStack push: true. + + aSendNode arguments first accept: self. + aSendNode receiver accept: self. + + searchForExpressionValueStack pop. + inExpressionStack pop +] + { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitBraceCaseNode: aTBraceCaseNode [ "shouldn't be reachable in real condition, TBraceCaseNode are translated to switch before going through any operation. @@ -687,13 +704,9 @@ SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ ] { #category : 'helpers-visiting' } -SLNodeAnnotatorVisitor >> visitConditionalSendVisit: aSendNode [ +SLNodeAnnotatorVisitor >> visitConditionalSend: aSendNode [ "conditionals are control-flow node and thus treated differently from the other sends" - 1 haltIf: [ - aSendNode tMethod selector - = #eeInstantiateSmallClassIndex:format:numSlots: and: [ - aSendNode selector = #ifTrue:ifFalse: ] ]. self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. @@ -735,6 +748,26 @@ SLNodeAnnotatorVisitor >> visitCppConditional: aSendNode [ inExpressionStack pop ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ + "in Slang, iterative are not considered as expression but rather as statement to be closer to C" + + | arguments | + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self pushInExpressionStack. + searchForExpressionValueStack push: true. + + arguments := aSendNode arguments. + arguments last accept: self. + arguments allButLast do: [ :node | node accept: self ]. + aSendNode receiver accept: self. + + searchForExpressionValueStack pop. + inExpressionStack pop +] + { #category : 'visiting-main-API' } SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ @@ -774,8 +807,15 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ (aTGotoNode haveBeenMetInInlining or: [ searchForStmtListValue top value not ]) ifTrue: [ ^ self ]. - searchForStmtListValue pop. - searchForStmtListValue push: true -> true + searchForStmtListValue top value ifTrue: [ + searchForStmtListValue pop. + searchForStmtListValue push: true -> true ]. + + self stacksForSpecificValueHolder do: [ :stack | + stack isNotEmpty ifTrue: [ + | top | + top := stack pop. + stack push: true -> top value ] ] ] { #category : 'visiting' } @@ -818,28 +858,50 @@ SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ - | inSend | - aSendNode isConditionalSend ifTrue: [ - self visitConditionalSendVisit: aSendNode. - ^ self ]. - - aSendNode isCppConditional ifTrue: [ - self visitCppConditional: aSendNode. - ^ self ]. + (self visitSpecialSend: aSendNode) ifTrue: [ ^ self ]. self annotateNodeState: aSendNode. searchForExpressionValueStack push: true. self pushInExpressionStack. - inSend := aSendNode isIterativeSend. - inSend ifFalse: [ self pushInSendStack ]. + self pushInSendStack. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. searchForExpressionValueStack pop. inExpressionStack pop. - inSend ifFalse: [ inSendStack pop ] + inSendStack pop +] + +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitSpecialSend: aSendNode [ + + aSendNode isConditionalSend ifTrue: [ + self visitConditionalSend: aSendNode. + ^ true ]. + + aSendNode isCppConditional ifTrue: [ + self visitCppConditional: aSendNode. + ^ true ]. + + aSendNode isUnaryIterativeSend ifTrue: [ + self visitUnaryIterativeSend: aSendNode. + ^ true ]. + + aSendNode isUnaryIterativeSendWithImportantLastStatement ifTrue: [ + self visitUnaryIterativeSendWithImportantLastStatement: aSendNode. + ^ true ]. + + aSendNode isBinaryIterativeSend ifTrue: [ + self visitBinaryIterativeSend: aSendNode. + ^ true ]. + + aSendNode isDoIterativeSend ifTrue: [ + self visitDoIterativeSend: aSendNode. + ^ true ]. + + ^ false ] { #category : 'visiting' } @@ -900,6 +962,48 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ searchForExpressionValueStack pop ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitUnaryIterativeSend: aSendNode [ + "in Slang, iterative are not considered as expression but rather as statement to be closer to C" + + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self pushInExpressionStack. + searchForExpressionValueStack push: true. + + aSendNode receiver accept: self. + + searchForExpressionValueStack pop. + inExpressionStack pop +] + +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSendNode [ + "for a 'a := [[ exp . condition ] whileTrue]' case (obtained through inlining for example), the last expression of the block is a condition not the expression value of a := ..." + + | block condition | + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + "to not interpret the condition as the effective value, we move the condition to the top" + block := aSendNode receiver. + condition := block lastNonCommentStatement. + block removeLast. + block addAllFirst: { condition }. + + self pushInExpressionStack. + searchForExpressionValueStack push: true. + + aSendNode receiver accept: self. + + searchForExpressionValueStack pop. + inExpressionStack pop. + + block removeFirstNonCommentStatement. + block addAllLastKeepingEndComments: {condition} +] + { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitVariableNode: aVariableNode [ From 4c50f645c5cd0a2ebcef38e79418a1576adeb402 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 15 Jul 2025 16:06:47 +0200 Subject: [PATCH 32/86] transpilation works --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 8d6965d9af9..759312c6fff 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -19,6 +19,8 @@ SLInlinerWithAnnotation >> annotateCurrentMethod [ SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod [ | returnCount | + "for now" + aTMethod inline = #always ifTrue: [ ^ true ]. (sLNodeAnnotatorVisitor isInSend: aNode) ifFalse: [ ^ true ]. returnCount := 0. aTMethod parseTree nodesDo: [ :node | From a837a84f5a1c3dd056bf61f49c2aa829115f02f5 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 15 Jul 2025 16:47:53 +0200 Subject: [PATCH 33/86] keep using stable version --- smalltalksrc/Slang/CCodeGenerator.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index 07294833292..fef04328aaa 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -3206,7 +3206,7 @@ CCodeGenerator >> initialize [ stopOnErrors := false. wordSize := 4. - inliner := SLInlinerWithAnnotation new + inliner := SLInliner new codeGenerator: self; yourself ] From 599ee63a1bf01ea8f259ebf6766213c3e06fc5fd Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 15 Jul 2025 18:58:23 +0200 Subject: [PATCH 34/86] bug found with returningIF --- .../Slang-Tests/SLInliningTest.class.st | 62 +++++++++++++++++++ .../SLMockInliningTestClass.class.st | 29 +++++++++ smalltalksrc/Slang/SLInliner.class.st | 3 +- .../Slang/SLInlinerWithAnnotation.class.st | 2 +- 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 51a8cea9fc9..c9a69a00919 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1142,6 +1142,68 @@ methodWithAssignmentOnWhileTrueInlined(void) }' ] +{ #category : 'inlining-jump' } +SLInliningTest >> testMethodWithReturningIfWithReturnPartiallyPushedDownInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodWithReturningIfWithReturnPartiallyPushedDownInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithReturningIfWithReturnPartiallyPushedDownInlined */ +static sqInt +methodWithReturningIfWithReturnPartiallyPushedDownInlined(void) +{ + methodB(((methodB()) + ? 2 + : 3)); + return 0; +}' +] + +{ #category : 'inlining-jump' } +SLInliningTest >> testMethodWithReturningIfWithReturnPushedDownInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodWithReturningIfWithReturnPushedDownInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithReturningIfWithReturnPushedDownInlined */ +static sqInt +methodWithReturningIfWithReturnPushedDownInlined(void) +{ + sqInt a; + + /* begin methodBMultipleReturningIfWithReturnPushedDown */ + if (methodB()) { + a = 2; + goto l2; + } else { + a = 3; + goto l2; + } + l2: + ; + /* end methodBMultipleReturningIfWithReturnPushedDown */ + return 0; +}' +] + { #category : 'inlining-simple' } SLInliningTest >> testSimpleInlining [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 034f7f31aa7..ead07a8a7b3 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -281,6 +281,22 @@ SLMockInliningTestClass >> methodBMultipleReturnExpressionAndStatement [ ifFalse: [ self methodCMultipleReturn ] ] +{ #category : 'inlining-jump-helpers' } +SLMockInliningTestClass >> methodBMultipleReturningIfWithReturnPartiallyPushedDown [ + + ^ self methodB + ifTrue: [ ^ [ 2 ] ] + ifFalse: [ 3 ] +] + +{ #category : 'inlining-jump-helpers' } +SLMockInliningTestClass >> methodBMultipleReturningIfWithReturnPushedDown [ + + self methodB + ifTrue: [ ^ 2 ] + ifFalse: [ ^ 3 ] +] + { #category : 'inlining-assignment-helpers' } SLMockInliningTestClass >> methodBReturnOnAssignment [ @@ -431,6 +447,19 @@ SLMockInliningTestClass >> methodWithRepeat [ [ self methodB ifTrue: [ ^ 5 ] ] repeat ] +{ #category : 'inlining-jump' } +SLMockInliningTestClass >> methodWithReturningIfWithReturnPartiallyPushedDownInlined [ + + self methodB: + self methodBMultipleReturningIfWithReturnPartiallyPushedDown +] + +{ #category : 'inlining-jump' } +SLMockInliningTestClass >> methodWithReturningIfWithReturnPushedDownInlined [ + + self methodB: self methodBMultipleReturningIfWithReturnPushedDown +] + { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithToByDo [ diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 77d5992e40a..cb2542d6449 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -783,6 +783,7 @@ SLInliner >> tryToInlineMethodsInCurrentMethod [ "marking a method complete is progress" currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. - currentMethod isComplete ifTrue: [ didSomething := true ] ]. "marking a method complete is progress" + currentMethod isComplete ifTrue: [ didSomething := true ] ]. + ^ didSomething ] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 759312c6fff..19f52c36aed 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -907,7 +907,7 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ "marking a method complete is progress" currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. - currentMethod isComplete ifTrue: [ didSomething := true ] ]. "marking a method complete is progress" + currentMethod isComplete ifTrue: [ didSomething := true ] ]. ^ didSomething ] From 4063388a21a259b8c9dc40b6ec5eebef6aa781ec Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 16 Jul 2025 17:44:01 +0200 Subject: [PATCH 35/86] fixing bugs --- .../Slang-Tests/SLInliningTest.class.st | 16 +- .../Slang/SLInlinerWithAnnotation.class.st | 85 +++++++--- .../Slang/SLNodeAnnotatorVisitor.class.st | 154 +++++++++++++----- smalltalksrc/Slang/TParseNode.class.st | 10 ++ .../Slang/TStatementListNode.class.st | 2 +- 5 files changed, 183 insertions(+), 84 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index c9a69a00919..02593e7e4b1 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1187,19 +1187,9 @@ SLInliningTest >> testMethodWithReturningIfWithReturnPushedDownInlined [ static sqInt methodWithReturningIfWithReturnPushedDownInlined(void) { - sqInt a; - - /* begin methodBMultipleReturningIfWithReturnPushedDown */ - if (methodB()) { - a = 2; - goto l2; - } else { - a = 3; - goto l2; - } - l2: - ; - /* end methodBMultipleReturningIfWithReturnPushedDown */ + methodB(((methodB()) + ? 2 + : 3)); return 0; }' ] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 19f52c36aed..6bb3beb5a2c 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -12,22 +12,22 @@ Class { { #category : 'inlining-preparation' } SLInlinerWithAnnotation >> annotateCurrentMethod [ + sLNodeAnnotatorVisitor cleanInfo. sLNodeAnnotatorVisitor visit: currentMethod parseTree ] { #category : 'inlining-decision' } SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod [ - - | returnCount | "for now" + + | parseTree | aTMethod inline = #always ifTrue: [ ^ true ]. (sLNodeAnnotatorVisitor isInSend: aNode) ifFalse: [ ^ true ]. - returnCount := 0. - aTMethod parseTree nodesDo: [ :node | - (node isGoTo or: [ returnCount > 1 ]) ifTrue: [ ^ false ]. - node isReturn ifTrue: [ returnCount := returnCount + 1 ] ]. - - ^ true + parseTree := aTMethod parseTree. + ^ sLNodeAnnotatorVisitor + do: [ + (sLNodeAnnotatorVisitor hasGotoOrMultipleReturn: parseTree) not ] + visit: parseTree ] { #category : 'testing' } @@ -38,7 +38,6 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ codeGenerator maybeBreakForTestOfInliningOf: aTMethod selector. foundIncompleteSend := false. - self annotateCurrentMethod. aTMethod parseTree nodesDo: [ :node | node isSend ifTrue: [ @@ -54,7 +53,6 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ node isSend and: [ node selector == #cCode:inSmalltalk: or: [ codeGenerator isAssertSelector: node selector ] ] ]. - sLNodeAnnotatorVisitor cleanInfo. foundIncompleteSend ifFalse: [ aTMethod complete: true ] ] @@ -133,7 +131,7 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ sel := aSendNode receiver selector. meth := codeGenerator methodNamed: sel. (meth notNil and: [ meth inline == true ]) ifFalse: [ ^ nil ]. - (meth isFunctionalIn: codeGenerator) ifTrue: [ + (self isFunctional: meth) ifTrue: [ inlinedReplacement := self inlineFunctionCall: aSendNode receiver. ^ TSendNode new setSelector: aSendNode selector @@ -236,11 +234,12 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ (parametersToReplaceByArgument includes: argName) ifFalse: [ currentMethod removeLocal: argName ] ]. meth parseTree bindVariablesIn: substitutionDict. - meth parseTree parent: aSendNode parent. - ^ meth parseTree endsWithReturn - ifTrue: [ meth parseTree copyWithoutReturn ] - ifFalse: [ meth parseTree ] + sLNodeAnnotatorVisitor copyWithoutReturn: meth. + "same 'optimization' as before" + meth parseTree children size = 1 ifTrue: [ + meth parseTree: meth parseTree last ]. + ^ meth parseTree ] { #category : 'inlining-support-condtional' } @@ -435,6 +434,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" inlineStmts statements: OrderedCollection new ]. + ^ inlineStmts ] @@ -464,6 +464,38 @@ SLInlinerWithAnnotation >> isConditionalToBeTransformedForAssignment: aSend [ self isConditionalToBeTransformedForAssignment: stmt ] ] ] ] ] ] ] +{ #category : 'testing' } +SLInlinerWithAnnotation >> isFunctional: aTMethod [ + "Answer if the receiver is a functional method. That is, if it + consists of a single return statement of an expression or an assert or flag followed by + such a statement. + + Answer false for methods with return types other than the simple + integer types to work around bugs in the inliner." + + | parseTree last | + parseTree := aTMethod parseTree. + parseTree statements size = 1 ifFalse: [ + (parseTree statements size = 2 and: [ + parseTree statements first isSend and: [ + parseTree statements first selector == #flag: or: [ + (codeGenerator isAssertSelector: + parseTree statements first selector) and: [ + parseTree statements first selector ~~ #asserta: ] ] ] ]) + ifFalse: [ ^ false ] ]. + last := parseTree lastNonCommentStatement. + (last isReturn or: [ + sLNodeAnnotatorVisitor + do: [ sLNodeAnnotatorVisitor isReturningIf: last ] + visit: last ]) ifFalse: [ ^ false ]. + + ^ #( int #'unsigned int' #long #'unsigned long' #'long long' + #'unsigned long long' sqInt usqInt #sqIntptr_t #usqIntptr_t + sqLong usqLong #'int *' #'unsigned int *' #'sqInt *' #'usqInt *' + #'sqLong *' #'usqLong *' #'char *' #'CogMethod *' #'AbstractInstruction *' + #'FILE *' ) includes: aTMethod returnType +] + { #category : 'inlining-decision' } SLInlinerWithAnnotation >> isInlineableConditional: aSendNode [ "Answer if the given send node is of the form aSend [ifTrue:|ifFalse:] [statements] @@ -509,7 +541,7 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr transformedConstantFrom: nodeForVisitorInfo ] ] ifNotNil: [ :m | (m ~~ currentMethod and: [ - ((m isFunctionalIn: codeGenerator) or: [ + ((self isFunctional: m) or: [ m mustBeInlined and: [ m isComplete ] ]) and: [ m mayBeInlined and: [ (codeGenerator mayInline: m selector) and: [ @@ -645,14 +677,14 @@ SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode replacement: replacementParseTree. - - sLNodeAnnotatorVisitor removeAssigningParentFrom: + + sLNodeAnnotatorVisitor moveDownAssigningParentFrom: replacementParseTree. - sLNodeAnnotatorVisitor removeReturningParentFrom: + sLNodeAnnotatorVisitor moveDownReturningParentFrom: replacementParseTree. - "the visitor is not re used after for in this call of inlineSend: so we do not set aBlock value" - sLNodeAnnotatorVisitor cleanBranchInfoFrom: replacementParseTree. + sLNodeAnnotatorVisitor cleanInfoAndBranchInfoFrom: + replacementParseTree. self revert: aSendNode replacement: aTMethodReplacement ] @@ -841,7 +873,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ | sendsToInline | sendsToInline := Dictionary new: 100. - self annotateCurrentMethod. codeGenerator pushScope: currentMethod while: [ currentMethod parseTree nodesDo: [ :node | @@ -855,7 +886,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ sendsToInline isEmpty ifTrue: [ ^ false ]. currentMethod replaceNodesIn: sendsToInline. - sLNodeAnnotatorVisitor cleanInfo. ^ true ] @@ -868,7 +898,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ didSomething := false. stmtLists := self statementsListsForInliningInCurrentMethod. - self annotateCurrentMethod. stmtLists do: [ :stmtList | newStatements := TStatementListNode statements: (OrderedCollection new: stmtList statements size). @@ -882,7 +911,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ currentMethod parseTree nodesDo: [ :node | node isGoTo ifTrue: [ node metInInlining ] ]. - sLNodeAnnotatorVisitor cleanInfo. ^ didSomething ] @@ -898,9 +926,12 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ ^ currentMethod complete: true ]. self ensureConditionalAssignmentsAreTransformedInCurrentMethod. + self annotateCurrentMethod. didSomething := self tryToInlineMethodStatementsListsInCurrentMethod. - didSomething := self tryToInlineMethodExpressionsInCurrentMethod or: [ - didSomething ]. + didSomething ifTrue: [ self annotateCurrentMethod ]. + self tryToInlineMethodExpressionsInCurrentMethod ifTrue: [ + self annotateCurrentMethod. + didSomething := true ]. didSomething ifTrue: [ currentMethod writtenToGlobalVarsCache: nil ]. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 9bf03b41421..05519993ab8 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -192,18 +192,46 @@ SLNodeAnnotatorVisitor >> cleanInfo [ self initialize ] +{ #category : 'cleanup' } +SLNodeAnnotatorVisitor >> cleanInfoAndBranchInfoFrom: aNode [ + + self cleanInfoFrom: aNode. + self cleanBranchInfoFrom: aNode +] + { #category : 'cleanup' } SLNodeAnnotatorVisitor >> cleanInfoFrom: aNode [ info removeKey: aNode ] +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> copyWithoutReturn: aTMethod [ + + | parseTree returns | + parseTree := aTMethod parseTree. + returns := parseTree allReturns. + returns do: [ :return | + return parent replaceChild: return with: return expression ] +] + { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> deActivateCurrentSouldBePartiallyIgnored [ currentShouldBePartiallyIgnored := false ] +{ #category : 'visiting-main-API' } +SLNodeAnnotatorVisitor >> do: aBlock visit: aNode [ + "do a block after visiting aNode, clean the info after" + + | blockResult | + self visit: aNode. + blockResult := aBlock value. + self cleanInfoAndBranchInfoFrom: aNode. + ^ blockResult +] + { #category : 'helpers-annotate' } SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ @@ -240,6 +268,39 @@ SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ assoc value push: assoc key ] ] ] ] +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> hasEffectiveReturnValue: aNode [ + + aNode nodesDo: [ :node | + (self isEffectiveReturnValue: node) ifTrue: [ ^ true ] ]. + ^ false +] + +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> hasGotoOrMultipleReturn: aNode [ + + | returnCounter | + returnCounter := 0. + + ^ self hasGotoOrMultipleReturn: aNode counterBlock: [ :bool | + bool ifTrue: [ returnCounter := returnCounter + 1 ]. + returnCounter ] +] + +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> hasGotoOrMultipleReturn: aNode counterBlock: aBlock [ + + | stopToExplore returnCounter | + aNode children reverseDo: [ :child | + stopToExplore := child isReturn or: [ self isReturningIf: child ]. + returnCounter := aBlock value: stopToExplore. + + (child isGoTo or: [ returnCounter > 1 ]) ifTrue: [ ^ true ]. + stopToExplore ifFalse: [ + ^ self hasGotoOrMultipleReturn: child counterBlock: aBlock ] ]. + ^ false +] + { #category : 'helpers-info-entry-string' } SLNodeAnnotatorVisitor >> inExpressionString [ "value doesn't actually matter, but the stack need elements" @@ -392,6 +453,55 @@ SLNodeAnnotatorVisitor >> isInSend: aNode [ ^ self presenceInfoFor: aNode entry: self inSendString ] +{ #category : 'testing' } +SLNodeAnnotatorVisitor >> isReturningIf: aNode [ + + ^ aNode isConditionalSend and: [ + (self isEffectivelyIgnoredInReturn: aNode) or: [ + aNode arguments allSatisfy: [ :arg | + self hasEffectiveReturnValue: arg ] ] ] +] + +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> moveDownAssigningParentFrom: aNode [ + "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}" + + | assignment | + assignment := self assigningParentFor: aNode. + assignment ifNil: [ ^ self ]. + + self transformToAssignmentFrom: assignment assignment: assignment. + + assignment parent + replaceChild: assignment + with: assignment expression. + + self + visitFromBranchStartingAt: assignment + replacement: assignment expression +] + +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> moveDownReturningParentFrom: aNode [ + "use in inlining for redondant return : return {return exp} -> {return exp}" + + | returningParent | + returningParent := self returningParentFor: aNode. + returningParent ifNil: [ ^ self ]. + + self + transformDirectReturnFrom: returningParent + return: returningParent. + + returningParent parent + replaceChild: returningParent + with: returningParent expression. + + self + visitFromBranchStartingAt: returningParent + replacement: returningParent expression +] + { #category : 'helpers-accessing-info' } SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ @@ -445,48 +555,6 @@ SLNodeAnnotatorVisitor >> pushInSendStack [ inSendStack push: true ] -{ #category : 'transformation' } -SLNodeAnnotatorVisitor >> removeAssigningParentFrom: aNode [ - "use in inlining to put assignment down the ast : x = { ... exp} -> {x = ... exp}, clean all the info as a side effect, it needs to be recalculated - answer if something was done" - - | assignment | - assignment := self assigningParentFor: aNode. - assignment ifNil: [ ^ self ]. - - self transformToAssignmentFrom: assignment assignment: assignment. - - assignment parent - replaceChild: assignment - with: assignment expression. - - self - visitFromBranchStartingAt: assignment - replacement: assignment expression -] - -{ #category : 'transformation' } -SLNodeAnnotatorVisitor >> removeReturningParentFrom: aNode [ - "use in inlining for redondant return : return {return exp} -> {return exp}, clean all the info as a side effect, it needs to be recalculated - answer if anything was done" - - | returningParent | - returningParent := self returningParentFor: aNode. - returningParent ifNil: [ ^ self ]. - - self - transformDirectReturnFrom: returningParent - return: returningParent. - - returningParent parent - replaceChild: returningParent - with: returningParent expression. - - self - visitFromBranchStartingAt: returningParent - replacement: returningParent expression -] - { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> resetSearchStacksAfterReturn: hasInvalidateAStmtSearch [ "unlike other nodes, search in statementList are from bottom to top, so after invalidating the stacks for a return exploration, we need to undo the modification done, meaning we might repush a false -> true on the stmtList stack, true indicating an active search, false indicating that the next node is not an used expression (we're waiting for a jump)" @@ -629,7 +697,7 @@ SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assi (self assigningParentFor: child) == assignmentNode ]) ifTrue: [ child parent replaceChild: child with: (TAssignmentNode new - variable: assignmentNode variable; + variable: assignmentNode variable copy; expression: child; yourself) ] ifFalse: [ diff --git a/smalltalksrc/Slang/TParseNode.class.st b/smalltalksrc/Slang/TParseNode.class.st index c10e8b14f63..b9f0d275841 100644 --- a/smalltalksrc/Slang/TParseNode.class.st +++ b/smalltalksrc/Slang/TParseNode.class.st @@ -34,6 +34,16 @@ TParseNode >> allCalls [ ^calls ] +{ #category : 'utilities' } +TParseNode >> allReturns [ + "Answer a collection of returns in this parse tree." + + | returns | + returns := Set new: 32. + self nodesDo: [ :node | node isReturn ifTrue: [ returns add: node ] ]. + ^ returns +] + { #category : 'enumerating' } TParseNode >> allSatisfy: aBlock [ self nodesDo: [:n| (aBlock value: n) ifFalse: [^false]]. diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index 5d5f96083ce..2a4b3c26861 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -647,7 +647,7 @@ TStatementListNode >> parameterNames [ TStatementListNode >> postCopy [ arguments := arguments copy. - self statements: (statements collect: [ :s | s copy parent: self ]) + self statements: (statements collect: [ :s | s copy ]) ] { #category : 'printing' } From cb7249fadf1af3c4cf868ed92caae17dde177da0 Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 17 Jul 2025 18:44:20 +0200 Subject: [PATCH 36/86] working generation + news tests --- .../SLAnnotatorVisitorTest.class.st | 164 ++++++++++++++++++ .../Slang-Tests/SLInliningTest.class.st | 150 ++++++++++++++++ .../SLMockInliningTestClass.class.st | 41 +++++ .../SLNodeAnnotatorVisitorTestClass.class.st | 17 ++ .../Slang/SLInlinerWithAnnotation.class.st | 107 ++++++++---- .../Slang/SLNodeAnnotatorVisitor.class.st | 80 ++++++--- smalltalksrc/Slang/TSwitchStmtNode.class.st | 17 ++ 7 files changed, 524 insertions(+), 52 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 73a3241457e..9eba8c559e4 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -1150,3 +1150,167 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchWithOtherwiseAsReturnExpression [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ]) ] + +{ #category : 'block' } +SLAnnotatorVisitorTest >> testMethodWithValue [ + + | tMethod constant1 returnNode constant2And3 firstStmtList blockNode effectiveExpressionValueConstantSet constant1And2 constant2 selfVariableNode sendNode | + tMethod := ccg methodNamed: #methodWithValue. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + self fillCollectionValue: tMethod. + + effectiveExpressionValueConstantSet := self + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. + + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 4. + self assert: effectiveExpressionValueCollection size equals: 3. + + + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. + constant1And2 := effectiveExpressionValueCollection select: [ :node | + node isConstant ]. + + selfVariableNode := (effectiveExpressionValueCollection select: [ + :node | node isVariable ]) first. + + constant1 := (constant1And2 select: [ :node | node value = 1 ]) first. + constant2 := (constant1And2 select: [ :node | node value = 2 ]) first. + + { + selfVariableNode. + constant1. + constant2 } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode + and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. + + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ + (sLNodeAnnotatorVisitor isInSend: constant2) not ]). + + { + selfVariableNode. + constant1 } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + sLNodeAnnotatorVisitor isInSend: node ]) ]. + + sendNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isSend ]) first. + + 1 halt. + firstStmtList := tMethod parseTree. + { + firstStmtList. + returnNode } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + + constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isConstant ]. + blockNode := nonEffectiveExpressionOrStatementCollection select: [ + :node | + node isStatementList and: [ node ~= firstStmtList ] ]. + constant2And3 addAll: blockNode. + constant2And3 do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection +] + +{ #category : 'block' } +SLAnnotatorVisitorTest >> testMethodWithValueArgument [ + + | tMethod constant1 returnNode constant2And3 firstStmtList blockNode effectiveExpressionValueConstantSet constant1And2 constant2 selfVariableNode sendNode | + tMethod := ccg methodNamed: #methodWithValueArgument. + + sLNodeAnnotatorVisitor visit: tMethod parseTree. + + self fillCollectionValue: tMethod. + + effectiveExpressionValueConstantSet := self + getValueOfConstantNodeIn: + nonEffectiveExpressionOrStatementCollection. + + self + assert: nonEffectiveExpressionOrStatementCollection size + equals: 4. + self assert: effectiveExpressionValueCollection size equals: 3. + + + returnNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isReturn ]) first. + constant1And2 := effectiveExpressionValueCollection select: [ :node | + node isConstant ]. + + selfVariableNode := (effectiveExpressionValueCollection select: [ + :node | node isVariable ]) first. + + constant1 := (constant1And2 select: [ :node | node value = 1 ]) first. + constant2 := (constant1And2 select: [ :node | node value = 2 ]) first. + + { + selfVariableNode. + constant1. + constant2 } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode + and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. + + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ + (sLNodeAnnotatorVisitor isInSend: constant2) not ]). + + { + selfVariableNode. + constant1 } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + sLNodeAnnotatorVisitor isInSend: node ]) ]. + + sendNode := (nonEffectiveExpressionOrStatementCollection select: [ + :node | node isSend ]) first. + + 1 halt. + firstStmtList := tMethod parseTree. + { + firstStmtList. + returnNode } do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + + constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ + :node | node isConstant ]. + blockNode := nonEffectiveExpressionOrStatementCollection select: [ + :node | + node isStatementList and: [ node ~= firstStmtList ] ]. + constant2And3 addAll: blockNode. + constant2And3 do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. + + nonEffectiveExpressionOrStatementCollection do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + + self assert: self checkNonEffectiveExpressionOrStatementCollection. + self assert: self checkEffectiveExpressionValueCollection +] diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 02593e7e4b1..ad99e3de6ef 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1142,6 +1142,156 @@ methodWithAssignmentOnWhileTrueInlined(void) }' ] +{ #category : 'inlining-block' } +SLInliningTest >> testMethodWithBlockArgumentInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodWithBlockArgumentInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithBlockArgumentInlined */ +static sqInt +methodWithBlockArgumentInlined(void) +{ + sqInt a; + + /* begin methodWithBlockArgument: */ + { + a = 2; + } + /* end methodWithBlockArgument: */ + return 0; +}' +] + +{ #category : 'inlining-block' } +SLInliningTest >> testMethodWithBlockWithReturningIfArgumentInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodWithBlockWithReturningIfArgumentInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithBlockWithReturningIfArgumentInlined */ +static sqInt +methodWithBlockWithReturningIfArgumentInlined(void) +{ + sqInt a; + + /* begin methodWithBlockArgument: */ + { + if (methodB()) { + /* begin methodB */ + a = 2 + 2; + /* end methodB */ + } else { + /* begin methodA */ + 1 + 1; + /* begin methodB */ + a = 2 + 2; + /* end methodB */ + /* end methodA */ + } + } + /* end methodWithBlockArgument: */ + return 0; +}' +] + +{ #category : 'inlining-block' } +SLInliningTest >> testMethodWithBlockWithReturningIfWithGotoInlinedArgumentInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodWithBlockWithReturningIfWithGotoInlinedArgumentInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithBlockWithReturningIfWithGotoInlinedArgumentInlined */ +static sqInt +methodWithBlockWithReturningIfWithGotoInlinedArgumentInlined(void) +{ + sqInt a; + + /* begin methodWithBlockArgument: */ + { + /* begin methodBMultipleReturn */ + if (methodB()) { + a = 0; + goto l4; + } + /* begin methodCMultipleReturn */ + if (methodC()) { + goto l5; + } + 3 + 3; + l5: + ; + /* end methodCMultipleReturn */ + a = 2 + 2; + l4: + ; + /* end methodBMultipleReturn */ + } + /* end methodWithBlockArgument: */ + return 0; +}' +] + +{ #category : 'inlining-block' } +SLInliningTest >> testMethodWithBlockWithReturningIfWithInlinedArgumentInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: + #methodWithBlockWithReturningIfWithInlinedArgumentInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithBlockWithReturningIfWithInlinedArgumentInlined */ +static sqInt +methodWithBlockWithReturningIfWithInlinedArgumentInlined(void) +{ + sqInt a; + + /* begin methodWithBlockArgument: */ + { + if (methodB()) { + a = 2; + } else { + a = methodCMultipleReturn(); + } + } + /* end methodWithBlockArgument: */ + return 0; +}' +] + { #category : 'inlining-jump' } SLInliningTest >> testMethodWithReturningIfWithReturnPartiallyPushedDownInlined [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index ead07a8a7b3..ccfa3a15ec4 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -281,6 +281,14 @@ SLMockInliningTestClass >> methodBMultipleReturnExpressionAndStatement [ ifFalse: [ self methodCMultipleReturn ] ] +{ #category : 'inlining-jump-helpers' } +SLMockInliningTestClass >> methodBMultipleReturnExpressionAndStatementInlined [ + + ^ self methodB + ifTrue: [ 2 ] + ifFalse: [ self methodBReturningInlinedIfTrueIfFalse ] +] + { #category : 'inlining-jump-helpers' } SLMockInliningTestClass >> methodBMultipleReturningIfWithReturnPartiallyPushedDown [ @@ -441,6 +449,39 @@ SLMockInliningTestClass >> methodWithAvoidedSelectors [ [ 'ifTrue receiver'. false ] ifTrue: ['ifTrue argument' . true ] ] +{ #category : 'inlining-block-helpers' } +SLMockInliningTestClass >> methodWithBlockArgument: aBlock [ + + | a | + a := aBlock value +] + +{ #category : 'inlining-block' } +SLMockInliningTestClass >> methodWithBlockArgumentInlined [ + + self methodWithBlockArgument: [ 2 ] +] + +{ #category : 'inlining-block' } +SLMockInliningTestClass >> methodWithBlockWithReturningIfArgumentInlined [ + + self methodWithBlockArgument: [ + self methodBReturningInlinedIfTrueIfFalse ] +] + +{ #category : 'inlining-block' } +SLMockInliningTestClass >> methodWithBlockWithReturningIfWithGotoInlinedArgumentInlined [ + + self methodWithBlockArgument: [ self methodBMultipleReturn ] +] + +{ #category : 'inlining-block' } +SLMockInliningTestClass >> methodWithBlockWithReturningIfWithInlinedArgumentInlined [ + + self methodWithBlockArgument: [ + self methodBMultipleReturnExpressionAndStatement ] +] + { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithRepeat [ diff --git a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st index a7e3718ff26..1862111c103 100644 --- a/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLNodeAnnotatorVisitorTestClass.class.st @@ -160,3 +160,20 @@ SLNodeAnnotatorVisitorTestClass >> methodWithSwitchWithOtherwiseAsReturnExpressi ([ 5 ] -> [ 7 ]) } otherwise: [ 8 ] ] + +{ #category : 'block' } +SLNodeAnnotatorVisitorTestClass >> methodWithValue [ + + | a | + a := [ true ifTrue: [ 2 ] ifFalse: [ 3 ] ] value. +] + +{ #category : 'block' } +SLNodeAnnotatorVisitorTestClass >> methodWithValueArgument [ + + | a | + a := [ :arg | + true + ifTrue: [ arg ] + ifFalse: [ 3 ] ] value: 2 +] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 6bb3beb5a2c..ff9ac66bc41 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -16,6 +16,49 @@ SLInlinerWithAnnotation >> annotateCurrentMethod [ sLNodeAnnotatorVisitor visit: currentMethod parseTree ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> argAssignmentsFor: meth send: aSendNode except: elidedArgs [ + "Return a collection of assignment nodes that assign the given argument expressions to the formal parameter variables of the given method." + + "Optimization: If the actual parameters are either constants or local variables in the target method (the receiver), substitute them directly into the body of meth. Note that global variables cannot be subsituted because the inlined method might depend on the exact ordering of side effects to the globals." + + | stmtList substitutionDict argList | + meth args size > (argList := aSendNode arguments) size ifTrue: [ + self assert: (meth args first beginsWith: 'self_in_'). + argList := { aSendNode receiver } , aSendNode arguments ]. + + stmtList := OrderedCollection new: argList size. + substitutionDict := Dictionary new: argList size. + meth args with: argList do: [ :argName :exprNode | + (currentMethod + isNode: exprNode + substitutableFor: argName + inMethod: meth + in: codeGenerator) + ifTrue: [ + substitutionDict at: argName put: (codeGenerator + node: exprNode + typeCompatibleWith: argName + inliningInto: meth + in: currentMethod). + + currentMethod + removeLocal: argName + ifAbsent: [ self assert: (argName beginsWith: 'self_in_') ]. + currentMethod declarations removeKey: argName ifAbsent: nil ] + ifFalse: [ "Add an assignment for anything except an unused self_in_foo argument" + (elidedArgs includes: argName) ifFalse: [ + stmtList addLast: (TAssignmentNode new + setVariable: (TVariableNode new setName: argName) + expression: (codeGenerator + node: exprNode copy + typeCompatibleWith: argName + inliningInto: meth + in: currentMethod)) ] ] ]. + self replaceVariableIn: substitutionDict for: meth. + ^ stmtList +] + { #category : 'inlining-decision' } SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod [ "for now" @@ -364,13 +407,16 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ otherwise the assignee variable type must match the return type of the inlinee. Return types are not propagated." - | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn assignDirectReturnAndExitVarBlock | + | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn isUsedExpression | sel := aSendNode selector. callee := codeGenerator methodNamed: sel. - assignDirectReturnAndExitVarBlock := [ :res | - directReturn := res first. - exitVar := res second ]. + directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: + aSendNode. + exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) + ifNotNil: [ :assignment | assignment variable name ]. + isUsedExpression := sLNodeAnnotatorVisitor isEffectiveExpression: + aSendNode. "convenient for debugging..." codeGenerator maybeBreakForInlineOf: aSendNode in: self. @@ -384,9 +430,6 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ self checkForFlagIn: callee. - assignDirectReturnAndExitVarBlock value: - (self setDirectReturnAndExitVar: aSendNode). - self propagateReturnTypeDirectReturn: directReturn exitVar: exitVar @@ -418,11 +461,10 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ (inlineStmts := OrderedCollection new: callee statements size + callee args size + 2) add: (label := TLabeledCommentNode new setComment: 'begin ' , sel); - addAll: (currentMethod + addAll: (self argAssignmentsFor: callee send: aSendNode - except: omittedParameters - in: codeGenerator); + except: omittedParameters); addAll: callee statements. "method body" exitLabel ifNotNil: [ @@ -434,7 +476,9 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" inlineStmts statements: OrderedCollection new ]. - + isUsedExpression ifFalse: [ + inlineStmts nodesDo: [ :node | + node isGoTo ifTrue: [ node metInInlining ] ] ]. ^ inlineStmts ] @@ -681,11 +725,7 @@ SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including sLNodeAnnotatorVisitor moveDownAssigningParentFrom: replacementParseTree. sLNodeAnnotatorVisitor moveDownReturningParentFrom: - replacementParseTree. - - sLNodeAnnotatorVisitor cleanInfoAndBranchInfoFrom: - replacementParseTree. - self revert: aSendNode replacement: aTMethodReplacement + replacementParseTree ] { #category : 'transformation' } @@ -734,6 +774,25 @@ SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ except: method args ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aTMethod [ + + | replacement | + aReplacementDictionary isEmpty ifTrue: [ ^ self ]. + aTMethod parseTree nodesDo: [ :node | + (node isVariable and: [ + aReplacementDictionary + at: node name + ifPresent: [ true ] + ifAbsent: [ false ] ]) ifTrue: [ + replacement := (aReplacementDictionary at: node name) copy. + node parent replaceChild: node with: replacement. + sLNodeAnnotatorVisitor + visitFromBranchStartingAt: node + replacement: replacement. + sLNodeAnnotatorVisitor moveDownAssigningParentFrom: replacement ] ] +] + { #category : 'transformation' } SLInlinerWithAnnotation >> revert: aSendNode replacement: aTMethod [ @@ -743,20 +802,6 @@ SLInlinerWithAnnotation >> revert: aSendNode replacement: aTMethod [ aTMethod parseTree parent: aTMethod ] -{ #category : 'inlining-support' } -SLInlinerWithAnnotation >> setDirectReturnAndExitVar: aSendNode [ - - | directReturn exitVar | - directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: - aSendNode. - exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) - ifNotNil: [ :assignment | assignment variable name ]. - ^ OrderedCollection new - add: directReturn; - add: exitVar; - yourself -] - { #category : 'inlining-preparation' } SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. @@ -909,8 +954,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ newStatements addAllLast: inlinedStmts statements ] ]. stmtList statements: newStatements statements ]. - currentMethod parseTree nodesDo: [ :node | - node isGoTo ifTrue: [ node metInInlining ] ]. ^ didSomething ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 05519993ab8..7d1437d8bcc 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -268,6 +268,43 @@ SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ assoc value push: assoc key ] ] ] ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> handleConditionalSend: aSendNode [ + + aSendNode isConditionalSend ifTrue: [ + self visitConditionalSend: aSendNode. + ^ true ]. + + aSendNode isCppConditional ifTrue: [ + self visitCppConditional: aSendNode. + ^ true ]. + + + ^ false +] + +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> handleIterativeSend: aSendNode [ + + aSendNode isUnaryIterativeSend ifTrue: [ + self visitUnaryIterativeSend: aSendNode. + ^ true ]. + + aSendNode isUnaryIterativeSendWithImportantLastStatement ifTrue: [ + self visitUnaryIterativeSendWithImportantLastStatement: aSendNode. + ^ true ]. + + aSendNode isBinaryIterativeSend ifTrue: [ + self visitBinaryIterativeSend: aSendNode. + ^ true ]. + + aSendNode isDoIterativeSend ifTrue: [ + self visitDoIterativeSend: aSendNode. + ^ true ]. + + ^ false +] + { #category : 'testing' } SLNodeAnnotatorVisitor >> hasEffectiveReturnValue: aNode [ @@ -945,28 +982,13 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> visitSpecialSend: aSendNode [ - aSendNode isConditionalSend ifTrue: [ - self visitConditionalSend: aSendNode. - ^ true ]. - - aSendNode isCppConditional ifTrue: [ - self visitCppConditional: aSendNode. - ^ true ]. - - aSendNode isUnaryIterativeSend ifTrue: [ - self visitUnaryIterativeSend: aSendNode. - ^ true ]. + (self handleConditionalSend: aSendNode) ifTrue: [ ^ true ]. - aSendNode isUnaryIterativeSendWithImportantLastStatement ifTrue: [ - self visitUnaryIterativeSendWithImportantLastStatement: aSendNode. - ^ true ]. + (self handleIterativeSend: aSendNode) ifTrue: [ ^ true ]. - aSendNode isBinaryIterativeSend ifTrue: [ - self visitBinaryIterativeSend: aSendNode. - ^ true ]. - - aSendNode isDoIterativeSend ifTrue: [ - self visitDoIterativeSend: aSendNode. + (aSendNode selector keywords allSatisfy: [ :k | + #( 'value' 'value:' ) includes: k ])ifTrue: [ + self visitValueExpansionSend: aSendNode. ^ true ]. ^ false @@ -1072,6 +1094,24 @@ SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSe block addAllLastKeepingEndComments: {condition} ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitValueExpansionSend: aSendNode [ + + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self pushInExpressionStack. + self pushInSendStack. + searchForExpressionValueStack push: true. + + aSendNode receiver accept: self. + aSendNode arguments do: [ :arg | arg accept: self ]. + + inExpressionStack pop. + inSendStack pop. + searchForExpressionValueStack pop +] + { #category : 'visiting' } SLNodeAnnotatorVisitor >> visitVariableNode: aVariableNode [ diff --git a/smalltalksrc/Slang/TSwitchStmtNode.class.st b/smalltalksrc/Slang/TSwitchStmtNode.class.st index feb309b6102..bee5e80d038 100644 --- a/smalltalksrc/Slang/TSwitchStmtNode.class.st +++ b/smalltalksrc/Slang/TSwitchStmtNode.class.st @@ -520,6 +520,23 @@ TSwitchStmtNode >> renameLocalVariablesGivenClassVariables: classVariables globa collectingChangesIn: changedVariables ] ] +{ #category : 'transformations' } +TSwitchStmtNode >> replaceChild: aNode with: aReplacementNode [ + + expression == aNode ifTrue: [ self expression: aReplacementNode ]. + otherwiseOrNil == aNode ifTrue: [ + self otherwiseOrNil: aReplacementNode ]. + self cases: (cases collect: [ :case | + { + (case first collect: [ :label | + label == aNode + ifTrue: [ aReplacementNode ] + ifFalse: [ label ] ]). + (case second == aNode + ifTrue: [ aReplacementNode ] + ifFalse: [ case second ]) } ]) +] + { #category : 'transformations' } TSwitchStmtNode >> replaceNodesIn: aDictionary [ From d0ea3eb62c4db39f437a17f855a5d04125d86766 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 18 Jul 2025 10:45:49 +0200 Subject: [PATCH 37/86] use new inliner --- smalltalksrc/Slang/CCodeGenerator.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index fef04328aaa..07294833292 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -3206,7 +3206,7 @@ CCodeGenerator >> initialize [ stopOnErrors := false. wordSize := 4. - inliner := SLInliner new + inliner := SLInlinerWithAnnotation new codeGenerator: self; yourself ] From 3246e06a8d20040c2a0a27fe9dd6ac8b9f904245 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 18 Jul 2025 11:39:45 +0200 Subject: [PATCH 38/86] remove an unused variable --- smalltalksrc/Slang/TMethod.class.st | 7 ------- 1 file changed, 7 deletions(-) diff --git a/smalltalksrc/Slang/TMethod.class.st b/smalltalksrc/Slang/TMethod.class.st index 81db076a2d8..2146518f6e8 100644 --- a/smalltalksrc/Slang/TMethod.class.st +++ b/smalltalksrc/Slang/TMethod.class.st @@ -444,13 +444,6 @@ TMethod >> asInlineNodeInto: atMethod in: aCodeGen [ ^ TInlineNode new method: self ] -{ #category : 'transformations' } -TMethod >> bindClassVariablesIn: constantDictionary [ - "Class variables are used as constants. This method replaces all references to class variables in the body of this method with the corresponding constant looked up in the class pool dictionary of the source class. The source class class variables should be initialized before this method is called." - - self parseTree: (parseTree bindVariablesIn: constantDictionary) -] - { #category : 'transformations' } TMethod >> bindVariableUsesIn: aDictionary [ From d1f2df64be1f2d1db825838477faf58a2a87d962 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 18 Jul 2025 15:19:10 +0200 Subject: [PATCH 39/86] fix a bug with locals --- .../Slang-Tests/SLInliningTest.class.st | 29 +++++++++++++++++++ .../SLMockInliningTestClass.class.st | 14 +++++++++ .../Slang/SLInlinerWithAnnotation.class.st | 5 +++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index ad99e3de6ef..e4d2e0f983e 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -963,6 +963,35 @@ methodAWithReturningSendWithReturningIfInSendInArgumentsInlined(void) }' ] +{ #category : 'inlining-variable' } +SLInliningTest >> testMethodInlineVariableWithSameName [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodInlineVariableWithSameName. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodInlineVariableWithSameName */ +static sqInt +methodInlineVariableWithSameName(void) +{ + sqInt a; + int a2; + + /* begin methodWithVariableA */ + a2 = 2; + a = 3; + /* end methodWithVariableA */ + return 0; +}' +] + { #category : 'inlining-iterative' } SLInliningTest >> testMethodWithAssignmentOnRepeatInlined [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index ccfa3a15ec4..c4289becea9 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -402,6 +402,12 @@ SLMockInliningTestClass >> methodCSimpleReturn [ ^ 3 + 3 ] +{ #category : 'inlining-variable' } +SLMockInliningTestClass >> methodInlineVariableWithSameName [ + | a | + a := self methodWithVariableA. +] + { #category : 'inlining-iterative' } SLMockInliningTestClass >> methodWithAssignmentOnRepeatInlined [ @@ -513,6 +519,14 @@ SLMockInliningTestClass >> methodWithToDo [ 1 to: 2 do: [:i | (self methodB: i) ifTrue: [ ^ self methodB ] ] ] +{ #category : 'inlinng-variable-helpers' } +SLMockInliningTestClass >> methodWithVariableA [ + + | a | + a := 2. + ^ 3 +] + { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithWhileTrue [ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index ff9ac66bc41..89cae272165 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -277,7 +277,10 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ (parametersToReplaceByArgument includes: argName) ifFalse: [ currentMethod removeLocal: argName ] ]. meth parseTree bindVariablesIn: substitutionDict. - + "copyWithoutReturn does it implictly, avoid duplicating them and in addition if meth end up in an expressionList, they won't appear which might cause bug, + see asCASTIn: vs asCASTExpressionIn: for stmtList. + " + meth locals: Set new. sLNodeAnnotatorVisitor copyWithoutReturn: meth. "same 'optimization' as before" meth parseTree children size = 1 ifTrue: [ From 1fac8518149a365c8be9db7596573aa970973dd6 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 18 Jul 2025 16:11:59 +0200 Subject: [PATCH 40/86] start to handle send that are exit point like error --- .../Slang/SLNodeAnnotatorVisitor.class.st | 23 ++++++++++++++++++- smalltalksrc/Slang/TSendNode.class.st | 7 ++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 7d1437d8bcc..ff2f65eea16 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -873,6 +873,25 @@ SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ inExpressionStack pop ] +{ #category : 'helpers-visiting' } +SLNodeAnnotatorVisitor >> visitExitPointSend: aSendNode [ + + self activateCurrentSouldBePartiallyIgnored. + self annotateNodeState: aSendNode. + + self invalidateSearchStacksAfterReturn. + searchForExpressionValueStack push: true. + self pushInExpressionStack. + self pushInSendStack. + + aSendNode receiver accept: self. + aSendNode arguments do: [ :arg | arg accept: self ]. + + searchForExpressionValueStack pop. + inExpressionStack pop. + inSendStack pop +] + { #category : 'visiting-main-API' } SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ @@ -987,10 +1006,12 @@ SLNodeAnnotatorVisitor >> visitSpecialSend: aSendNode [ (self handleIterativeSend: aSendNode) ifTrue: [ ^ true ]. (aSendNode selector keywords allSatisfy: [ :k | - #( 'value' 'value:' ) includes: k ])ifTrue: [ + #( 'value' 'value:' ) includes: k ]) ifTrue: [ self visitValueExpansionSend: aSendNode. ^ true ]. + aSendNode isExitPoint ifTrue: [ self visitExitPointSend: aSendNode ]. + ^ false ] diff --git a/smalltalksrc/Slang/TSendNode.class.st b/smalltalksrc/Slang/TSendNode.class.st index e67ee5c9e6b..06a92483abe 100644 --- a/smalltalksrc/Slang/TSendNode.class.st +++ b/smalltalksrc/Slang/TSendNode.class.st @@ -449,6 +449,13 @@ TSendNode >> isDoIterativeSend [ ^ #( #to:do: #to:by:do: ) includes: selector ] +{ #category : 'testing' } +TSendNode >> isExitPoint [ + "useful for error() or abort()" + + ^ #( #error: ) includes: selector +] + { #category : 'testing' } TSendNode >> isExpression [ From f1e0b17e63b02d24df68fa7cab546cc7e8dddd05 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 18 Jul 2025 16:40:14 +0200 Subject: [PATCH 41/86] restore stacks after invalidation --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 1 + smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 89cae272165..7635165d20e 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -482,6 +482,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ isUsedExpression ifFalse: [ inlineStmts nodesDo: [ :node | node isGoTo ifTrue: [ node metInInlining ] ] ]. + ^ inlineStmts ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index ff2f65eea16..02de04c4d76 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -876,10 +876,11 @@ SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ { #category : 'helpers-visiting' } SLNodeAnnotatorVisitor >> visitExitPointSend: aSendNode [ + | hasInvalidateAStmtSearch | self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. - self invalidateSearchStacksAfterReturn. + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. searchForExpressionValueStack push: true. self pushInExpressionStack. self pushInSendStack. @@ -889,7 +890,8 @@ SLNodeAnnotatorVisitor >> visitExitPointSend: aSendNode [ searchForExpressionValueStack pop. inExpressionStack pop. - inSendStack pop + inSendStack pop. + self resetSearchStacksAfterReturn: hasInvalidateAStmtSearch ] { #category : 'visiting-main-API' } From 90bbac039634e0f0afe743c45feab1df2f09dc4f Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 18 Jul 2025 18:42:42 +0200 Subject: [PATCH 42/86] fix addAllFirst --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 2 +- smalltalksrc/Slang/TStatementListNode.class.st | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 02de04c4d76..3e645dc8386 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -1114,7 +1114,7 @@ SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSe inExpressionStack pop. block removeFirstNonCommentStatement. - block addAllLastKeepingEndComments: {condition} + block addAllLastKeepingEndComments: { condition } ] { #category : 'helpers-visiting' } diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index 2a4b3c26861..19bb68addb8 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -80,8 +80,10 @@ TStatementListNode >> addAllFirst: aListOfStatement [ statements do: [ :e | actualFirstMet ifFalse: [ - firstComments add: e. - actualFirstMet := e == actualFirst ] + actualFirstMet := e == actualFirst. + actualFirstMet + ifTrue: [ allWithoutFirstComments add: e ] + ifFalse: [ firstComments add: e ] ] ifTrue: [ allWithoutFirstComments add: e ] ]. self statements: firstComments , aListOfStatement , allWithoutFirstComments ] From 7e260292b183428fd5993637d57a967e602449d1 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 21 Jul 2025 11:54:53 +0200 Subject: [PATCH 43/86] clean merge --- smalltalksrc/Slang/CCodeGenerator.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/CCodeGenerator.class.st b/smalltalksrc/Slang/CCodeGenerator.class.st index aa33efc6f20..07294833292 100644 --- a/smalltalksrc/Slang/CCodeGenerator.class.st +++ b/smalltalksrc/Slang/CCodeGenerator.class.st @@ -1028,7 +1028,7 @@ CCodeGenerator >> doBasicInlining: inlineFlagOrSymbol [ "Inline the bodies of all methods that are suitable for inlining. This method does only the basic inlining suitable for both the core VM and plugins - no bytecode inlining etc" - | pass progress inliner | + | pass progress | self collectInlineList: inlineFlagOrSymbol. pass := 0. From 387584dac4cc1e23b3420b1c9496ead10e7cc84b Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 21 Jul 2025 16:34:55 +0200 Subject: [PATCH 44/86] fix bug with conditional receiver inlined + tests for it --- .../Slang-Tests/SLInliningTest.class.st | 83 +++++++++++++++++++ .../SLMockInliningTestClass.class.st | 25 ++++++ .../Slang/SLInlinerWithAnnotation.class.st | 16 +++- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index e4d2e0f983e..04b6a151f08 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1321,6 +1321,89 @@ methodWithBlockWithReturningIfWithInlinedArgumentInlined(void) }' ] +{ #category : 'inlining-conditional-receiver' } +SLInliningTest >> testMethodWithIfTrueReceiverInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodWithIfTrueReceiverInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithIfTrueReceiverInlined */ +static sqInt +methodWithIfTrueReceiverInlined(void) +{ + { + if (methodB()) { + goto l2; + } + /* begin methodCMultipleReturn */ + if (methodC()) { + goto l3; + } + 3 + 3; + l3: + ; + /* end methodCMultipleReturn */ + /* end methodWithMultipleBooleanReturn; fall through */ + /* begin methodB */ + 2 + 2; + /* end methodB */ + l2: + ; + } + return 1; +}' +] + +{ #category : 'inlining-conditional-receiver' } +SLInliningTest >> testMethodWithReturningIfTrueReceiverInlined [ + + | method translation | + ccg doBasicInlining: true. + + method := ccg methodNamed: #methodWithReturningIfTrueReceiverInlined. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithReturningIfTrueReceiverInlined */ +static sqInt +methodWithReturningIfTrueReceiverInlined(void) +{ + { + if (methodB()) { + goto l2; + } + /* begin methodCMultipleReturn */ + if (methodC()) { + goto l3; + } + 3 + 3; + l3: + ; + /* end methodCMultipleReturn */ + /* end methodWithMultipleBooleanReturn; fall through */ + /* begin methodB */ + 2 + 2; + /* end methodB */ + return 0; + l2: + ; + } + return 1; +}' +] + { #category : 'inlining-jump' } SLInliningTest >> testMethodWithReturningIfWithReturnPartiallyPushedDownInlined [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index c4289becea9..1de92af2a63 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -488,12 +488,37 @@ SLMockInliningTestClass >> methodWithBlockWithReturningIfWithInlinedArgumentInli self methodBMultipleReturnExpressionAndStatement ] ] +{ #category : 'inlining-conditional-receiver' } +SLMockInliningTestClass >> methodWithIfTrueReceiverInlined [ + + self methodWithMultipleBooleanReturn ifTrue: [ self methodB ]. + ^ 1 +] + +{ #category : 'inlining-conditional-receiver-helpers' } +SLMockInliningTestClass >> methodWithMultipleBooleanReturn [ + + + self methodB ifTrue: [ ^ false ]. + self methodCMultipleReturn. + ^ true +] + { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithRepeat [ [ self methodB ifTrue: [ ^ 5 ] ] repeat ] +{ #category : 'inlining-conditional-receiver' } +SLMockInliningTestClass >> methodWithReturningIfTrueReceiverInlined [ + + self methodWithMultipleBooleanReturn ifTrue: [ + self methodB. + ^ 0 ]. + ^ 1 +] + { #category : 'inlining-jump' } SLMockInliningTestClass >> methodWithReturningIfWithReturnPartiallyPushedDownInlined [ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 7635165d20e..2b5e15584c2 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -231,9 +231,9 @@ SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode at: #method put: (codeGenerator methodNamed: aSendNode receiver selector) copy. - data - at: #replacementTree - put: (self inlineFunctionCall: aSendNode receiver). + data at: #replacementTree put: (self + inlineFunctionCall: aSendNode receiver + inInlineableConditional: true). data at: #returnReplacement put: (isGuarding ifTrue: [ @@ -246,6 +246,12 @@ SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode { #category : 'inlining-support' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ + + ^ self inlineFunctionCall: aSendNode inInlineableConditional: false +] + +{ #category : 'inlining-support' } +SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional: aBoolean [ "Answer the body of the called function, substituting the actual parameters for the formal argument variables in the method body. Assume caller has established that: @@ -281,10 +287,12 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ see asCASTIn: vs asCASTExpressionIn: for stmtList. " meth locals: Set new. - sLNodeAnnotatorVisitor copyWithoutReturn: meth. + aBoolean ifFalse: [ sLNodeAnnotatorVisitor copyWithoutReturn: meth ]. + "same 'optimization' as before" meth parseTree children size = 1 ifTrue: [ meth parseTree: meth parseTree last ]. + ^ meth parseTree ] From e584c0108e85fe6b543f208055688cb256d12948 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 21 Jul 2025 18:19:06 +0200 Subject: [PATCH 45/86] clean merge --- smalltalksrc/VMMaker/SlangInliningTest.extension.st | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 smalltalksrc/VMMaker/SlangInliningTest.extension.st diff --git a/smalltalksrc/VMMaker/SlangInliningTest.extension.st b/smalltalksrc/VMMaker/SlangInliningTest.extension.st deleted file mode 100644 index c6b5def57e9..00000000000 --- a/smalltalksrc/VMMaker/SlangInliningTest.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : 'SlangInliningTest' } - -{ #category : '*VMMaker' } -SlangInliningTest >> setUp [ - - super setUp. - ccg addClass: SLMockInliningTestClass. - "necessary to get the type of sqInt" - SpurMemoryManager initBytesPerWord: 8. - ccg inferTypes. - sLInliner := SLInliner new -] From 24e4ecc09944533331bb529fbaf09715aff2ca60 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 21 Jul 2025 18:35:30 +0200 Subject: [PATCH 46/86] handle abort in special send --- smalltalksrc/Slang/TSendNode.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/TSendNode.class.st b/smalltalksrc/Slang/TSendNode.class.st index 06a92483abe..12a62a07a56 100644 --- a/smalltalksrc/Slang/TSendNode.class.st +++ b/smalltalksrc/Slang/TSendNode.class.st @@ -453,7 +453,7 @@ TSendNode >> isDoIterativeSend [ TSendNode >> isExitPoint [ "useful for error() or abort()" - ^ #( #error: ) includes: selector + ^ #( #error: #abort ) includes: selector ] { #category : 'testing' } From b4ffb3dc8d5ad93ecfa20169384018c061008d5f Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 23 Jul 2025 15:46:15 +0200 Subject: [PATCH 47/86] handle cppIf in returningIf --- .../Slang/SLInlinerWithAnnotation.class.st | 5 ++--- .../Slang/SLNodeAnnotatorVisitor.class.st | 17 +++++++++++++---- smalltalksrc/Slang/TParseNode.class.st | 6 ++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 2b5e15584c2..09abf8443cf 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -622,9 +622,8 @@ SLInlinerWithAnnotation >> isInlineableSend: aNode [ m mayBeInlined and: [ (m isComplete and: [ (codeGenerator mayInline: m selector) and: [ - self - canBeInlineInExpression: aNode - replacement: m ] ]) or: [ m checkForRequiredInlinability ] ] ] + self canBeInlineInExpression: aNode replacement: m ] ]) or: [ + m checkForRequiredInlinability ] ] ] ] { #category : 'inlining-decision' } diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 3e645dc8386..9db9bf66f79 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -493,10 +493,19 @@ SLNodeAnnotatorVisitor >> isInSend: aNode [ { #category : 'testing' } SLNodeAnnotatorVisitor >> isReturningIf: aNode [ - ^ aNode isConditionalSend and: [ - (self isEffectivelyIgnoredInReturn: aNode) or: [ - aNode arguments allSatisfy: [ :arg | - self hasEffectiveReturnValue: arg ] ] ] + | isReturningIf | + isReturningIf := aNode isConditionalSend and: [ + (self isEffectivelyIgnoredInReturn: aNode) or: [ + aNode arguments allSatisfy: [ :arg | + self hasEffectiveReturnValue: arg ] ] ]. + + isReturningIf := isReturningIf or: [ + aNode isCppConditional and: [ + (self isEffectivelyIgnoredInReturn: aNode) or: [ + aNode arguments allButFirst allSatisfy: [ :arg | + self hasEffectiveReturnValue: arg ] ] ] ]. + + ^ isReturningIf ] { #category : 'transformation' } diff --git a/smalltalksrc/Slang/TParseNode.class.st b/smalltalksrc/Slang/TParseNode.class.st index 119a5a12534..a9f73adcb73 100644 --- a/smalltalksrc/Slang/TParseNode.class.st +++ b/smalltalksrc/Slang/TParseNode.class.st @@ -260,6 +260,12 @@ TParseNode >> isConstant [ ^false ] +{ #category : 'testing' } +TParseNode >> isCppConditional [ + + ^ false +] + { #category : 'testing' } TParseNode >> isDefine [ From c6a5f93744b8730d81f4db65409c2e87c8bda626 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 23 Jul 2025 16:36:34 +0200 Subject: [PATCH 48/86] fix bug with value selectors preventing inlining --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 9db9bf66f79..c69bd84d6db 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -1133,14 +1133,14 @@ SLNodeAnnotatorVisitor >> visitValueExpansionSend: aSendNode [ self annotateNodeState: aSendNode. self pushInExpressionStack. - self pushInSendStack. + searchForExpressionValueStack push: true. aSendNode receiver accept: self. aSendNode arguments do: [ :arg | arg accept: self ]. inExpressionStack pop. - inSendStack pop. + searchForExpressionValueStack pop ] From 550d12da640778f640eaef8129887eeefe984bda Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 23 Jul 2025 16:45:04 +0200 Subject: [PATCH 49/86] remove a now non necessary check --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 1 - 1 file changed, 1 deletion(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 09abf8443cf..ebffd5512c6 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -64,7 +64,6 @@ SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod "for now" | parseTree | - aTMethod inline = #always ifTrue: [ ^ true ]. (sLNodeAnnotatorVisitor isInSend: aNode) ifFalse: [ ^ true ]. parseTree := aTMethod parseTree. ^ sLNodeAnnotatorVisitor From 65d6a41631f326bb5329863fae19ab6389303177 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 25 Jul 2025 15:05:38 +0200 Subject: [PATCH 50/86] fix bug with goTo ambiguity --- .../Slang-Tests/SLInliningTest.class.st | 10 +++++++++- .../Slang/SLInlinerWithAnnotation.class.st | 20 ++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 04b6a151f08..1539270eeda 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1313,7 +1313,15 @@ methodWithBlockWithReturningIfWithInlinedArgumentInlined(void) if (methodB()) { a = 2; } else { - a = methodCMultipleReturn(); + /* begin methodCMultipleReturn */ + if (methodC()) { + a = 1; + goto l3; + } + a = 3 + 3; + l3: + ; + /* end methodCMultipleReturn */ } } /* end methodWithBlockArgument: */ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index ebffd5512c6..20b3bd80ffe 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -417,16 +417,23 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ otherwise the assignee variable type must match the return type of the inlinee. Return types are not propagated." - | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn isUsedExpression | + | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent | sel := aSendNode selector. callee := codeGenerator methodNamed: sel. - directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: aSendNode. - exitVar := (sLNodeAnnotatorVisitor assigningParentFor: aSendNode) - ifNotNil: [ :assignment | assignment variable name ]. - isUsedExpression := sLNodeAnnotatorVisitor isEffectiveExpression: - aSendNode. + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: aSendNode) + ifTrue: [ + assigningParent := sLNodeAnnotatorVisitor assigningParentFor: + aSendNode. + exitVar := assigningParent variable name ]. + "for goTo management" + isUsedExpression := (sLNodeAnnotatorVisitor isEffectiveExpression: + aSendNode) and: [ + directReturn not and: [ + exitVar isNil or: [ + sLNodeAnnotatorVisitor isEffectiveExpression: + assigningParent ] ] ]. "convenient for debugging..." codeGenerator maybeBreakForInlineOf: aSendNode in: self. @@ -489,7 +496,6 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ isUsedExpression ifFalse: [ inlineStmts nodesDo: [ :node | node isGoTo ifTrue: [ node metInInlining ] ] ]. - ^ inlineStmts ] From bcf592384e0f37cb1df06ff6f74d86cc0e1b3a52 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 25 Jul 2025 16:22:45 +0200 Subject: [PATCH 51/86] test adding now missing else --- smalltalksrc/VMMaker/SpurGenerationScavenger.class.st | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st index ea9b083b961..62b25c56f73 100644 --- a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st +++ b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st @@ -823,11 +823,14 @@ SpurGenerationScavenger >> nextCorpseOffset: corpse [ SpurGenerationScavenger >> nextCorpseOrNil: corpse [ "corpse is the corpse of a weak array that has been added to the weakList. Answer the next object on the list, or nil if none." + + | listOffset | self assert: (manager isYoung: corpse). listOffset := self nextCorpseOffset: corpse. - ^listOffset ~= 0 ifTrue: - [self corpseForCorpseOffset: listOffset] + ^ listOffset ~= 0 + ifTrue: [ self corpseForCorpseOffset: listOffset ] + ifFalse: [ 0 ] ] { #category : 'weakness and ephemerality' } From 901adeff0c33a19747ab70e0dd8ca00fd1571eb6 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 29 Jul 2025 10:36:18 +0200 Subject: [PATCH 52/86] test redo old comportment --- smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st b/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st index 00bee5805cd..5144ac4aa91 100644 --- a/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st +++ b/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st @@ -2602,11 +2602,11 @@ StackToRegisterMappingCogit >> genSpecialSelectorArithmetic [ [AndRR] -> [argIsConst ifTrue: [self AndCq: argInt R: ReceiverResultReg] ifFalse: [self AndR: Arg0Reg R: ReceiverResultReg]. - jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0]]. + jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [0]]. [OrRR] -> [argIsConst ifTrue: [self OrCq: argInt R: ReceiverResultReg] ifFalse: [self OrR: Arg0Reg R: ReceiverResultReg]. - jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0]] }. + jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [0]] }. jumpNotSmallInts ifNil: [jumpContinue ifNil: "overflow cannot happen" [self annotateInstructionForBytecode. From 31852c685ed621c267ac4a5ea07631eca1873f84 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 29 Jul 2025 11:44:39 +0200 Subject: [PATCH 53/86] remove uneccesary stmt list --- .../Slang/SLInlinerWithAnnotation.class.st | 17 +++++++++-------- .../Slang/SLNodeAnnotatorVisitor.class.st | 2 +- smalltalksrc/Slang/TMethod.class.st | 2 +- smalltalksrc/Slang/TStatementListNode.class.st | 16 +++++++++++++++- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 20b3bd80ffe..2b4118e895a 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -475,21 +475,22 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. - (inlineStmts := OrderedCollection new: - callee statements size + callee args size + 2) - add: (label := TLabeledCommentNode new setComment: 'begin ' , sel); - addAll: (self + inlineStmts := callee parseTree. + inlineStmts + addAllFirst: (self argAssignmentsFor: callee send: aSendNode except: omittedParameters); - addAll: callee statements. "method body" + addFirst: + (label := TLabeledCommentNode new setComment: 'begin ' , sel). + exitLabel ifNotNil: [ - inlineStmts add: + inlineStmts addLast: (TLabeledCommentNode new setLabel: exitLabel comment: 'end ' , sel) ] ifNil: [ - inlineStmts add: (TLabeledCommentNode new setComment: 'end ' , sel) ]. - inlineStmts := TStatementListNode statements: inlineStmts. + inlineStmts addLast: + (TLabeledCommentNode new setComment: 'end ' , sel) ]. inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" inlineStmts statements: OrderedCollection new ]. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index c69bd84d6db..1e9b0007c3a 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -1112,7 +1112,7 @@ SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSe block := aSendNode receiver. condition := block lastNonCommentStatement. block removeLast. - block addAllFirst: { condition }. + block addAllFirstKeepingFirstComments: { condition }. self pushInExpressionStack. searchForExpressionValueStack push: true. diff --git a/smalltalksrc/Slang/TMethod.class.st b/smalltalksrc/Slang/TMethod.class.st index 14e6e0b8ec6..1b35a3b9b8c 100644 --- a/smalltalksrc/Slang/TMethod.class.st +++ b/smalltalksrc/Slang/TMethod.class.st @@ -2088,7 +2088,7 @@ TMethod >> removeUnusedTempsAndNilIfRequiredIn: aCodeGen [ (d beginsWith: 'extern') or: [ (d beginsWith: 'static') or: [ (d includes: $[) or: [ d includes: $= ] ] ] ]. - parseTree addAllFirst: + parseTree addAllFirstKeepingFirstComments: (readBeforeAssigned asSortedCollection collect: [ :var | | varNode varType zeroNode | varNode := TVariableNode new diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index 0f825fb3021..671cd2703c2 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -69,6 +69,12 @@ TStatementListNode >> accept: aVisitor [ { #category : 'adding' } TStatementListNode >> addAllFirst: aListOfStatement [ + self statements: aListOfStatement , statements +] + +{ #category : 'adding' } +TStatementListNode >> addAllFirstKeepingFirstComments: aListOfStatement [ + (statements isNotEmpty and: [ statements first isComment ]) ifFalse: [ self statements: aListOfStatement , statements ] ifTrue: [ @@ -135,6 +141,14 @@ TStatementListNode >> addFirst: aNode [ self statements: (OrderedCollection new add: aNode ; yourself) , statements ] +{ #category : 'adding' } +TStatementListNode >> addLast: aNode [ + + self statements: statements , (OrderedCollection new + add: aNode; + yourself) +] + { #category : 'utilities' } TStatementListNode >> addReadBeforeAssignedIn: variables to: readBeforeAssigned assignments: assigned in: aCodeGen [ "Add any variables in variables that are read before written to readBeforeAssigned. @@ -480,7 +494,7 @@ TStatementListNode >> firstNonCommentStatement [ | first indexOfFirst limitIndex | first := statements first. - indexOfFirst := 1. + indexOfFirst := 0. limitIndex := statements size. [ first isComment ] whileTrue: [ indexOfFirst := indexOfFirst + 1. From 51897bcb586bdfe8155c94005b1135fb7cb9b17f Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 29 Jul 2025 12:17:17 +0200 Subject: [PATCH 54/86] fix endsWithReturn --- smalltalksrc/Slang-Tests/SLInliningTest.class.st | 6 +++++- smalltalksrc/Slang/TStatementListNode.class.st | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 1539270eeda..5fdd2b6d9a7 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -909,7 +909,11 @@ methodAReturnBlockAssignment(void) { sqInt a; - return (/* begin methodB */ (a = 2 + 2) /* end methodB */); + { + /* begin methodB */ + return (a = 2 + 2); + /* end methodB */ + } }' ] diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index 671cd2703c2..024419ad1f2 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -478,8 +478,7 @@ TStatementListNode >> endsWithReturn [ "Answer true if the last statement of this lock is a return." ^ statements notEmpty and: [ - self lastNonCommentStatement isReturn or: [ - self lastNonCommentStatement isReturningIf ] ] + self lastNonCommentStatement endsWithReturn ] ] { #category : 'accessing' } From f17d8d82e6324bc57b2d9209b7cdf9bcee367f0c Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 29 Jul 2025 14:34:21 +0200 Subject: [PATCH 55/86] fix tests + use nil instead of 0 for unit tests --- smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st | 3 --- smalltalksrc/Slang-Tests/SlangBasicTranslationTest.class.st | 2 +- smalltalksrc/VMMaker/SpurGenerationScavenger.class.st | 2 +- smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st | 4 ++-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st b/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st index cbb9ba0eab3..ce7ef2e3d21 100644 --- a/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st +++ b/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st @@ -2345,7 +2345,6 @@ methodWithUnusedConstantAndReturnInCaseOfNoSendInExpression(SLDeadCodeEliminatio return; } } - break; default: error("Case not found and no otherwise clause"); } @@ -3958,7 +3957,6 @@ methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwi return; } } - break; default: { } @@ -4002,7 +4000,6 @@ methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwi return; } } - break; default: { } diff --git a/smalltalksrc/Slang-Tests/SlangBasicTranslationTest.class.st b/smalltalksrc/Slang-Tests/SlangBasicTranslationTest.class.st index 8ee0c03cb14..58fcb361391 100644 --- a/smalltalksrc/Slang-Tests/SlangBasicTranslationTest.class.st +++ b/smalltalksrc/Slang-Tests/SlangBasicTranslationTest.class.st @@ -1639,8 +1639,8 @@ switchInReturn(void) } default: error("Case not found and no otherwise clause"); - return -1; } + return 0; }' ] diff --git a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st index 62b25c56f73..41cef08ea62 100644 --- a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st +++ b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st @@ -830,7 +830,7 @@ SpurGenerationScavenger >> nextCorpseOrNil: corpse [ listOffset := self nextCorpseOffset: corpse. ^ listOffset ~= 0 ifTrue: [ self corpseForCorpseOffset: listOffset ] - ifFalse: [ 0 ] + ifFalse: [ nil ] ] { #category : 'weakness and ephemerality' } diff --git a/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st b/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st index 5144ac4aa91..22c4572ab76 100644 --- a/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st +++ b/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st @@ -2602,11 +2602,11 @@ StackToRegisterMappingCogit >> genSpecialSelectorArithmetic [ [AndRR] -> [argIsConst ifTrue: [self AndCq: argInt R: ReceiverResultReg] ifFalse: [self AndR: Arg0Reg R: ReceiverResultReg]. - jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [0]]. + jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [nil]]. [OrRR] -> [argIsConst ifTrue: [self OrCq: argInt R: ReceiverResultReg] ifFalse: [self OrR: Arg0Reg R: ReceiverResultReg]. - jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [0]] }. + jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [nil]] }. jumpNotSmallInts ifNil: [jumpContinue ifNil: "overflow cannot happen" [self annotateInstructionForBytecode. From a7978239072913bcde1f8a92a32d7714b9e425c1 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 29 Jul 2025 18:08:04 +0200 Subject: [PATCH 56/86] fix 2 broken tests --- .../SLAnnotatorVisitorTest.class.st | 180 ++++++++---------- 1 file changed, 81 insertions(+), 99 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 9eba8c559e4..1e79fbc32d7 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -1154,80 +1154,70 @@ SLAnnotatorVisitorTest >> testMethodWithSwitchWithOtherwiseAsReturnExpression [ { #category : 'block' } SLAnnotatorVisitorTest >> testMethodWithValue [ - | tMethod constant1 returnNode constant2And3 firstStmtList blockNode effectiveExpressionValueConstantSet constant1And2 constant2 selfVariableNode sendNode | + | tMethod assigmnentNode constantNodes firstStmtList constant2 constant3 | tMethod := ccg methodNamed: #methodWithValue. sLNodeAnnotatorVisitor visit: tMethod parseTree. self fillCollectionValue: tMethod. - effectiveExpressionValueConstantSet := self - getValueOfConstantNodeIn: - nonEffectiveExpressionOrStatementCollection. - self assert: nonEffectiveExpressionOrStatementCollection size equals: 4. - self assert: effectiveExpressionValueCollection size equals: 3. + self assert: effectiveExpressionValueCollection size equals: 7. - returnNode := (nonEffectiveExpressionOrStatementCollection select: [ - :node | node isReturn ]) first. - constant1And2 := effectiveExpressionValueCollection select: [ :node | + assigmnentNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]) first. + constantNodes := effectiveExpressionValueCollection select: [ :node | node isConstant ]. - selfVariableNode := (effectiveExpressionValueCollection select: [ - :node | node isVariable ]) first. - - constant1 := (constant1And2 select: [ :node | node value = 1 ]) first. - constant2 := (constant1And2 select: [ :node | node value = 2 ]) first. - + constant2 := (constantNodes select: [ :node | node value = 2 ]) first. + constant3 := (constantNodes select: [ :node | node value = 3 ]) first. { - selfVariableNode. - constant1. - constant2 } do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode - and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. - - self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ - (sLNodeAnnotatorVisitor isInSend: constant2) not ]). + constant2. + constant3 } do: [ :node | + self assert: + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) ]. - { - selfVariableNode. - constant1 } do: [ :node | + (effectiveExpressionValueCollection reject: [ :node | + { + constant2. + constant3 } includes: node ]) do: [ :node | self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - sLNodeAnnotatorVisitor isInSend: node ]) ]. + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ]. - sendNode := (nonEffectiveExpressionOrStatementCollection select: [ - :node | node isSend ]) first. + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assigmnentNode and: [ + (sLNodeAnnotatorVisitor isInSend: node) not ] ] ] ] ]) ]. - 1 halt. firstStmtList := tMethod parseTree. { firstStmtList. - returnNode } do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInExpression: node) not and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + assigmnentNode } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ]) ]. - constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ - :node | node isConstant ]. - blockNode := nonEffectiveExpressionOrStatementCollection select: [ - :node | - node isStatementList and: [ node ~= firstStmtList ] ]. - constant2And3 addAll: blockNode. - constant2And3 do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInExpression: node) and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. + (nonEffectiveExpressionOrStatementCollection reject: [ :node | + { + firstStmtList. + assigmnentNode } includes: node ]) do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assigmnentNode and: [ + sLNodeAnnotatorVisitor isInAssignment: node ] ]) ]. nonEffectiveExpressionOrStatementCollection do: [ :node | - self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInSend: node) not ] ]) ]. self assert: self checkNonEffectiveExpressionOrStatementCollection. self assert: self checkEffectiveExpressionValueCollection @@ -1236,80 +1226,72 @@ SLAnnotatorVisitorTest >> testMethodWithValue [ { #category : 'block' } SLAnnotatorVisitorTest >> testMethodWithValueArgument [ - | tMethod constant1 returnNode constant2And3 firstStmtList blockNode effectiveExpressionValueConstantSet constant1And2 constant2 selfVariableNode sendNode | + | tMethod firstStmtList assigmnentNode constantNodes constant3 variableArg | tMethod := ccg methodNamed: #methodWithValueArgument. sLNodeAnnotatorVisitor visit: tMethod parseTree. self fillCollectionValue: tMethod. - effectiveExpressionValueConstantSet := self - getValueOfConstantNodeIn: - nonEffectiveExpressionOrStatementCollection. - self assert: nonEffectiveExpressionOrStatementCollection size equals: 4. - self assert: effectiveExpressionValueCollection size equals: 3. + self assert: effectiveExpressionValueCollection size equals: 8. - returnNode := (nonEffectiveExpressionOrStatementCollection select: [ - :node | node isReturn ]) first. - constant1And2 := effectiveExpressionValueCollection select: [ :node | + assigmnentNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]) first. + constantNodes := effectiveExpressionValueCollection select: [ :node | node isConstant ]. - selfVariableNode := (effectiveExpressionValueCollection select: [ - :node | node isVariable ]) first. - - constant1 := (constant1And2 select: [ :node | node value = 1 ]) first. - constant2 := (constant1And2 select: [ :node | node value = 2 ]) first. + constant3 := (constantNodes select: [ :node | node value = 3 ]) first. + variableArg := (effectiveExpressionValueCollection select: [ :node | + node isVariable and: [ node name = #arg ] ]) first. { - selfVariableNode. - constant1. - constant2 } do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode - and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. - - self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ - (sLNodeAnnotatorVisitor isInSend: constant2) not ]). + variableArg. + constant3 } do: [ :node | + self assert: + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) ]. - { - selfVariableNode. - constant1 } do: [ :node | + (effectiveExpressionValueCollection reject: [ :node | + { + variableArg. + constant3 } includes: node ]) do: [ :node | self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - sLNodeAnnotatorVisitor isInSend: node ]) ]. + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ]. - sendNode := (nonEffectiveExpressionOrStatementCollection select: [ - :node | node isSend ]) first. + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assigmnentNode and: [ + (sLNodeAnnotatorVisitor isInSend: node) not ] ] ] ] ]) ]. - 1 halt. firstStmtList := tMethod parseTree. { firstStmtList. - returnNode } do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInExpression: node) not and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + assigmnentNode } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ]) ]. - constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ - :node | node isConstant ]. - blockNode := nonEffectiveExpressionOrStatementCollection select: [ - :node | - node isStatementList and: [ node ~= firstStmtList ] ]. - constant2And3 addAll: blockNode. - constant2And3 do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInExpression: node) and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. + (nonEffectiveExpressionOrStatementCollection reject: [ :node | + { + firstStmtList. + assigmnentNode } includes: node ]) do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assigmnentNode and: [ + sLNodeAnnotatorVisitor isInAssignment: node ] ]) ]. nonEffectiveExpressionOrStatementCollection do: [ :node | - self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInSend: node) not ] ]) ]. self assert: self checkNonEffectiveExpressionOrStatementCollection. self assert: self checkEffectiveExpressionValueCollection From 92b9140d0ef6d785c2dd7c5aa45c4fe2df25c837 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 30 Jul 2025 14:21:08 +0200 Subject: [PATCH 57/86] fix a broken test --- .../SLAnnotatorVisitorTest.class.st | 96 ++++++++++--------- 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 1e79fbc32d7..bbfea609639 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -180,80 +180,88 @@ SLAnnotatorVisitorTest >> testMethodWithBlockReturn [ { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithBlockwithExpressionReturn [ - | tMethod constant1 returnNode constant2And3 firstStmtList blockNode effectiveExpressionValueConstantSet constant1And2 constant2 selfVariableNode sendNode | + | tMethod returnNode firstStmtList constant3 constantNodes constant4 constant2 constantTrue variableA assignmentNode | tMethod := ccg methodNamed: #methodWithBlockwithExpressionReturn. sLNodeAnnotatorVisitor visit: tMethod parseTree. self fillCollectionValue: tMethod. - effectiveExpressionValueConstantSet := self - getValueOfConstantNodeIn: - nonEffectiveExpressionOrStatementCollection. - self assert: nonEffectiveExpressionOrStatementCollection size - equals: 4. - self assert: effectiveExpressionValueCollection size equals: 3. + equals: 7. + self assert: effectiveExpressionValueCollection size equals: 5. returnNode := (nonEffectiveExpressionOrStatementCollection select: [ :node | node isReturn ]) first. - constant1And2 := effectiveExpressionValueCollection select: [ :node | + assignmentNode := (nonEffectiveExpressionOrStatementCollection + select: [ :node | node isAssignment ]) first. + constantNodes := effectiveExpressionValueCollection select: [ :node | node isConstant ]. - selfVariableNode := (effectiveExpressionValueCollection select: [ - :node | node isVariable ]) first. - constant1 := (constant1And2 select: [ :node | node value = 1 ]) first. - constant2 := (constant1And2 select: [ :node | node value = 2 ]) first. + constantTrue := (constantNodes select: [ :node | node value = true ]) + first. + constant2 := (constantNodes select: [ :node | node value = 2 ]) first. + constant3 := (constantNodes select: [ :node | node value = 3 ]) first. + constant4 := (constantNodes select: [ :node | node value = 4 ]) first. + variableA := (effectiveExpressionValueCollection select: [ :node | + node isVariable ]) first. { - selfVariableNode. - constant1. - constant2 } do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode - and: [ - (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ - (sLNodeAnnotatorVisitor assigningParentFor: node) isNil ] ] ]) ]. - - self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2) and: [ - (sLNodeAnnotatorVisitor isInSend: constant2) not ]). + constant3. + constant4. + constantTrue. + variableA } do: [ :node | + self assert: (sLNodeAnnotatorVisitor isInAssignment: node) ]. { - selfVariableNode. - constant1 } do: [ :node | + constant3. + constant4 } do: [ :node | + self assert: + ((sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) + = assignmentNode ]) ]. + + + (effectiveExpressionValueCollection reject: [ :node | + { + constant3. + constant4 } includes: node ]) do: [ :node | self assert: - ((sLNodeAnnotatorVisitor isEffectiveReturnValue: node) not and: [ - sLNodeAnnotatorVisitor isInSend: node ]) ]. + (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: node) not ]. + + self assert: + ((sLNodeAnnotatorVisitor isInAssignment: constant2) not and: [ + sLNodeAnnotatorVisitor isEffectiveReturnValue: constant2 ]). - sendNode := (nonEffectiveExpressionOrStatementCollection select: [ - :node | node isSend ]) first. + effectiveExpressionValueCollection do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode + and: [ (sLNodeAnnotatorVisitor isInSend: node) not ] ]) ]. - 1 halt. firstStmtList := tMethod parseTree. { firstStmtList. returnNode } do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) not and: [ - (sLNodeAnnotatorVisitor isInExpression: node) not and: [ - (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ]) ]. + self assert: + ((sLNodeAnnotatorVisitor isInExpression: node) not and: [ + (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ + (sLNodeAnnotatorVisitor assigningParentFor: node) isNil and: [ + (sLNodeAnnotatorVisitor isInReturn: node) not and: [ + (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ] ]) ]. - constant2And3 := nonEffectiveExpressionOrStatementCollection select: [ - :node | node isConstant ]. - blockNode := nonEffectiveExpressionOrStatementCollection select: [ - :node | - node isStatementList and: [ node ~= firstStmtList ] ]. - constant2And3 addAll: blockNode. - constant2And3 do: [ :node | - self assert: ((sLNodeAnnotatorVisitor isInReturn: node) and: [ - (sLNodeAnnotatorVisitor isInExpression: node) and: [ + (nonEffectiveExpressionOrStatementCollection reject: [ :node | + { + firstStmtList. + returnNode } includes: node ]) do: [ :node | + self assert: ((sLNodeAnnotatorVisitor isInExpression: node) and: [ + (sLNodeAnnotatorVisitor isInReturn: node) and: [ (sLNodeAnnotatorVisitor returningParentFor: node) = returnNode ] ]) ]. nonEffectiveExpressionOrStatementCollection do: [ :node | - self assert: (sLNodeAnnotatorVisitor isInAssignment: node) not ]. + self assert: (sLNodeAnnotatorVisitor isInSend: node) not ]. self assert: self checkNonEffectiveExpressionOrStatementCollection. self assert: self checkEffectiveExpressionValueCollection From 46f98dbc97d93edc9159dcbff20454c412751c94 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 30 Jul 2025 16:05:13 +0200 Subject: [PATCH 58/86] handle single branch conditional as arguments --- .../SLAnnotatorVisitorTest.class.st | 3 - .../Slang/SLNodeAnnotatorVisitor.class.st | 103 ++++++++++++------ .../VMMaker/SpurGenerationScavenger.class.st | 7 +- .../StackToRegisterMappingCogit.class.st | 4 +- 4 files changed, 72 insertions(+), 45 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index bbfea609639..91d096749b4 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -192,7 +192,6 @@ SLAnnotatorVisitorTest >> testMethodWithBlockwithExpressionReturn [ equals: 7. self assert: effectiveExpressionValueCollection size equals: 5. - returnNode := (nonEffectiveExpressionOrStatementCollection select: [ :node | node isReturn ]) first. assignmentNode := (nonEffectiveExpressionOrStatementCollection @@ -200,7 +199,6 @@ SLAnnotatorVisitorTest >> testMethodWithBlockwithExpressionReturn [ constantNodes := effectiveExpressionValueCollection select: [ :node | node isConstant ]. - constantTrue := (constantNodes select: [ :node | node value = true ]) first. constant2 := (constantNodes select: [ :node | node value = 2 ]) first. @@ -224,7 +222,6 @@ SLAnnotatorVisitorTest >> testMethodWithBlockwithExpressionReturn [ (sLNodeAnnotatorVisitor assigningParentFor: node) = assignmentNode ]) ]. - (effectiveExpressionValueCollection reject: [ :node | { constant3. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 1e9b0007c3a..ac3603e4078 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -516,7 +516,7 @@ SLNodeAnnotatorVisitor >> moveDownAssigningParentFrom: aNode [ assignment := self assigningParentFor: aNode. assignment ifNil: [ ^ self ]. - self transformToAssignmentFrom: assignment assignment: assignment. + self moveDownAssignmentFrom: assignment assignment: assignment. assignment parent replaceChild: assignment @@ -527,6 +527,45 @@ SLNodeAnnotatorVisitor >> moveDownAssigningParentFrom: aNode [ replacement: assignment expression ] +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> moveDownAssignmentFrom: parentNode assignment: assignmentNode [ + "recursively go throught the ast to transfrom expression of assignment + a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" + + parentNode children do: [ :child | + ((self isEffectiveAssignmentValue: child) and: [ + (self assigningParentFor: child) == assignmentNode ]) + ifTrue: [ + child parent replaceChild: child with: (TAssignmentNode new + variable: assignmentNode variable copy; + expression: child; + yourself) ] + ifFalse: [ + self moveDownAssignmentFrom: child assignment: assignmentNode. + (self isEffectivelyIgnoredInAssignment: child) ifTrue: [ + self + moveDownSingleBranchConditional: child + newBranch: (TAssignmentNode + variable: assignmentNode variable copy + expression: (TConstantNode value: 0)) ] ] ] +] + +{ #category : 'transformation' } +SLNodeAnnotatorVisitor >> moveDownReturnFrom: parentNode return: returnNode [ + "recursively go throught the ast to move down returnNode + ^ exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [^exp] ifFalse [^exp]" + + parentNode children do: [ :child | + ((self isEffectiveReturnValue: child) and: [ + (self returningParentFor: child) = returnNode ]) + ifTrue: [ + child parent replaceChild: child with: child asReturnNode ] + ifFalse: [ + child isReturn ifTrue: [ "already got a return for this branch" + ^ self ]. + self moveDownReturnFrom: child return: returnNode ] ] +] + { #category : 'transformation' } SLNodeAnnotatorVisitor >> moveDownReturningParentFrom: aNode [ "use in inlining for redondant return : return {return exp} -> {return exp}" @@ -536,7 +575,7 @@ SLNodeAnnotatorVisitor >> moveDownReturningParentFrom: aNode [ returningParent ifNil: [ ^ self ]. self - transformDirectReturnFrom: returningParent + moveDownReturnFrom: returningParent return: returningParent. returningParent parent @@ -548,6 +587,33 @@ SLNodeAnnotatorVisitor >> moveDownReturningParentFrom: aNode [ replacement: returningParent expression ] +{ #category : 'helpers-transformation' } +SLNodeAnnotatorVisitor >> moveDownSingleBranchConditional: aNode newBranch: anOtherNode [ + "does nothing is the node isnt a single branch conditional. + when moving down a assignment/return in a single branch conditional, we need to consider the missing branch which Slang would have consider is the conditional was still an argument, see generateCASTIfNotNilAsArgument: for exemple and the other methods related to single conditional as arguments" + + | newSelector | + aNode isSend ifFalse: [ ^ self ]. + aNode selector = #ifTrue: ifTrue: [ newSelector := #ifTrue:ifFalse: ]. + + aNode selector = #ifFalse: ifTrue: [ newSelector := #ifFalse:ifTrue: ]. + + aNode selector = #ifNotNil: ifTrue: [ + newSelector := #ifNotNil:ifNil: ]. + + + aNode selector = #ifNil: ifTrue: [ + newSelector := #ifNil:ifNotNil:. + anOtherNode expression: aNode receiver ]. + + newSelector ifNotNil: [ + aNode + selector: newSelector; + arguments: + aNode arguments + , { (TStatementListNode statements: { anOtherNode }) } ] +] + { #category : 'helpers-accessing-info' } SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ @@ -717,39 +783,6 @@ SLNodeAnnotatorVisitor >> topOfStack: aStack [ ^ aStack isNotEmpty and: [ aStack top ] ] -{ #category : 'transformation' } -SLNodeAnnotatorVisitor >> transformDirectReturnFrom: parentNode return: returnNode [ - "recursively go throught the ast to transfrom directReturn - ^ exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [^exp] ifFalse [^exp]" - - parentNode children do: [ :child | - ((self isEffectiveReturnValue: child) and: [ - (self returningParentFor: child) = returnNode ]) - ifTrue: [ - child parent replaceChild: child with: child asReturnNode ] - ifFalse: [ - child isReturn ifTrue: [ "already got a return for this branch" - ^ self ]. - self transformDirectReturnFrom: child return: returnNode ] ] -] - -{ #category : 'transformation' } -SLNodeAnnotatorVisitor >> transformToAssignmentFrom: parentNode assignment: assignmentNode [ - "recursively go throught the ast to transfrom expression of assignment - a = exp ifTrue: [exp] ifFalse [exp] -> ifTrue: [a = exp] ifFalse [a = exp]" - - parentNode children do: [ :child | - ((self isEffectiveAssignmentValue: child) and: [ - (self assigningParentFor: child) == assignmentNode ]) - ifTrue: [ - child parent replaceChild: child with: (TAssignmentNode new - variable: assignmentNode variable copy; - expression: child; - yourself) ] - ifFalse: [ - self transformToAssignmentFrom: child assignment: assignmentNode ] ] -] - { #category : 'visiting-main-API' } SLNodeAnnotatorVisitor >> visit: aNode [ diff --git a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st index 41cef08ea62..ea9b083b961 100644 --- a/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st +++ b/smalltalksrc/VMMaker/SpurGenerationScavenger.class.st @@ -823,14 +823,11 @@ SpurGenerationScavenger >> nextCorpseOffset: corpse [ SpurGenerationScavenger >> nextCorpseOrNil: corpse [ "corpse is the corpse of a weak array that has been added to the weakList. Answer the next object on the list, or nil if none." - - | listOffset | self assert: (manager isYoung: corpse). listOffset := self nextCorpseOffset: corpse. - ^ listOffset ~= 0 - ifTrue: [ self corpseForCorpseOffset: listOffset ] - ifFalse: [ nil ] + ^listOffset ~= 0 ifTrue: + [self corpseForCorpseOffset: listOffset] ] { #category : 'weakness and ephemerality' } diff --git a/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st b/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st index 22c4572ab76..00bee5805cd 100644 --- a/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st +++ b/smalltalksrc/VMMaker/StackToRegisterMappingCogit.class.st @@ -2602,11 +2602,11 @@ StackToRegisterMappingCogit >> genSpecialSelectorArithmetic [ [AndRR] -> [argIsConst ifTrue: [self AndCq: argInt R: ReceiverResultReg] ifFalse: [self AndR: Arg0Reg R: ReceiverResultReg]. - jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [nil]]. + jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0]]. [OrRR] -> [argIsConst ifTrue: [self OrCq: argInt R: ReceiverResultReg] ifFalse: [self OrR: Arg0Reg R: ReceiverResultReg]. - jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0] ifNil: [nil]] }. + jumpContinue := jumpNotSmallInts ifNotNil: [self Jump: 0]] }. jumpNotSmallInts ifNil: [jumpContinue ifNil: "overflow cannot happen" [self annotateInstructionForBytecode. From 236790a4d082a87decdd8c123c40c574c87fe530 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 30 Jul 2025 16:40:49 +0200 Subject: [PATCH 59/86] fix broken tests --- .../Slang-Tests/SLInliningTest.class.st | 17 +++++++++++------ .../Slang/SLNodeAnnotatorVisitor.class.st | 8 +++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 5fdd2b6d9a7..325e4735a85 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -804,6 +804,8 @@ methodAInlineReturningInlinedIfTrueInAssignment(void) /* begin methodB */ a = 2 + 2; /* end methodB */ + } else { + a = 0; } return 0; }' @@ -1021,6 +1023,9 @@ methodWithAssignmentOnRepeatInlined(void) if (methodB()) { a = 5; goto l2; + } else { + a = 0; + goto l2; } } l2: @@ -1132,6 +1137,9 @@ methodWithAssignmentOnWhileTrueBinaryInlined(void) a = 2 + 2; goto l2; /* end methodB */ + } else { + a = 0; + goto l2; } } l2: @@ -1166,6 +1174,9 @@ methodWithAssignmentOnWhileTrueInlined(void) if (methodB()) { a = 5; goto l2; + } else { + a = 0; + goto l2; } } while (1); l2: @@ -1495,9 +1506,3 @@ SLInliningTest >> useNewInlining [ inliner := SLInlinerWithAnnotation new ] - -{ #category : 'parameters' } -SLInliningTest >> useOldInlining [ - - inliner := SLOldInlineStrategy new -] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index ac3603e4078..3b92a0eb5d0 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -607,11 +607,13 @@ SLNodeAnnotatorVisitor >> moveDownSingleBranchConditional: aNode newBranch: anOt anOtherNode expression: aNode receiver ]. newSelector ifNotNil: [ + | newArgument last | + newArgument := TStatementListNode statements: { anOtherNode }. + last := aNode arguments first lastNonCommentStatement. + last isGoTo ifTrue: [ newArgument addLast: last copy ]. aNode selector: newSelector; - arguments: - aNode arguments - , { (TStatementListNode statements: { anOtherNode }) } ] + arguments: aNode arguments , { newArgument } ] ] { #category : 'helpers-accessing-info' } From 421be309a20d2ecf2672c96a57448d8ee6934ef5 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 30 Jul 2025 17:06:13 +0200 Subject: [PATCH 60/86] generate less unecessary goTo --- smalltalksrc/Slang/TMethod.class.st | 48 +++++++++---------- smalltalksrc/Slang/TParseNode.class.st | 6 +++ .../Slang/TStatementListNode.class.st | 6 +++ 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/smalltalksrc/Slang/TMethod.class.st b/smalltalksrc/Slang/TMethod.class.st index 1b35a3b9b8c..f71087915f4 100644 --- a/smalltalksrc/Slang/TMethod.class.st +++ b/smalltalksrc/Slang/TMethod.class.st @@ -2542,31 +2542,31 @@ TMethod >> terminateConditionalDefineFor: compileTimeOptionPragmas on: aStream [ { #category : 'inlining' } TMethod >> transformReturnSubExpression: node toAssignmentOf: exitVar andGoto: exitLabel unless: eliminateReturnSelfs into: aBinaryBlock [ + | expr replacement | - expr := node isReturn ifTrue: [node expression] ifFalse: [node]. - replacement := (expr isVariable "Eliminate ^self's" - and: [expr name = 'self' - and: [eliminateReturnSelfs]]) - ifTrue: [nil] - ifFalse: - [exitVar - ifNil: [expr] - ifNotNil: [TAssignmentNode new - setVariable: (TVariableNode new setName: exitVar) - expression: expr]]. - node == parseTree lastNonCommentStatement - ifTrue: - [aBinaryBlock value: replacement value: false] - ifFalse: - [replacement := replacement - ifNil: [TGoToNode label: exitLabel] - ifNotNil: - [TStatementListNode new - setArguments: #() - statements: {replacement. - TGoToNode label: exitLabel}; - yourself]. - aBinaryBlock value: replacement value: true] + expr := node isReturn + ifTrue: [ node expression ] + ifFalse: [ node ]. + replacement := (expr isVariable and: [ + expr name = 'self' and: [ eliminateReturnSelfs ] ]) + ifTrue: [ nil ] + ifFalse: [ + exitVar ifNil: [ expr ] ifNotNil: [ + TAssignmentNode new + setVariable: (TVariableNode new setName: exitVar) + expression: expr ] ]. "Eliminate ^self's" + (parseTree lastExpression includes: node) + ifTrue: [ aBinaryBlock value: replacement value: false ] + ifFalse: [ + replacement := replacement + ifNil: [ TGoToNode label: exitLabel ] + ifNotNil: [ + TStatementListNode new + setArguments: #( ) statements: { + replacement. + (TGoToNode label: exitLabel) }; + yourself ]. + aBinaryBlock value: replacement value: true ] ] { #category : 'type inference' } diff --git a/smalltalksrc/Slang/TParseNode.class.st b/smalltalksrc/Slang/TParseNode.class.st index a9f73adcb73..99f72af3594 100644 --- a/smalltalksrc/Slang/TParseNode.class.st +++ b/smalltalksrc/Slang/TParseNode.class.st @@ -371,6 +371,12 @@ TParseNode >> isVariable [ ^false ] +{ #category : 'accessing' } +TParseNode >> lastExpression [ + + ^ { self } asIdentitySet +] + { #category : 'accessing' } TParseNode >> nameOrValue [ diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index 024419ad1f2..bc5a8f00f0b 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -573,6 +573,12 @@ TStatementListNode >> last [ ^ self statements last ] +{ #category : 'accessing' } +TStatementListNode >> lastExpression [ + + ^ self lastNonCommentStatement lastExpression +] + { #category : 'accessing' } TStatementListNode >> lastNonCommentOrGoToOrLabelStatement [ "the last statement can be a comment if the TStatementList has been through inlining, return the actual last statement" From 13ebdb1135758ae92987b9f7eeb610c4e808ca17 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 30 Jul 2025 17:06:56 +0200 Subject: [PATCH 61/86] commit all the method --- smalltalksrc/Slang/TSendNode.class.st | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/smalltalksrc/Slang/TSendNode.class.st b/smalltalksrc/Slang/TSendNode.class.st index 12a62a07a56..f050d4fca67 100644 --- a/smalltalksrc/Slang/TSendNode.class.st +++ b/smalltalksrc/Slang/TSendNode.class.st @@ -553,6 +553,15 @@ TSendNode >> isValueExpansion [ and: [selector keywords allSatisfy: [:k| #('value' 'value:') includes: k]] ] +{ #category : 'accessing' } +TSendNode >> lastExpression [ + + | lastExpressions | + self isConditionalSend ifFalse: [ ^ super lastExpression ]. + lastExpressions := arguments collect: [ :arg | arg lastExpression ]. + ^ lastExpressions +] + { #category : 'accessing' } TSendNode >> name [ From 90c5aaeede5a58120784f6bae59bbee17b7715c6 Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 31 Jul 2025 11:35:35 +0200 Subject: [PATCH 62/86] correctly supress all the unecessary goTo --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 2 +- smalltalksrc/Slang/TSendNode.class.st | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 2b4118e895a..ae2070eeed9 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -721,7 +721,7 @@ SLInlinerWithAnnotation >> label: exitLabel for: aTMethod [ aTMethod parseTree nodesDo: [ :node | | list | (node isStatementList and: [ - node statements notEmpty and: [ + node isComment not and: [ node lastNonCommentStatement isStatementList ] ]) ifTrue: [ list := node lastNonCommentStatement statements. node replaceChild: node lastNonCommentStatement withList: list ] ]. diff --git a/smalltalksrc/Slang/TSendNode.class.st b/smalltalksrc/Slang/TSendNode.class.st index f050d4fca67..f98fa7b21a9 100644 --- a/smalltalksrc/Slang/TSendNode.class.st +++ b/smalltalksrc/Slang/TSendNode.class.st @@ -558,7 +558,8 @@ TSendNode >> lastExpression [ | lastExpressions | self isConditionalSend ifFalse: [ ^ super lastExpression ]. - lastExpressions := arguments collect: [ :arg | arg lastExpression ]. + lastExpressions := IdentitySet new. + arguments do: [ :arg | lastExpressions addAll: arg lastExpression ]. ^ lastExpressions ] From ae60a5acd7ebbda635b2f185c6b29d03ffa47e6b Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 31 Jul 2025 15:24:06 +0200 Subject: [PATCH 63/86] add more generated comments --- smalltalksrc/Slang/SLInliner.class.st | 2 +- .../Slang/SLInlinerWithAnnotation.class.st | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index cb2542d6449..1cb309b7c2f 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -784,6 +784,6 @@ SLInliner >> tryToInlineMethodsInCurrentMethod [ currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. currentMethod isComplete ifTrue: [ didSomething := true ] ]. - + ^ didSomething ] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index ae2070eeed9..f42e7a7643f 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -257,7 +257,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional 1. the method arguments are all substitutable nodes, and 2. the method to be inlined contains no additional embedded returns." - | sel meth parametersToReplaceByArgument argsForInlining substitutionDict | + | sel meth parametersToReplaceByArgument argsForInlining substitutionDict parseTree | sel := aSendNode selector. meth := (codeGenerator methodNamed: sel) copy. meth ifNil: [ ^ self inlineBuiltin: aSendNode ]. @@ -288,9 +288,15 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional meth locals: Set new. aBoolean ifFalse: [ sLNodeAnnotatorVisitor copyWithoutReturn: meth ]. - "same 'optimization' as before" - meth parseTree children size = 1 ifTrue: [ - meth parseTree: meth parseTree last ]. + "same 'optimization' as before but with comments if possible" + parseTree := meth parseTree. + parseTree children size = 1 + ifTrue: [ meth parseTree: parseTree last ] + ifFalse: [ + parseTree addFirst: + (TLabeledCommentNode new setComment: 'begin ' , sel). + parseTree addLast: + (TLabeledCommentNode new setComment: 'end ' , sel) ]. ^ meth parseTree ] @@ -417,7 +423,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ otherwise the assignee variable type must match the return type of the inlinee. Return types are not propagated." - | sel callee exitLabel inlineStmts label omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent | + | sel callee exitLabel inlineStmts omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent | sel := aSendNode selector. callee := codeGenerator methodNamed: sel. directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: @@ -482,7 +488,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ send: aSendNode except: omittedParameters); addFirst: - (label := TLabeledCommentNode new setComment: 'begin ' , sel). + (TLabeledCommentNode new setComment: 'begin ' , sel). exitLabel ifNotNil: [ From b4c4d865547a5b3ef6864fa74ac192f3bd9eac42 Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 31 Jul 2025 16:02:39 +0200 Subject: [PATCH 64/86] fix tests missing the new comments --- smalltalksrc/Slang-Tests/SLInliningTest.class.st | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 325e4735a85..f25b64467dd 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -163,7 +163,7 @@ SLInliningTest >> test2ChainInliningMethodAWithArgumentsInlined [ static sqInt methodAWithArgumentsInlined(void) { - return methodB((3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */)); + return methodB((/* begin methodCAlwaysInlined */ 3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */ /* end methodCAlwaysInlined */)); }' ] @@ -185,7 +185,7 @@ SLInliningTest >> test2ChainInliningMethodAWithReturningSendArgumentsInlined [ static sqInt methodAWithReturningSendArgumentsInlined(void) { - return methodB((3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */)); + return methodB((/* begin methodCAlwaysInlined */ 3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */ /* end methodCAlwaysInlined */)); }' ] @@ -1363,6 +1363,7 @@ static sqInt methodWithIfTrueReceiverInlined(void) { { + /* begin methodWithMultipleBooleanReturn */ if (methodB()) { goto l2; } @@ -1375,6 +1376,7 @@ methodWithIfTrueReceiverInlined(void) ; /* end methodCMultipleReturn */ /* end methodWithMultipleBooleanReturn; fall through */ + /* end methodWithMultipleBooleanReturn */ /* begin methodB */ 2 + 2; /* end methodB */ @@ -1404,6 +1406,7 @@ static sqInt methodWithReturningIfTrueReceiverInlined(void) { { + /* begin methodWithMultipleBooleanReturn */ if (methodB()) { goto l2; } @@ -1416,6 +1419,7 @@ methodWithReturningIfTrueReceiverInlined(void) ; /* end methodCMultipleReturn */ /* end methodWithMultipleBooleanReturn; fall through */ + /* end methodWithMultipleBooleanReturn */ /* begin methodB */ 2 + 2; /* end methodB */ From 0c9fd73fee282523a8bba43e6ce2e3ca40e8ba07 Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 31 Jul 2025 18:00:34 +0200 Subject: [PATCH 65/86] clean up the code --- .../Slang/SLInlinerWithAnnotation.class.st | 95 +++++++++++-------- .../Slang/SLNodeAnnotatorVisitor.class.st | 20 ++-- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index f42e7a7643f..1b9a2931235 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -9,6 +9,22 @@ Class { #package : 'Slang' } +{ #category : 'transformation' } +SLInlinerWithAnnotation >> addBeginAndEndCommentsFor: aTStatementList selector: aSelector exitLabel: aLabel [ + + aTStatementList addFirst: + (TLabeledCommentNode new setComment: 'begin ' , aSelector). + aLabel + ifNotNil: [ + aTStatementList addLast: + (TLabeledCommentNode new + setLabel: aLabel + comment: 'end ' , aSelector) ] + ifNil: [ + aTStatementList addLast: + (TLabeledCommentNode new setComment: 'end ' , aSelector) ] +] + { #category : 'inlining-preparation' } SLInlinerWithAnnotation >> annotateCurrentMethod [ @@ -257,9 +273,9 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional 1. the method arguments are all substitutable nodes, and 2. the method to be inlined contains no additional embedded returns." - | sel meth parametersToReplaceByArgument argsForInlining substitutionDict parseTree | - sel := aSendNode selector. - meth := (codeGenerator methodNamed: sel) copy. + | selector meth parametersToReplaceByArgument argsForInlining substitutionDict parseTree | + selector := aSendNode selector. + meth := (codeGenerator methodNamed: selector) copy. meth ifNil: [ ^ self inlineBuiltin: aSendNode ]. parametersToReplaceByArgument := Set withAll: currentMethod args. argsForInlining := aSendNode argumentsForInliningCodeGenerator: @@ -286,17 +302,17 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional see asCASTIn: vs asCASTExpressionIn: for stmtList. " meth locals: Set new. - aBoolean ifFalse: [ sLNodeAnnotatorVisitor copyWithoutReturn: meth ]. + aBoolean ifFalse: [ SLNodeAnnotatorVisitor copyWithoutReturn: meth ]. "same 'optimization' as before but with comments if possible" parseTree := meth parseTree. parseTree children size = 1 ifTrue: [ meth parseTree: parseTree last ] ifFalse: [ - parseTree addFirst: - (TLabeledCommentNode new setComment: 'begin ' , sel). - parseTree addLast: - (TLabeledCommentNode new setComment: 'end ' , sel) ]. + self + addBeginAndEndCommentsFor: parseTree + selector: selector + exitLabel: nil ]. ^ meth parseTree ] @@ -423,9 +439,9 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ otherwise the assignee variable type must match the return type of the inlinee. Return types are not propagated." - | sel callee exitLabel inlineStmts omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent | - sel := aSendNode selector. - callee := codeGenerator methodNamed: sel. + | selector callee exitLabel inlineStmts omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent | + selector := aSendNode selector. + callee := codeGenerator methodNamed: selector. directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: aSendNode. (sLNodeAnnotatorVisitor isEffectiveAssignmentValue: aSendNode) @@ -471,32 +487,22 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ addVarsDeclarationsAndLabelsOf: callee except: omittedParameters. - callee hasReturn ifTrue: [ - directReturn ifFalse: [ - exitLabel := currentMethod unusedLabelForInliningInto: - currentMethod. - (self label: exitLabel for: callee) - ifTrue: [ currentMethod labels add: exitLabel ] - ifFalse: [ "is label used?" exitLabel := nil ] ] ]. + exitLabel := self + replaceReturnWithGoToIfNeededIn: callee + directReturn: directReturn. self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. inlineStmts := callee parseTree. - inlineStmts - addAllFirst: (self - argAssignmentsFor: callee - send: aSendNode - except: omittedParameters); - addFirst: - (TLabeledCommentNode new setComment: 'begin ' , sel). - - exitLabel - ifNotNil: [ - inlineStmts addLast: - (TLabeledCommentNode new setLabel: exitLabel comment: 'end ' , sel) ] - ifNil: [ - inlineStmts addLast: - (TLabeledCommentNode new setComment: 'end ' , sel) ]. + inlineStmts addAllFirst: (self + argAssignmentsFor: callee + send: aSendNode + except: omittedParameters). + + self + addBeginAndEndCommentsFor: inlineStmts + selector: selector + exitLabel: exitLabel. inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" inlineStmts statements: OrderedCollection new ]. @@ -797,6 +803,20 @@ SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ except: method args ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> replaceReturnWithGoToIfNeededIn: callee directReturn: directReturn [ + + | exitLabel | + callee hasReturn ifTrue: [ + directReturn ifFalse: [ + exitLabel := currentMethod unusedLabelForInliningInto: + currentMethod. + (self label: exitLabel for: callee) + ifTrue: [ currentMethod labels add: exitLabel ] + ifFalse: [ "is label used?" ^ nil ] ] ]. + ^ exitLabel +] + { #category : 'transformation' } SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aTMethod [ @@ -816,15 +836,6 @@ SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aTMeth sLNodeAnnotatorVisitor moveDownAssigningParentFrom: replacement ] ] ] -{ #category : 'transformation' } -SLInlinerWithAnnotation >> revert: aSendNode replacement: aTMethod [ - - aTMethod parseTree parent - replaceChild: aTMethod parseTree - with: aSendNode. - aTMethod parseTree parent: aTMethod -] - { #category : 'inlining-preparation' } SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 3b92a0eb5d0..b0349a57e80 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -15,6 +15,16 @@ Class { #package : 'Slang' } +{ #category : 'transformation' } +SLNodeAnnotatorVisitor class >> copyWithoutReturn: aTMethod [ + + | parseTree returns | + parseTree := aTMethod parseTree. + returns := parseTree allReturns. + returns do: [ :return | + return parent replaceChild: return with: return expression ] +] + { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> activateCurrentSouldBePartiallyIgnored [ @@ -205,16 +215,6 @@ SLNodeAnnotatorVisitor >> cleanInfoFrom: aNode [ info removeKey: aNode ] -{ #category : 'transformation' } -SLNodeAnnotatorVisitor >> copyWithoutReturn: aTMethod [ - - | parseTree returns | - parseTree := aTMethod parseTree. - returns := parseTree allReturns. - returns do: [ :return | - return parent replaceChild: return with: return expression ] -] - { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> deActivateCurrentSouldBePartiallyIgnored [ From 9a3050302a658764dcce4525bd30f4fbdd837950 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 1 Aug 2025 15:12:44 +0200 Subject: [PATCH 66/86] optimize inlining + get rid of all the non necessary '{}' --- .../SLASTTransformationTest.class.st | 24 +- .../SLDeadCodeEliminationTest.class.st | 642 +++++------------- .../Slang-Tests/SLInliningTest.class.st | 8 +- .../Slang/SLInlinerWithAnnotation.class.st | 93 +-- .../Slang/SLNodeAnnotatorVisitor.class.st | 40 +- .../Slang/TStatementListNode.class.st | 10 +- 6 files changed, 263 insertions(+), 554 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st b/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st index 7c8b8686bd0..a71a6e34bb5 100644 --- a/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st +++ b/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st @@ -25,7 +25,7 @@ SLASTTransformationTest >> testMethodWithBlockValue [ | translation tMethod | tMethod := ccg methodNamed: #methodWithBlockValue. - sLExpandASTVisitor currentMethod: tMethod . + sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. translation := self translate: tMethod. @@ -39,9 +39,7 @@ methodWithBlockValue(void) { sqInt arg; - { - 2 + 1; - } + 2 + 1; return 0; }' ] @@ -68,9 +66,7 @@ methodWithBlockValueValue(void) sqInt arg1; sqInt arg2; - { - 2 + 3; - } + 2 + 3; return 0; }' ] @@ -126,10 +122,8 @@ methodWithBockValueInAssignment(void) sqInt arg; sqInt var; - { - emptyMethod(1); - var = 5; - } + emptyMethod(1); + var = 5; return 0; }' ] @@ -157,11 +151,9 @@ methodWithBockValueValueInAssignment(void) sqInt arg2; sqInt var; - { - emptyMethod(1); - emptyMethod(2); - var = 5; - } + emptyMethod(1); + emptyMethod(2); + var = 5; return 0; }' ] diff --git a/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st b/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st index ce7ef2e3d21..8fc887a55fc 100644 --- a/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st +++ b/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st @@ -45,9 +45,7 @@ SLDeadCodeEliminationTest >> testConditionalWithOnlyCommentNoSendInReceiver [ static void conditionalWithOnlyCommentNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_conditionalWithOnlyCommentNoSendInReceiver) { - { - return; - } + return; }' ] @@ -74,12 +72,8 @@ SLDeadCodeEliminationTest >> testConditionalWithOnlyCommentSendInReceiver [ static void conditionalWithOnlyCommentSendInReceiver(SLDeadCodeEliminationTestClass * self_in_conditionalWithOnlyCommentSendInReceiver) { - { - method(self_in_conditionalWithOnlyCommentSendInReceiver, method(self_in_conditionalWithOnlyCommentSendInReceiver)); - } - { - return; - } + method(self_in_conditionalWithOnlyCommentSendInReceiver, method(self_in_conditionalWithOnlyCommentSendInReceiver)); + return; }' ] @@ -103,9 +97,7 @@ static void methodAddingCallInCoerce(SLDeadCodeEliminationTestClass * self_in_methodAddingCallInCoerce) { ((type) ((method(self_in_methodAddingCallInCoerce)) + (method(self_in_methodAddingCallInCoerce))) ); - { - return; - } + return; }' ] @@ -134,9 +126,7 @@ methodWithBlockValueAssignmentIntoSameVariableInArguments(SLDeadCodeEliminationT sqInt var; var = 0; - { - method(self_in_methodWithBlockValueAssignmentIntoSameVariableInArguments, var); - } + method(self_in_methodWithBlockValueAssignmentIntoSameVariableInArguments, var); return var; }' ] @@ -167,10 +157,8 @@ methodWithBlockValueAssignmentNotIntoSameVariableInArguments(SLDeadCodeEliminati sqInt var2; var2 = 0; - { - method(self_in_methodWithBlockValueAssignmentNotIntoSameVariableInArguments, var2); - var = var2; - } + method(self_in_methodWithBlockValueAssignmentNotIntoSameVariableInArguments, var2); + var = var2; return var; }' ] @@ -204,10 +192,8 @@ methodWithBlockValueValueAssignmentIntoSameVariableInArguments(SLDeadCodeElimina var = 0; var2 = 0; - { - method(self_in_methodWithBlockValueValueAssignmentIntoSameVariableInArguments, var2); - method(self_in_methodWithBlockValueValueAssignmentIntoSameVariableInArguments, var); - } + method(self_in_methodWithBlockValueValueAssignmentIntoSameVariableInArguments, var2); + method(self_in_methodWithBlockValueValueAssignmentIntoSameVariableInArguments, var); return var; }' ] @@ -240,14 +226,10 @@ methodWithBlockValueValueAssignmentNotIntoSameVariableInArguments(SLDeadCodeElim var = 0; var2 = 0; - { - method(self_in_methodWithBlockValueValueAssignmentNotIntoSameVariableInArguments, var); - method(self_in_methodWithBlockValueValueAssignmentNotIntoSameVariableInArguments, var2); - var = var2; - } - { - return; - } + method(self_in_methodWithBlockValueValueAssignmentNotIntoSameVariableInArguments, var); + method(self_in_methodWithBlockValueValueAssignmentNotIntoSameVariableInArguments, var2); + var = var2; + return; }' ] @@ -273,9 +255,7 @@ static void methodWithCallInCoerce(SLDeadCodeEliminationTestClass * self_in_methodWithCallInCoerce) { ((type) (method(self_in_methodWithCallInCoerce)) ); - { - return; - } + return; }' ] @@ -352,9 +332,7 @@ methodWithDeadCodeInRepeat(SLDeadCodeEliminationTestClass * self_in_methodWithDe while (1) { method(self_in_methodWithDeadCodeInRepeat); } - { - return; - } + return; }' ] @@ -474,9 +452,7 @@ methodWithDeadCodeInWhileFalseLastExpressionIsLeaf(SLDeadCodeEliminationTestClas do { method(self_in_methodWithDeadCodeInWhileFalseLastExpressionIsLeaf); } while (!1); - { - return; - } + return; }' ] @@ -508,9 +484,7 @@ methodWithDeadCodeInWhileFalseBinaryNoSendInReceiver(SLDeadCodeEliminationTestCl while (!(var == 1)) { method(self_in_methodWithDeadCodeInWhileFalseBinaryNoSendInReceiver); } - { - return; - } + return; }' ] @@ -537,9 +511,7 @@ methodWithDeadCodeInWhileFalseBinarySendInReceiver(SLDeadCodeEliminationTestClas while (!(method(self_in_methodWithDeadCodeInWhileFalseBinarySendInReceiver))) { method(self_in_methodWithDeadCodeInWhileFalseBinarySendInReceiver); } - { - return; - } + return; }' ] @@ -566,9 +538,7 @@ methodWithDeadCodeInWhileTrueLastExpressionIsLeaf(SLDeadCodeEliminationTestClass do { method(self_in_methodWithDeadCodeInWhileTrueLastExpressionIsLeaf); } while (1); - { - return; - } + return; }' ] @@ -600,9 +570,7 @@ methodWithDeadCodeInWhileTrueBinaryNoSendInReceiver(SLDeadCodeEliminationTestCla while (var == 0) { method(self_in_methodWithDeadCodeInWhileTrueBinaryNoSendInReceiver); } - { - return; - } + return; }' ] @@ -629,9 +597,7 @@ methodWithDeadCodeInWhileTrueBinarySendInReceiver(SLDeadCodeEliminationTestClass while (method(self_in_methodWithDeadCodeInWhileTrueBinarySendInReceiver)) { method(self_in_methodWithDeadCodeInWhileTrueBinarySendInReceiver); } - { - return; - } + return; }' ] @@ -665,9 +631,7 @@ methodWithEmptyCaseOfNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_m default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -693,9 +657,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyCaseOfOtherwiseNoSendInReceiver static void methodWithEmptyCaseOfOtherwiseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyCaseOfOtherwiseNoSendInReceiver, sqInt anInt) { - { - return; - } + return; }' ] @@ -721,12 +683,8 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyCaseOfOtherwiseSendInReceiver [ static void methodWithEmptyCaseOfOtherwiseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyCaseOfOtherwiseSendInReceiver) { - { - method(self_in_methodWithEmptyCaseOfOtherwiseSendInReceiver, 2); - } - { - return; - } + method(self_in_methodWithEmptyCaseOfOtherwiseSendInReceiver, 2); + return; }' ] @@ -761,9 +719,7 @@ methodWithEmptyCaseOfSendInReceiver(SLDeadCodeEliminationTestClass * self_in_met default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -788,9 +744,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseIfTrueAndNoSendInReceiver static void methodWithEmptyIfFalseIfTrueAndNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfFalseIfTrueAndNoSendInReceiver) { - { - return; - } + return; }' ] @@ -815,15 +769,11 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseIfTrueAndSendInReceiver [ static void methodWithEmptyIfFalseIfTrueAndSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver) { - { - method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver); - method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver, 1); - method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver); - method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver, method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver)); - } - { - return; - } + method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver); + method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver, 1); + method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver); + method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver, method(self_in_methodWithEmptyIfFalseIfTrueAndSendInReceiver)); + return; }' ] @@ -972,9 +922,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNilIfNotNilAndNoSendInReceiver static void methodWithEmptyIfNilIfNotNilAndNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfNilIfNotNilAndNoSendInReceiver) { - { - return; - } + return; }' ] @@ -999,12 +947,8 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNilIfNotNilAndSendInReceiver [ static void methodWithEmptyIfNilIfNotNilAndSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfNilIfNotNilAndSendInReceiver) { - { - method(self_in_methodWithEmptyIfNilIfNotNilAndSendInReceiver); - } - { - return; - } + method(self_in_methodWithEmptyIfNilIfNotNilAndSendInReceiver); + return; }' ] @@ -1029,9 +973,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNotNilIfNilAndNoSendInReceiver static void methodWithEmptyIfNotNilIfNilAndNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfNotNilIfNilAndNoSendInReceiver) { - { - return; - } + return; }' ] @@ -1056,12 +998,8 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNotNilIfNilAndSendInReceiver [ static void methodWithEmptyIfNotNilIfNilAndSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfNotNilIfNilAndSendInReceiver) { - { - method(self_in_methodWithEmptyIfNotNilIfNilAndSendInReceiver); - } - { - return; - } + method(self_in_methodWithEmptyIfNotNilIfNilAndSendInReceiver); + return; }' ] @@ -1086,9 +1024,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueIfFalseAndNoSendInReceiver static void methodWithEmptyIfTrueIfFalseAndNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfTrueIfFalseAndNoSendInReceiver) { - { - return; - } + return; }' ] @@ -1113,15 +1049,11 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueIfFalseAndSendInReceiver [ static void methodWithEmptyIfTrueIfFalseAndSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver) { - { - method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver); - method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver, 1); - method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver); - method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver, method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver)); - } - { - return; - } + method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver); + method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver, 1); + method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver); + method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver, method(self_in_methodWithEmptyIfTrueIfFalseAndSendInReceiver)); + return; }' ] @@ -1269,9 +1201,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyWhileFalseNoSendInCondition [ static void methodWithEmptyWhileFalseNoSendInCondition(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyWhileFalseNoSendInCondition) { - { - return; - } + return; }' ] @@ -1294,12 +1224,8 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyWhileFalseSendInCondition [ static void methodWithEmptyWhileFalseSendInCondition(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyWhileFalseSendInCondition) { - { - method(self_in_methodWithEmptyWhileFalseSendInCondition); - } - { - return; - } + method(self_in_methodWithEmptyWhileFalseSendInCondition); + return; }' ] @@ -1323,9 +1249,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyWhileTrueNoSendInCondition [ static void methodWithEmptyWhileTrueNoSendInCondition(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyWhileTrueNoSendInCondition) { - { - return; - } + return; }' ] @@ -1348,12 +1272,8 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyWhileTrueSendInCondition [ static void methodWithEmptyWhileTrueSendInCondition(SLDeadCodeEliminationTestClass * self_in_methodWithEmptyWhileTrueSendInCondition) { - { - method(self_in_methodWithEmptyWhileTrueSendInCondition); - } - { - return; - } + method(self_in_methodWithEmptyWhileTrueSendInCondition); + return; }' ] @@ -1716,9 +1636,7 @@ methodWithNeverUsedLocalsFromBlock(SLDeadCodeEliminationTestClass * self_in_meth int var1; var1 = 5; - { - var1 += 1; - } + var1 += 1; return var1; }' ] @@ -1750,9 +1668,7 @@ methodWithNeverUsedLocalsFromBlockStatement(SLDeadCodeEliminationTestClass * sel sqInt var2; var1 = 4; - { - var2 = var1 + 1; - } + var2 = var1 + 1; return var2; }' ] @@ -1782,10 +1698,8 @@ methodWithNeverUsedLocalsFromBlockWithExpressionInArguments(SLDeadCodeEliminatio sqInt arg1; sqInt var1; - { - arg1 = 5 + 1; - var1 = arg1 + 1; - } + arg1 = 5 + 1; + var1 = arg1 + 1; return var1; }' ] @@ -1908,9 +1822,7 @@ methodWithOnlyBlockValueAssignmentNotIntoSameVariableInArguments(SLDeadCodeElimi sqInt var2; var2 = 0; - { - var = var2; - } + var = var2; return var; }' ] @@ -1940,9 +1852,7 @@ methodWithOnlyComment(SLDeadCodeEliminationTestClass * self_in_methodWithOnlyCom { /* begin method */ /* end method */ - { - return; - } + return; }' ] @@ -2051,9 +1961,7 @@ methodWithOnlyDeadCodeInWhileFalseBinarySendInReceiver(SLDeadCodeEliminationTest sqInt var; var = 0; - { - method(self_in_methodWithOnlyDeadCodeInWhileFalseBinarySendInReceiver); - } + method(self_in_methodWithOnlyDeadCodeInWhileFalseBinarySendInReceiver); return var; }' ] @@ -2137,9 +2045,7 @@ methodWithOnlyDeadCodeInWhileTrueBinarySendInReceiver(SLDeadCodeEliminationTestC sqInt var; var = 0; - { - method(self_in_methodWithOnlyDeadCodeInWhileTrueBinarySendInReceiver); - } + method(self_in_methodWithOnlyDeadCodeInWhileTrueBinarySendInReceiver); return var; }' ] @@ -2192,9 +2098,7 @@ SLDeadCodeEliminationTest >> testMethodWithOnlyUselessAssignment [ static void methodWithOnlyUselessAssignment(SLDeadCodeEliminationTestClass * self_in_methodWithOnlyUselessAssignment) { - { - return; - } + return; }' ] @@ -2280,9 +2184,7 @@ SLDeadCodeEliminationTest >> testMethodWithSendWithNoSideEffectInCoerce [ static void methodWithSendWithNoSideEffectInCoerce(SLDeadCodeEliminationTestClass * self_in_methodWithSendWithNoSideEffectInCoerce) { - { - return; - } + return; }' ] @@ -2305,9 +2207,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstant [ static void methodWithUnusedConstant(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstant) { - { - return; - } + return; }' ] @@ -2341,16 +2241,12 @@ methodWithUnusedConstantAndReturnInCaseOfNoSendInExpression(SLDeadCodeEliminatio break; case 6: { - { - return; - } + return; } default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -2526,9 +2422,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfAndUnusedConsta static sqInt methodWithUnusedConstantInCaseOfAndUnusedConstantInOtherwiseSendInExpression(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInCaseOfAndUnusedConstantInOtherwiseSendInExpression) { - { - method(self_in_methodWithUnusedConstantInCaseOfAndUnusedConstantInOtherwiseSendInExpression, 3); - } + method(self_in_methodWithUnusedConstantInCaseOfAndUnusedConstantInOtherwiseSendInExpression, 3); return 0; }' ] @@ -2668,14 +2562,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseIfTrueSendInRe static void methodWithUnusedConstantInIfFalseIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver); - method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver, 3); - method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver, method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver); + method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver, 3); + method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver, method(self_in_methodWithUnusedConstantInIfFalseIfTrueSendInReceiver)); + return; }' ] @@ -2699,9 +2589,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseNoSendInReceiv static void methodWithUnusedConstantInIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfFalseNoSendInReceiver) { - { - return; - } + return; }' ] @@ -2725,14 +2613,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseSendInReceiver static void methodWithUnusedConstantInIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfFalseSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver); - method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver, 3); - method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver, method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver); + method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver, 3); + method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver, method(self_in_methodWithUnusedConstantInIfFalseSendInReceiver)); + return; }' ] @@ -2756,9 +2640,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilIfNotNilNoSendIn static void methodWithUnusedConstantInIfNilIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNilIfNotNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -2782,12 +2664,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilIfNotNillSendInR static void methodWithUnusedConstantInIfNilIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNilIfNotNilSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfNilIfNotNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNilIfNotNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfNilIfNotNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNilIfNotNilSendInReceiver)); + return; }' ] @@ -2811,9 +2689,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilNoSendInReceiver static void methodWithUnusedConstantInIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -2837,12 +2713,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilSendInReceiver [ static void methodWithUnusedConstantInIfNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNilSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNilSendInReceiver)); + return; }' ] @@ -2866,9 +2738,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilIfNilNoSendIn static void methodWithUnusedConstantInIfNotNilIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNotNilIfNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -2892,12 +2762,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilIfNilSendInRe static void methodWithUnusedConstantInIfNotNilIfNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNotNilIfNilSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfNotNilIfNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNotNilIfNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfNotNilIfNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNotNilIfNilSendInReceiver)); + return; }' ] @@ -2921,9 +2787,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilNoSendInRecei static void methodWithUnusedConstantInIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNotNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -2947,12 +2811,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilSendInReceive static void methodWithUnusedConstantInIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfNotNilSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfNotNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNotNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfNotNilSendInReceiver, method(self_in_methodWithUnusedConstantInIfNotNilSendInReceiver)); + return; }' ] @@ -2976,9 +2836,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueIfFalseNoSendIn static void methodWithUnusedConstantInIfTrueIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfTrueIfFalseNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3002,14 +2860,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueIfFalseSendInRe static void methodWithUnusedConstantInIfTrueIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver); - method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver, 3); - method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver, method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver); + method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver, 3); + method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver, method(self_in_methodWithUnusedConstantInIfTrueIfFalseSendInReceiver)); + return; }' ] @@ -3033,9 +2887,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueNoSendInReceive static void methodWithUnusedConstantInIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfTrueNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3059,14 +2911,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueSendInReceiver static void methodWithUnusedConstantInIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedConstantInIfTrueSendInReceiver) { - { - method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver); - method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver, 3); - method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver, method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver); + method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver, 3); + method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver, method(self_in_methodWithUnusedConstantInIfTrueSendInReceiver)); + return; }' ] @@ -3168,9 +3016,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariable [ static void methodWithUnusedInstanceVariable(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariable) { - { - return; - } + return; }' ] @@ -3372,9 +3218,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfAndUnus static sqInt methodWithUnusedInstanceVariableInCaseOfAndUnusedInstanceVariableInOtherwiseSendInExpression(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInCaseOfAndUnusedInstanceVariableInOtherwiseSendInExpression) { - { - method(self_in_methodWithUnusedInstanceVariableInCaseOfAndUnusedInstanceVariableInOtherwiseSendInExpression, 3); - } + method(self_in_methodWithUnusedInstanceVariableInCaseOfAndUnusedInstanceVariableInOtherwiseSendInExpression, 3); return 0; }' ] @@ -3409,9 +3253,7 @@ methodWithUnusedInstanceVariableInCaseOfNoSendInExpression(SLDeadCodeElimination default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -3446,9 +3288,7 @@ methodWithUnusedInstanceVariableInCaseOfSendInExpression(SLDeadCodeEliminationTe default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -3471,9 +3311,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCoerce [ static void methodWithUnusedInstanceVariableInCoerce(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInCoerce) { - { - return; - } + return; }' ] @@ -3497,9 +3335,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseIfTrue static void methodWithUnusedInstanceVariableInIfFalseIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3523,14 +3359,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseIfTrue static void methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver); - method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver, 3); - method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver); + method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver, 3); + method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver)); + return; }' ] @@ -3554,9 +3386,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseNoSend static void methodWithUnusedInstanceVariableInIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfFalseNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3580,14 +3410,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseSendIn static void methodWithUnusedInstanceVariableInIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver); - method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver, 3); - method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver); + method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver, 3); + method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfFalseSendInReceiver)); + return; }' ] @@ -3611,9 +3437,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilIfNotNil static void methodWithUnusedInstanceVariableInIfNilIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNilIfNotNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3637,12 +3461,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilIfNotNil static void methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver)); + return; }' ] @@ -3666,9 +3486,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilNoSendIn static void methodWithUnusedInstanceVariableInIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3692,12 +3510,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilSendInRe static void methodWithUnusedInstanceVariableInIfNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNilSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNilSendInReceiver)); + return; }' ] @@ -3721,9 +3535,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilIfNil static void methodWithUnusedInstanceVariableInIfNotNilIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNotNilIfNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3747,12 +3559,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilIfNil static void methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver)); + return; }' ] @@ -3776,9 +3584,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilNoSen static void methodWithUnusedInstanceVariableInIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNotNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3801,12 +3607,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilSendI static void methodWithUnusedInstanceVariableInIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfNotNilSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfNotNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNotNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfNotNilSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfNotNilSendInReceiver)); + return; }' ] @@ -3830,9 +3632,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueIfFalse static void methodWithUnusedInstanceVariableInIfTrueIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3856,14 +3656,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueIfFalse static void methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver); - method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver, 3); - method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver); + method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver, 3); + method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver)); + return; }' ] @@ -3887,9 +3683,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueNoSendI static void methodWithUnusedInstanceVariableInIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfTrueNoSendInReceiver) { - { - return; - } + return; }' ] @@ -3913,14 +3707,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueSendInR static void methodWithUnusedInstanceVariableInIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver) { - { - method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver); - method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver, 3); - method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver); + method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver, 3); + method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver, method(self_in_methodWithUnusedInstanceVariableInIfTrueSendInReceiver)); + return; }' ] @@ -3953,17 +3743,13 @@ methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwi break; case 6: { - { - return; - } + return; } default: { } } - { - return; - } + return; }' ] @@ -3996,17 +3782,13 @@ methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwi break; case 6: { - { - return; - } + return; } default: { } } - { - return; - } + return; }' ] @@ -4030,9 +3812,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariable [ static void methodWithUnusedVariable(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariable) { - { - return; - } + return; }' ] @@ -4221,9 +4001,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfAndUnusedVariab static void methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseNoSendInExpression(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseNoSendInExpression, sqInt anInt) { - { - return; - } + return; }' ] @@ -4249,12 +4027,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfAndUnusedVariab static void methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseSendInExpression(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseSendInExpression) { - { - method(self_in_methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseSendInExpression, 3); - } - { - return; - } + method(self_in_methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseSendInExpression, 3); + return; }' ] @@ -4289,9 +4063,7 @@ methodWithUnusedVariableInCaseOfNoSendInExpression(SLDeadCodeEliminationTestClas default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -4325,9 +4097,7 @@ methodWithUnusedVariableInCaseOfSendInExpression(SLDeadCodeEliminationTestClass default: error("Case not found and no otherwise clause"); } - { - return; - } + return; }' ] @@ -4350,9 +4120,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCoerce [ static void methodWithUnusedVariableInCoerce(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInCoerce) { - { - return; - } + return; }' ] @@ -4376,9 +4144,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseIfTrueNoSendIn static void methodWithUnusedVariableInIfFalseIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfFalseIfTrueNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4402,14 +4168,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseIfTrueSendInRe static void methodWithUnusedVariableInIfFalseIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver); - method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver, 3); - method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver, method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver); + method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver, 3); + method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver, method(self_in_methodWithUnusedVariableInIfFalseIfTrueSendInReceiver)); + return; }' ] @@ -4433,9 +4195,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseNoSendInReceiv static void methodWithUnusedVariableInIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfFalseNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4459,14 +4219,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseSendInReceiver static void methodWithUnusedVariableInIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfFalseSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver); - method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver, 3); - method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver, method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver); + method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver, 3); + method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver, method(self_in_methodWithUnusedVariableInIfFalseSendInReceiver)); + return; }' ] @@ -4491,9 +4247,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilIfNotNilNoSendIn static void methodWithUnusedVariableInIfNilIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNilIfNotNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4517,12 +4271,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilIfNotNilSendInRe static void methodWithUnusedVariableInIfNilIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNilIfNotNilSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfNilIfNotNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNilIfNotNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfNilIfNotNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNilIfNotNilSendInReceiver)); + return; }' ] @@ -4546,9 +4296,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilNoSendInReceiver static void methodWithUnusedVariableInIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4572,12 +4320,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilSendInReceiver [ static void methodWithUnusedVariableInIfNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNilSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNilSendInReceiver)); + return; }' ] @@ -4601,9 +4345,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilIfNilNoSendIn static void methodWithUnusedVariableInIfNotNilIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNotNilIfNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4627,12 +4369,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilIfNilSendInRe static void methodWithUnusedVariableInIfNotNilIfNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNotNilIfNilSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfNotNilIfNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNotNilIfNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfNotNilIfNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNotNilIfNilSendInReceiver)); + return; }' ] @@ -4656,9 +4394,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilNoSendInRecei static void methodWithUnusedVariableInIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNotNilNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4682,12 +4418,8 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilSendInReceive static void methodWithUnusedVariableInIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfNotNilSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfNotNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNotNilSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfNotNilSendInReceiver, method(self_in_methodWithUnusedVariableInIfNotNilSendInReceiver)); + return; }' ] @@ -4711,9 +4443,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueIfFalseNoSendIn static void methodWithUnusedVariableInIfTrueIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfTrueIfFalseNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4737,14 +4467,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueIfFalseSendInRe static void methodWithUnusedVariableInIfTrueIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver); - method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver, 3); - method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver, method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver); + method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver, 3); + method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver, method(self_in_methodWithUnusedVariableInIfTrueIfFalseSendInReceiver)); + return; }' ] @@ -4768,9 +4494,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueNoSendInReceive static void methodWithUnusedVariableInIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfTrueNoSendInReceiver) { - { - return; - } + return; }' ] @@ -4794,14 +4518,10 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueSendInReceiver static void methodWithUnusedVariableInIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * self_in_methodWithUnusedVariableInIfTrueSendInReceiver) { - { - method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver); - method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver, 3); - method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver, method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver)); - } - { - return; - } + method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver); + method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver, 3); + method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver, method(self_in_methodWithUnusedVariableInIfTrueSendInReceiver)); + return; }' ] @@ -5211,9 +4931,7 @@ SLDeadCodeEliminationTest >> testSwitchWithOnlyCommentNoSendInReceiver [ static void switchWithOnlyCommentNoSendInReceiver(SLDeadCodeEliminationTestClass * self_in_switchWithOnlyCommentNoSendInReceiver, sqInt _anInt) { - { - return; - } + return; }' ] @@ -5240,11 +4958,7 @@ SLDeadCodeEliminationTest >> testSwitchWithOnlyCommentSendInReceiver [ static void switchWithOnlyCommentSendInReceiver(SLDeadCodeEliminationTestClass * self_in_switchWithOnlyCommentSendInReceiver) { - { - method(self_in_switchWithOnlyCommentSendInReceiver, method(self_in_switchWithOnlyCommentSendInReceiver)); - } - { - return; - } + method(self_in_switchWithOnlyCommentSendInReceiver, method(self_in_switchWithOnlyCommentSendInReceiver)); + return; }' ] diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index f25b64467dd..7daf64c78f2 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -911,11 +911,9 @@ methodAReturnBlockAssignment(void) { sqInt a; - { - /* begin methodB */ - return (a = 2 + 2); - /* end methodB */ - } + /* begin methodB */ + return (a = 2 + 2); + /* end methodB */ }' ] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 1b9a2931235..fb2eacdc109 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -71,8 +71,9 @@ SLInlinerWithAnnotation >> argAssignmentsFor: meth send: aSendNode except: elide typeCompatibleWith: argName inliningInto: meth in: currentMethod)) ] ] ]. - self replaceVariableIn: substitutionDict for: meth. - ^ stmtList + + meth parseTree addAllFirst: stmtList. + ^ substitutionDict ] { #category : 'inlining-decision' } @@ -115,12 +116,12 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ ] { #category : 'testing' } -SLInlinerWithAnnotation >> checkForFlagIn: aTMethod [ +SLInlinerWithAnnotation >> checkForFlagIn: aTStatementList [ - (aTMethod parseTree sizeWithoutComments > 1 and: [ - aTMethod parseTree firstNonCommentStatement isSend and: [ - aTMethod parseTree firstNonCommentStatement selector == #flag: ] ]) - ifTrue: [ aTMethod parseTree removeFirstNonCommentStatement ] + (aTStatementList sizeWithoutComments > 1 and: [ + aTStatementList firstNonCommentStatement isSend and: [ + aTStatementList firstNonCommentStatement selector == #flag: ] ]) + ifTrue: [ aTStatementList removeFirstNonCommentStatement ] ] { #category : 'accessing' } @@ -202,17 +203,22 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ { #category : 'inlining-support' } SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ - "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." + "If the given statement node can be inlined, answer if anything was done." (aNode isReturn and: [ self isInlineableSend: aNode expression ]) - ifTrue: [ ^ self inlineSend: aNode expression ]. + ifTrue: [ + self inlineSend: aNode expression. + ^ true ]. (aNode isAssignment and: [ self isInlineableSend: aNode expression ]) - ifTrue: [ ^ self inlineSend: aNode expression ]. + ifTrue: [ + self inlineSend: aNode expression. + ^ true ]. (aNode isSend and: [ self isInlineableSend: aNode ]) ifTrue: [ - ^ self inlineSend: aNode ]. - ^ nil + self inlineSend: aNode. + ^ true ]. + ^ false ] { #category : 'inlining-support-condtional' } @@ -277,13 +283,14 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional selector := aSendNode selector. meth := (codeGenerator methodNamed: selector) copy. meth ifNil: [ ^ self inlineBuiltin: aSendNode ]. + parseTree := meth parseTree. parametersToReplaceByArgument := Set withAll: currentMethod args. argsForInlining := aSendNode argumentsForInliningCodeGenerator: codeGenerator. meth args with: argsForInlining do: [ :argName :exprNode | exprNode isLeaf ifTrue: [ parametersToReplaceByArgument add: argName ] ]. - self checkForFlagIn: meth. + self checkForFlagIn: parseTree. meth renameVarsForInliningInto: currentMethod except: parametersToReplaceByArgument @@ -297,7 +304,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional substitutionDict at: argName put: exprNode. (parametersToReplaceByArgument includes: argName) ifFalse: [ currentMethod removeLocal: argName ] ]. - meth parseTree bindVariablesIn: substitutionDict. + parseTree bindVariablesIn: substitutionDict. "copyWithoutReturn does it implictly, avoid duplicating them and in addition if meth end up in an expressionList, they won't appear which might cause bug, see asCASTIn: vs asCASTExpressionIn: for stmtList. " @@ -305,7 +312,6 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional aBoolean ifFalse: [ SLNodeAnnotatorVisitor copyWithoutReturn: meth ]. "same 'optimization' as before but with comments if possible" - parseTree := meth parseTree. parseTree children size = 1 ifTrue: [ meth parseTree: parseTree last ] ifFalse: [ @@ -439,7 +445,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ otherwise the assignee variable type must match the return type of the inlinee. Return types are not propagated." - | selector callee exitLabel inlineStmts omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent | + | selector callee exitLabel inlineStmts omittedParameters calleeParameters exitVar directReturn isUsedExpression assigningParent argumentsSubstitutionDict | selector := aSendNode selector. callee := codeGenerator methodNamed: selector. directReturn := sLNodeAnnotatorVisitor isEffectiveReturnValue: @@ -466,8 +472,9 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ (self trimArgumentsIn: callee). calleeParameters size = aSendNode arguments size ifFalse: [ ^ nil ]. callee := callee copy. + inlineStmts := callee parseTree. - self checkForFlagIn: callee. + self checkForFlagIn: inlineStmts. self propagateReturnTypeDirectReturn: directReturn @@ -491,13 +498,10 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ replaceReturnWithGoToIfNeededIn: callee directReturn: directReturn. - self moveDownReturnsAndAssignmentsFor: aSendNode including: callee. - - inlineStmts := callee parseTree. - inlineStmts addAllFirst: (self - argAssignmentsFor: callee - send: aSendNode - except: omittedParameters). + argumentsSubstitutionDict := self + argAssignmentsFor: callee + send: aSendNode + except: omittedParameters. self addBeginAndEndCommentsFor: inlineStmts @@ -506,6 +510,12 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ inlineStmts isComment ifTrue: [ "Nuke empty methods; e.g. override of flushAtCache" inlineStmts statements: OrderedCollection new ]. + + self + moveDownReturnsAndAssignmentsFor: aSendNode + including: inlineStmts. + self replaceVariableIn: argumentsSubstitutionDict for: callee. + isUsedExpression ifFalse: [ inlineStmts nodesDo: [ :node | node isGoTo ifTrue: [ node metInInlining ] ] ]. @@ -513,9 +523,12 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ ] { #category : 'transformation' } -SLInlinerWithAnnotation >> invert: aSendNode replacement: aNode [ +SLInlinerWithAnnotation >> invert: aSendNode replacement: aTStatementList [ + + | parent | + parent := aSendNode parent. - aSendNode parent replaceChild: aSendNode with: aNode + parent replaceChild: aSendNode with: aTStatementList ] { #category : 'inlining-decision' } @@ -741,20 +754,15 @@ SLInlinerWithAnnotation >> label: exitLabel for: aTMethod [ ] { #category : 'transformation' } -SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTMethodReplacement [ - - | replacementParseTree | - replacementParseTree := aTMethodReplacement parseTree. +SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTStatementList [ - self invert: aSendNode replacement: replacementParseTree. + self invert: aSendNode replacement: aTStatementList. sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode - replacement: replacementParseTree. + replacement: aTStatementList. - sLNodeAnnotatorVisitor moveDownAssigningParentFrom: - replacementParseTree. - sLNodeAnnotatorVisitor moveDownReturningParentFrom: - replacementParseTree + sLNodeAnnotatorVisitor moveDownAssigningParentFrom: aTStatementList. + sLNodeAnnotatorVisitor moveDownReturningParentFrom: aTStatementList ] { #category : 'transformation' } @@ -973,20 +981,14 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ "Expand any (complete) inline methods sent by this method as top-level statements. Answer if anything was inlined." - | stmtLists didSomething newStatements | + | stmtLists didSomething | didSomething := false. stmtLists := self statementsListsForInliningInCurrentMethod. stmtLists do: [ :stmtList | - newStatements := TStatementListNode statements: - (OrderedCollection new: stmtList statements size). stmtList statements do: [ :stmt | - (self inlineCodeOrNilForStatement: stmt) - ifNil: [ newStatements addAllLast: { stmt } ] - ifNotNil: [ :inlinedStmts | - didSomething := true. - newStatements addAllLast: inlinedStmts statements ] ]. - stmtList statements: newStatements statements ]. + (self inlineCodeOrNilForStatement: stmt) ifTrue: [ + didSomething := true ] ] ]. ^ didSomething ] @@ -1005,7 +1007,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ self ensureConditionalAssignmentsAreTransformedInCurrentMethod. self annotateCurrentMethod. didSomething := self tryToInlineMethodStatementsListsInCurrentMethod. - didSomething ifTrue: [ self annotateCurrentMethod ]. self tryToInlineMethodExpressionsInCurrentMethod ifTrue: [ self annotateCurrentMethod. didSomething := true ]. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index b0349a57e80..8b3a195f45d 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -107,7 +107,7 @@ SLNodeAnnotatorVisitor >> addToInSend: aNode [ self addToInStack: aNode stack: inSendStack entry: self inSendString ] -{ #category : 'helpers-annotate' } +{ #category : 'annotate-helpers' } SLNodeAnnotatorVisitor >> addToInStack: aNode stack: aStack entry: aString [ aStack isEmpty ifTrue: [ ^ self ]. @@ -161,7 +161,7 @@ SLNodeAnnotatorVisitor >> assignmentString [ ^ #assignment ] -{ #category : 'helpers-accessing-info' } +{ #category : 'accessing-info-helpers' } SLNodeAnnotatorVisitor >> associationKeyInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -170,7 +170,7 @@ SLNodeAnnotatorVisitor >> associationKeyInfoFor: aNode entry: aString [ ifAbsent: [ false ] ] -{ #category : 'helpers-accessing-info' } +{ #category : 'accessing-info-helpers' } SLNodeAnnotatorVisitor >> associationValueInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -179,7 +179,7 @@ SLNodeAnnotatorVisitor >> associationValueInfoFor: aNode entry: aString [ ifAbsent: [ nil ] ] -{ #category : 'helpers-accessing-info' } +{ #category : 'accessing-info-helpers' } SLNodeAnnotatorVisitor >> booleanInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -232,7 +232,7 @@ SLNodeAnnotatorVisitor >> do: aBlock visit: aNode [ ^ blockResult ] -{ #category : 'helpers-annotate' } +{ #category : 'annotate-helpers' } SLNodeAnnotatorVisitor >> ensureEntryInInfoDictFor: aNode [ info at: aNode ifAbsentPut: [ IdentityDictionary new ] @@ -251,7 +251,7 @@ SLNodeAnnotatorVisitor >> fromSearchStmtListString [ ^ #fromSearchStmtList ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ "re give the informations for each branch after using it" @@ -268,7 +268,7 @@ SLNodeAnnotatorVisitor >> giveStacksInfoForMultipleBranches: aCollectionOfNode [ assoc value push: assoc key ] ] ] ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> handleConditionalSend: aSendNode [ aSendNode isConditionalSend ifTrue: [ @@ -283,7 +283,7 @@ SLNodeAnnotatorVisitor >> handleConditionalSend: aSendNode [ ^ false ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> handleIterativeSend: aSendNode [ aSendNode isUnaryIterativeSend ifTrue: [ @@ -587,7 +587,7 @@ SLNodeAnnotatorVisitor >> moveDownReturningParentFrom: aNode [ replacement: returningParent expression ] -{ #category : 'helpers-transformation' } +{ #category : 'transformation-helpers' } SLNodeAnnotatorVisitor >> moveDownSingleBranchConditional: aNode newBranch: anOtherNode [ "does nothing is the node isnt a single branch conditional. when moving down a assignment/return in a single branch conditional, we need to consider the missing branch which Slang would have consider is the conditional was still an argument, see generateCASTIfNotNilAsArgument: for exemple and the other methods related to single conditional as arguments" @@ -616,7 +616,7 @@ SLNodeAnnotatorVisitor >> moveDownSingleBranchConditional: aNode newBranch: anOt arguments: aNode arguments , { newArgument } ] ] -{ #category : 'helpers-accessing-info' } +{ #category : 'accessing-info-helpers' } SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ ^ (info at: aNode) @@ -625,7 +625,7 @@ SLNodeAnnotatorVisitor >> presenceInfoFor: aNode entry: aString [ ifAbsent: [ false ] ] -{ #category : 'helpers-annotate' } +{ #category : 'annotate-helpers' } SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantString: constantString ignoredString: ignoredString [ | entry assoc isEffectivelyIgnored | @@ -811,7 +811,7 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ searchForAssignValueStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitBinaryIterativeSend: aSendNode [ "in Slang, iterative are not considered as expression but rather as statement to be closer to C" @@ -852,7 +852,7 @@ SLNodeAnnotatorVisitor >> visitCaseStatementNode: aCaseStmtNode [ searchForExpressionValueStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitConditionalSend: aSendNode [ "conditionals are control-flow node and thus treated differently from the other sends" @@ -876,7 +876,7 @@ SLNodeAnnotatorVisitor >> visitConstantNode: aConstantNode [ self annotateNodeState: aConstantNode ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitCppConditional: aSendNode [ "conditionals are control-flow node and thus treated differently from the other sends" @@ -897,7 +897,7 @@ SLNodeAnnotatorVisitor >> visitCppConditional: aSendNode [ inExpressionStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ "in Slang, iterative are not considered as expression but rather as statement to be closer to C" @@ -917,7 +917,7 @@ SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ inExpressionStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitExitPointSend: aSendNode [ | hasInvalidateAStmtSearch | @@ -1044,7 +1044,7 @@ SLNodeAnnotatorVisitor >> visitSendNode: aSendNode [ inSendStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitSpecialSend: aSendNode [ (self handleConditionalSend: aSendNode) ifTrue: [ ^ true ]. @@ -1119,7 +1119,7 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ searchForExpressionValueStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitUnaryIterativeSend: aSendNode [ "in Slang, iterative are not considered as expression but rather as statement to be closer to C" @@ -1135,7 +1135,7 @@ SLNodeAnnotatorVisitor >> visitUnaryIterativeSend: aSendNode [ inExpressionStack pop ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSendNode [ "for a 'a := [[ exp . condition ] whileTrue]' case (obtained through inlining for example), the last expression of the block is a condition not the expression value of a := ..." @@ -1161,7 +1161,7 @@ SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSe block addAllLastKeepingEndComments: { condition } ] -{ #category : 'helpers-visiting' } +{ #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitValueExpansionSend: aSendNode [ self activateCurrentSouldBePartiallyIgnored. diff --git a/smalltalksrc/Slang/TStatementListNode.class.st b/smalltalksrc/Slang/TStatementListNode.class.st index bc5a8f00f0b..9971cf79f42 100644 --- a/smalltalksrc/Slang/TStatementListNode.class.st +++ b/smalltalksrc/Slang/TStatementListNode.class.st @@ -782,10 +782,14 @@ TStatementListNode >> renameLocalVariablesGivenClassVariables: classVariables gl { #category : 'transformations' } TStatementListNode >> replaceChild: aNode with: bNode [ + bNode isStatementList ifTrue: [ + self replaceChild: aNode withList: bNode statements. + ^ self ]. + self statements: (statements collect: [ :node | - node == aNode - ifTrue: [ bNode ] - ifFalse: [ node ] ]) + node == aNode + ifTrue: [ bNode ] + ifFalse: [ node ] ]) ] { #category : 'transformations' } From c61fc19c54edeafacd497976d598cec167e43c72 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 4 Aug 2025 15:23:54 +0200 Subject: [PATCH 67/86] optimize the tests to add only the required method and not the full class each time -> now way faster + easier to debug --- .../SLASTTransformationTest.class.st | 15 +- .../SLAbstractTranslationTestCase.class.st | 51 +++ .../SLDeadCodeEliminationTest.class.st | 358 +++++++++--------- .../Slang-Tests/SLInliningTest.class.st | 325 +++++++--------- .../VMMaker/SLInliningTest.extension.st | 10 +- 5 files changed, 386 insertions(+), 373 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st b/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st index a71a6e34bb5..c925c50171c 100644 --- a/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st +++ b/smalltalksrc/Slang-Tests/SLASTTransformationTest.class.st @@ -12,10 +12,9 @@ Class { SLASTTransformationTest >> setUp [ super setUp. - ccg addClass: SLASTTransformationTestClass. + testClass := SLASTTransformationTestClass. "necessary to get the type of sqInt" SpurMemoryManager initBytesPerWord: 8. - ccg inferTypes. sLExpandASTVisitor := SLExpandASTVisitor new ] @@ -23,7 +22,7 @@ SLASTTransformationTest >> setUp [ SLASTTransformationTest >> testMethodWithBlockValue [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithBlockValue. + tMethod := self addMethodAndMethodCalledBy: #methodWithBlockValue. sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. @@ -48,7 +47,7 @@ methodWithBlockValue(void) SLASTTransformationTest >> testMethodWithBlockValueValue [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithBlockValueValue. + tMethod := self addMethodAndMethodCalledBy: #methodWithBlockValueValue. sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. @@ -75,7 +74,7 @@ methodWithBlockValueValue(void) SLASTTransformationTest >> testMethodWithBockInAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithBockInAssignment. + tMethod := self addMethodAndMethodCalledBy: #methodWithBockInAssignment. sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. @@ -104,7 +103,7 @@ methodWithBockInAssignment(void) SLASTTransformationTest >> testMethodWithBockValueInAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithBockValueInAssignment. + tMethod := self addMethodAndMethodCalledBy: #methodWithBockValueInAssignment. sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. @@ -132,7 +131,7 @@ methodWithBockValueInAssignment(void) SLASTTransformationTest >> testMethodWithBockValueValueInAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithBockValueValueInAssignment. + tMethod := self addMethodAndMethodCalledBy: #methodWithBockValueValueInAssignment. sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. @@ -162,7 +161,7 @@ methodWithBockValueValueInAssignment(void) SLASTTransformationTest >> testMethodWithFPrintF [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithFPrintF. + tMethod := self addMethodAndMethodCalledBy: #methodWithFPrintF. sLExpandASTVisitor currentMethod: tMethod. sLExpandASTVisitor expandASTInCurrentMethod. diff --git a/smalltalksrc/Slang-Tests/SLAbstractTranslationTestCase.class.st b/smalltalksrc/Slang-Tests/SLAbstractTranslationTestCase.class.st index bd40270b618..b335db4885f 100644 --- a/smalltalksrc/Slang-Tests/SLAbstractTranslationTestCase.class.st +++ b/smalltalksrc/Slang-Tests/SLAbstractTranslationTestCase.class.st @@ -1,10 +1,55 @@ Class { #name : 'SLAbstractTranslationTestCase', #superclass : 'SlangAbstractTestCase', + #instVars : [ + 'testClass' + ], #category : 'Slang-Tests', #package : 'Slang-Tests' } +{ #category : 'helpers' } +SLAbstractTranslationTestCase >> addMethodAndMethodCalledBy: aSelector [ + + ^ self + addMethodAndMethodCalledBy: aSelector + structOrNotBlock: [ :selector | + ccg addMethodFor: testClass selector: selector ] +] + +{ #category : 'helpers' } +SLAbstractTranslationTestCase >> addMethodAndMethodCalledBy: aSelector structOrNotBlock: aBlock [ + + | requiredCalls visited previousSize tMethod classMethodSelectors | + classMethodSelectors := testClass methodDictionary keys. + tMethod := aBlock value: aSelector. + (visited := Set new) add: aSelector. + (requiredCalls := tMethod allCalls) add: tMethod selector. + previousSize := requiredCalls size. + + [ + requiredCalls do: [ :selector | + ((classMethodSelectors includes: selector) and: [ + (visited includes: selector) not ]) ifTrue: [ + tMethod := aBlock value: selector. + visited add: selector. + requiredCalls addAll: tMethod allCalls ] ]. + requiredCalls size > previousSize ] whileTrue: [ + previousSize := requiredCalls size ]. + + ccg inferTypes. + ^ ccg methodNamed: aSelector +] + +{ #category : 'helpers' } +SLAbstractTranslationTestCase >> addStructMethodAndMethodCalledBy: aSelector [ + + ^ self + addMethodAndMethodCalledBy: aSelector + structOrNotBlock: [ :selector | + ccg addStructMethodFor: testClass selector: selector ] +] + { #category : 'helpers' } SLAbstractTranslationTestCase >> astTranslate: tast inStream: aWriteStream [ @@ -15,6 +60,12 @@ SLAbstractTranslationTestCase >> astTranslate: tast inStream: aWriteStream [ cAST acceptVisitor: prettyPrinter. ] +{ #category : 'helpers' } +SLAbstractTranslationTestCase >> doInliningIn: aTMethod [ + + ccg doBasicInlining: true +] + { #category : 'helpers' } SLAbstractTranslationTestCase >> translate: tast [ diff --git a/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st b/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st index 8fc887a55fc..fb7e6765c42 100644 --- a/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st +++ b/smalltalksrc/Slang-Tests/SLDeadCodeEliminationTest.class.st @@ -13,12 +13,10 @@ Class { SLDeadCodeEliminationTest >> setUp [ super setUp. - ccg addStructClass: SLDeadCodeEliminationTestClass. + testClass := SLDeadCodeEliminationTestClass. "necessary to get the type of sqInt" SpurMemoryManager initBytesPerWord: 8. - ccg inferTypes. - sLDeadCodeElimination := SLDeadCodeElimination new codeGenerator: ccg. - + sLDeadCodeElimination := SLDeadCodeElimination new codeGenerator: ccg ] { #category : 'only-comment' } @@ -26,11 +24,11 @@ SLDeadCodeEliminationTest >> testConditionalWithOnlyCommentNoSendInReceiver [ "currently the only way to get comments in a methods is through inlining, having only comments is equivalent to being empty so it shouldn't change the behavior of the dead code elimination process" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #conditionalWithOnlyCommentNoSendInReceiver. ccg prepareMethods. - ccg doBasicInlining: true. + self doInliningIn: tMethod. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -54,10 +52,10 @@ SLDeadCodeEliminationTest >> testConditionalWithOnlyCommentSendInReceiver [ "currently the only way to get comments in a methods is through inlining, having only comments is equivalent to being empty so it shouldn't change the behavior of the dead code elimination process" | translation tMethod | - tMethod := ccg methodNamed: #conditionalWithOnlyCommentSendInReceiver. + tMethod := self addStructMethodAndMethodCalledBy: #conditionalWithOnlyCommentSendInReceiver. ccg prepareMethods. - ccg doBasicInlining: true. + self doInliningIn: tMethod. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -81,7 +79,7 @@ conditionalWithOnlyCommentSendInReceiver(SLDeadCodeEliminationTestClass * self_i SLDeadCodeEliminationTest >> testMethodAddingCallInCoerce [ | translation tMethod | - tMethod := ccg methodNamed: #methodAddingCallInCoerce. + tMethod := self addStructMethodAndMethodCalledBy: #methodAddingCallInCoerce. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -105,7 +103,7 @@ methodAddingCallInCoerce(SLDeadCodeEliminationTestClass * self_in_methodAddingCa SLDeadCodeEliminationTest >> testMethodWithBlockValueAssignmentIntoSameVariableInArguments [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithBlockValueAssignmentIntoSameVariableInArguments. sLDeadCodeElimination currentMethod: tMethod. @@ -135,7 +133,7 @@ methodWithBlockValueAssignmentIntoSameVariableInArguments(SLDeadCodeEliminationT SLDeadCodeEliminationTest >> testMethodWithBlockValueAssignmentNotIntoSameVariableInArguments [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithBlockValueAssignmentNotIntoSameVariableInArguments. sLDeadCodeElimination currentMethod: tMethod. @@ -168,7 +166,7 @@ SLDeadCodeEliminationTest >> testMethodWithBlockValueValueAssignmentIntoSameVari "remove var = var" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithBlockValueValueAssignmentIntoSameVariableInArguments. sLDeadCodeElimination currentMethod: tMethod. @@ -202,7 +200,7 @@ methodWithBlockValueValueAssignmentIntoSameVariableInArguments(SLDeadCodeElimina SLDeadCodeEliminationTest >> testMethodWithBlockValueValueAssignmentNotIntoSameVariableInArguments [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithBlockValueValueAssignmentNotIntoSameVariableInArguments. sLDeadCodeElimination currentMethod: tMethod. @@ -238,7 +236,7 @@ SLDeadCodeEliminationTest >> testMethodWithCallInCoerce [ "the cCoerce is unecessary but is kept anyway" | translation tMethod | - tMethod := ccg methodNamed: #methodWithCallInCoerce. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithCallInCoerce. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -263,7 +261,7 @@ methodWithCallInCoerce(SLDeadCodeEliminationTestClass * self_in_methodWithCallIn SLDeadCodeEliminationTest >> testMethodWithConstantInAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithConstantInAssignment. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithConstantInAssignment. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -290,7 +288,7 @@ methodWithConstantInAssignment(SLDeadCodeEliminationTestClass * self_in_methodWi SLDeadCodeEliminationTest >> testMethodWithConstantInReturn [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithConstantInReturn. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithConstantInReturn. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -314,7 +312,7 @@ methodWithConstantInReturn(SLDeadCodeEliminationTestClass * self_in_methodWithCo SLDeadCodeEliminationTest >> testMethodWithDeadCodeInRepeat [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithDeadCodeInRepeat. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInRepeat. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -340,7 +338,7 @@ methodWithDeadCodeInRepeat(SLDeadCodeEliminationTestClass * self_in_methodWithDe SLDeadCodeEliminationTest >> testMethodWithDeadCodeInTimesRepeat [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithDeadCodeInTimesRepeat. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInTimesRepeat. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -377,7 +375,7 @@ methodWithDeadCodeInTimesRepeat(SLDeadCodeEliminationTestClass * self_in_methodW SLDeadCodeEliminationTest >> testMethodWithDeadCodeInToByDo [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithDeadCodeInToByDo. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInToByDo. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -405,7 +403,7 @@ methodWithDeadCodeInToByDo(SLDeadCodeEliminationTestClass * self_in_methodWithDe SLDeadCodeEliminationTest >> testMethodWithDeadCodeInToDo [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithDeadCodeInToDo. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInToDo. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -433,7 +431,7 @@ methodWithDeadCodeInToDo(SLDeadCodeEliminationTestClass * self_in_methodWithDead SLDeadCodeEliminationTest >> testMethodWithDeadCodeInWhileFalse [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInWhileFalseLastExpressionIsLeaf. sLDeadCodeElimination currentMethod: tMethod. @@ -460,7 +458,7 @@ methodWithDeadCodeInWhileFalseLastExpressionIsLeaf(SLDeadCodeEliminationTestClas SLDeadCodeEliminationTest >> testMethodWithDeadCodeInWhileFalseBinaryNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInWhileFalseBinaryNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -492,7 +490,7 @@ methodWithDeadCodeInWhileFalseBinaryNoSendInReceiver(SLDeadCodeEliminationTestCl SLDeadCodeEliminationTest >> testMethodWithDeadCodeInWhileFalseBinarySendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInWhileFalseBinarySendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -519,7 +517,7 @@ methodWithDeadCodeInWhileFalseBinarySendInReceiver(SLDeadCodeEliminationTestClas SLDeadCodeEliminationTest >> testMethodWithDeadCodeInWhileTrue [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInWhileTrueLastExpressionIsLeaf. sLDeadCodeElimination currentMethod: tMethod. @@ -546,7 +544,7 @@ methodWithDeadCodeInWhileTrueLastExpressionIsLeaf(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithDeadCodeInWhileTrueBinaryNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInWhileTrueBinaryNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -578,7 +576,7 @@ methodWithDeadCodeInWhileTrueBinaryNoSendInReceiver(SLDeadCodeEliminationTestCla SLDeadCodeEliminationTest >> testMethodWithDeadCodeInWhileTrueBinarySendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithDeadCodeInWhileTrueBinarySendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -606,7 +604,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyCaseOfNoSendInReceiver [ "we need to use prepareMethodIn to build switch node, we keep the switch because of the default being an error" | translation tMethod | - tMethod := ccg methodNamed: #methodWithEmptyCaseOfNoSendInReceiver:. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyCaseOfNoSendInReceiver:. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -640,7 +638,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyCaseOfOtherwiseNoSendInReceiver "we need to use prepareMethodIn to build switch node, the cases and the otherwise are empty so the node is suppressed" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyCaseOfOtherwiseNoSendInReceiver:. tMethod prepareMethodIn: ccg. @@ -666,7 +664,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyCaseOfOtherwiseSendInReceiver [ "we need to use prepareMethodIn to build switch node, we keep the message send but the cases and the otherwise are empty so the node is suppressed " | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyCaseOfOtherwiseSendInReceiver. tMethod prepareMethodIn: ccg. @@ -693,7 +691,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyCaseOfSendInReceiver [ "we need to use prepareMethodIn to build switch node, the cases are empty only the default remains " | translation tMethod | - tMethod := ccg methodNamed: #methodWithEmptyCaseOfSendInReceiver. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyCaseOfSendInReceiver. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -728,7 +726,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseIfTrueAndNoSendInReceiver "supress the send" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfFalseIfTrueAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -753,7 +751,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseIfTrueAndSendInReceiver [ "suppress the send and keep the 4 send in the receiver" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfFalseIfTrueAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -782,7 +780,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseInIfFalseIfTrueAndNoSendI "reduce the conditional to an ifTrue" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfFalseInIfFalseIfTrueAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -814,7 +812,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseInIfFalseIfTrueAndSendInR "reduce the conditional to an ifTrue" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfFalseInIfFalseIfTrueAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -844,7 +842,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseInIfTrueIfFalseAndNoSendI "reduce the conditional to an ifTrue" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfFalseInIfTrueIfFalseAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -876,7 +874,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfFalseInIfTrueIfFalseAndSendInR "reduce the conditional to an ifTrue" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfFalseInIfTrueIfFalseAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -906,7 +904,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNilIfNotNilAndNoSendInReceiver "supress the send" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfNilIfNotNilAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -931,7 +929,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNilIfNotNilAndSendInReceiver [ "supress the send and keep the send in the receiver" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfNilIfNotNilAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -957,7 +955,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNotNilIfNilAndNoSendInReceiver "supress the send" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfNotNilIfNilAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -982,7 +980,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfNotNilIfNilAndSendInReceiver [ "supress the send and keep the send in the receiver" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfNotNilIfNilAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1008,7 +1006,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueIfFalseAndNoSendInReceiver "supress the send" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfTrueIfFalseAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1033,7 +1031,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueIfFalseAndSendInReceiver [ "supress the send and keep the 4 send in the receiver" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfTrueIfFalseAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1062,7 +1060,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueInIfFalseIfTrueAndNoSendIn "reduce the conditional to an ifFalse" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfTrueInIfFalseIfTrueAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1094,7 +1092,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueInIfFalseIfTrueAndSendInRe "reduce the conditional to an ifFalse" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfTrueInIfFalseIfTrueAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1124,7 +1122,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueInIfTrueIfFalseAndNoSendIn "reduce the conditional to an ifFalse" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfTrueInIfTrueIfFalseAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1156,7 +1154,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyIfTrueInIfTrueIfFalseAndSendInRe "reduce the conditional to an ifFalse" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyIfTrueInIfTrueIfFalseAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1185,7 +1183,7 @@ methodWithEmptyIfTrueInIfTrueIfFalseAndSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithEmptyWhileFalseNoSendInCondition [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyWhileFalseNoSendInCondition. sLDeadCodeElimination currentMethod: tMethod. @@ -1209,7 +1207,7 @@ methodWithEmptyWhileFalseNoSendInCondition(SLDeadCodeEliminationTestClass * self SLDeadCodeEliminationTest >> testMethodWithEmptyWhileFalseSendInCondition [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithEmptyWhileFalseSendInCondition. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyWhileFalseSendInCondition. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1233,7 +1231,7 @@ methodWithEmptyWhileFalseSendInCondition(SLDeadCodeEliminationTestClass * self_i SLDeadCodeEliminationTest >> testMethodWithEmptyWhileTrueNoSendInCondition [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyWhileTrueNoSendInCondition. sLDeadCodeElimination currentMethod: tMethod. @@ -1257,7 +1255,7 @@ methodWithEmptyWhileTrueNoSendInCondition(SLDeadCodeEliminationTestClass * self_ SLDeadCodeEliminationTest >> testMethodWithEmptyWhileTrueSendInCondition [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithEmptyWhileTrueSendInCondition. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyWhileTrueSendInCondition. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1282,7 +1280,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNilInIfNilIfNotNilAndNoSendInR "reduce the conditional to an ifNotNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNilInIfNilIfNotNilAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1314,7 +1312,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNilInIfNilIfNotNilAndSendInRec "reduce the conditional to an ifNotNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNilInIfNilIfNotNilAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1346,7 +1344,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNilInIfNotNilIfNilAndNoSendInR "reduce the conditional to an ifNotNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNilInIfNotNilIfNilAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1378,7 +1376,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNilInIfNotNilIfNilAndSendInRec "reduce the conditional to an ifNotNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNilInIfNotNilIfNilAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1410,7 +1408,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNotNilInIfNilIfNotNilAndNoSend "reduce the conditional to an ifNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNotNilInIfNilIfNotNilAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1442,7 +1440,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNotNilInIfNilIfNotNilAndSendIn "reduce the conditional to an ifNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNotNilInIfNilIfNotNilAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1474,7 +1472,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNotNilInIfNotNilIfNilAndNoSend "reduce the conditional to an ifNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNotNilInIfNotNilIfNilAndNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1506,7 +1504,7 @@ SLDeadCodeEliminationTest >> testMethodWithEmptyifNotNilInIfNotNilIfNilAndSendIn "reduce the conditional to an ifNil" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithEmptyifNotNilInIfNotNilIfNilAndSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1537,7 +1535,7 @@ methodWithEmptyifNotNilInIfNotNilIfNilAndSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithInstanceVariableInAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithInstanceVariableInAssignment. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithInstanceVariableInAssignment. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1563,7 +1561,7 @@ methodWithInstanceVariableInAssignment(SLDeadCodeEliminationTestClass * self_in_ SLDeadCodeEliminationTest >> testMethodWithInstanceVariableInReturn [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithInstanceVariableInReturn. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithInstanceVariableInReturn. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1586,7 +1584,7 @@ methodWithInstanceVariableInReturn(SLDeadCodeEliminationTestClass * self_in_meth SLDeadCodeEliminationTest >> testMethodWithNeverUsedLocals [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithNeverUsedLocals. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithNeverUsedLocals. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -1616,7 +1614,7 @@ methodWithNeverUsedLocals(SLDeadCodeEliminationTestClass * self_in_methodWithNev SLDeadCodeEliminationTest >> testMethodWithNeverUsedLocalsFromBlock [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithNeverUsedLocalsFromBlock. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithNeverUsedLocalsFromBlock. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -1645,7 +1643,7 @@ methodWithNeverUsedLocalsFromBlock(SLDeadCodeEliminationTestClass * self_in_meth SLDeadCodeEliminationTest >> testMethodWithNeverUsedLocalsFromBlockStatement [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithNeverUsedLocalsFromBlockStatement. tMethod prepareMethodIn: ccg. @@ -1677,7 +1675,7 @@ methodWithNeverUsedLocalsFromBlockStatement(SLDeadCodeEliminationTestClass * sel SLDeadCodeEliminationTest >> testMethodWithNeverUsedLocalsFromBlockWithExpressionInArguments [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithNeverUsedLocalsFromBlockWithExpressionInArguments. @@ -1708,11 +1706,11 @@ methodWithNeverUsedLocalsFromBlockWithExpressionInArguments(SLDeadCodeEliminatio SLDeadCodeEliminationTest >> testMethodWithNeverUsedLocalsFromBlockasArguments [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithNeverUsedLocalsFromBlockasArguments. ccg prepareMethods. - ccg doBasicInlining: true. + self doInliningIn: tMethod. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1737,7 +1735,7 @@ methodWithNeverUsedLocalsFromBlockasArguments(SLDeadCodeEliminationTestClass * s SLDeadCodeEliminationTest >> testMethodWithNoRedundantLocalDefinition [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithNoRedundantLocalDefinition. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithNoRedundantLocalDefinition. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1775,7 +1773,7 @@ SLDeadCodeEliminationTest >> testMethodWithOnlyBlockValueAssignmentIntoSameVaria "all of the body is dead code" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyBlockValueAssignmentIntoSameVariableInArguments. sLDeadCodeElimination currentMethod: tMethod. @@ -1801,7 +1799,7 @@ methodWithOnlyBlockValueAssignmentIntoSameVariableInArguments(SLDeadCodeEliminat SLDeadCodeEliminationTest >> testMethodWithOnlyBlockValueAssignmentNotIntoSameVariableInArguments [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyBlockValueAssignmentNotIntoSameVariableInArguments. sLDeadCodeElimination currentMethod: tMethod. @@ -1832,10 +1830,10 @@ SLDeadCodeEliminationTest >> testMethodWithOnlyComment [ "currently the only way to get comments in a methods is through inlining, having only comments is equivalent to being empty so it shouldn't change the behavior of the dead code elimination process" | translation tMethod | - tMethod := ccg methodNamed: #methodWithOnlyComment. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyComment. ccg prepareMethods. - ccg doBasicInlining: true. + self doInliningIn: tMethod. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1860,7 +1858,7 @@ methodWithOnlyComment(SLDeadCodeEliminationTestClass * self_in_methodWithOnlyCom SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInRepeat [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithOnlyDeadCodeInRepeat. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInRepeat. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1887,7 +1885,7 @@ methodWithOnlyDeadCodeInRepeat(SLDeadCodeEliminationTestClass * self_in_methodWi SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInWhileFalse [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithOnlyDeadCodeInWhileFalse. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInWhileFalse. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1914,7 +1912,7 @@ methodWithOnlyDeadCodeInWhileFalse(SLDeadCodeEliminationTestClass * self_in_meth SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInWhileFalseBinaryNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInWhileFalseBinaryNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1941,7 +1939,7 @@ methodWithOnlyDeadCodeInWhileFalseBinaryNoSendInReceiver(SLDeadCodeEliminationTe SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInWhileFalseBinarySendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInWhileFalseBinarySendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -1970,7 +1968,7 @@ methodWithOnlyDeadCodeInWhileFalseBinarySendInReceiver(SLDeadCodeEliminationTest SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInWhileTrue [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithOnlyDeadCodeInWhileTrue. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInWhileTrue. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -1997,7 +1995,7 @@ methodWithOnlyDeadCodeInWhileTrue(SLDeadCodeEliminationTestClass * self_in_metho SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInWhileTrueBinaryNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInWhileTrueBinaryNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2025,7 +2023,7 @@ methodWithOnlyDeadCodeInWhileTrueBinaryNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithOnlyDeadCodeInWhileTrueBinarySendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyDeadCodeInWhileTrueBinarySendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2054,7 +2052,7 @@ methodWithOnlyDeadCodeInWhileTrueBinarySendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithOnlyNeverUsedLocals [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithOnlyNeverUsedLocals. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyNeverUsedLocals. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -2080,7 +2078,7 @@ methodWithOnlyNeverUsedLocals(SLDeadCodeEliminationTestClass * self_in_methodWit SLDeadCodeEliminationTest >> testMethodWithOnlyUselessAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithOnlyUselessAssignment. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithOnlyUselessAssignment. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -2106,7 +2104,7 @@ methodWithOnlyUselessAssignment(SLDeadCodeEliminationTestClass * self_in_methodW SLDeadCodeEliminationTest >> testMethodWithRedundantLocalDefinition [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithRedundantLocalDefinition. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithRedundantLocalDefinition. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -2142,7 +2140,7 @@ SLDeadCodeEliminationTest >> testMethodWithSelfAssign [ "happens because of polymorphisme or inlining" | translation tMethod | - tMethod := ccg methodNamed: #methodWithSelfAssign. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithSelfAssign. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -2169,7 +2167,7 @@ methodWithSelfAssign(SLDeadCodeEliminationTestClass * self_in_methodWithSelfAssi SLDeadCodeEliminationTest >> testMethodWithSendWithNoSideEffectInCoerce [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithSendWithNoSideEffectInCoerce. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithSendWithNoSideEffectInCoerce. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -2192,7 +2190,7 @@ methodWithSendWithNoSideEffectInCoerce(SLDeadCodeEliminationTestClass * self_in_ SLDeadCodeEliminationTest >> testMethodWithUnusedConstant [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUnusedConstant. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstant. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -2216,7 +2214,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantAndReturnInCaseOfNoSend "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantAndReturnInCaseOfNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -2255,7 +2253,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantAndReturnInCaseOfSendIn "we need to use prepareMethodIn to build switch node." | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantAndReturnInCaseOfSendInExpression. tMethod prepareMethodIn: ccg. @@ -2294,7 +2292,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfAndReturnInOthe "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCaseOfAndReturnInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -2336,7 +2334,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfAndReturnInOthe "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCaseOfAndReturnInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -2377,7 +2375,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfAndUnusedConsta "we need to use prepareMethodIn to build switch node, the cases and the otherwise all have unused constant so the node is suppressed" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCaseOfAndUnusedConstantInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -2404,7 +2402,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfAndUnusedConsta "we need to use prepareMethodIn to build switch node, the cases and the otherwise all have unused constant, the node is suppressed but the message call is kept" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCaseOfAndUnusedConstantInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -2432,7 +2430,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfNoSendInExpress "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCaseOfNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -2466,7 +2464,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCaseOfSendInExpressio "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCaseOfSendInExpression. tMethod prepareMethodIn: ccg. @@ -2499,7 +2497,7 @@ methodWithUnusedConstantInCaseOfSendInExpression(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInCoerce [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUnusedConstantInCoerce. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInCoerce. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -2522,7 +2520,7 @@ methodWithUnusedConstantInCoerce(SLDeadCodeEliminationTestClass * self_in_method SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseIfTrueNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfFalseIfTrueNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2546,7 +2544,7 @@ methodWithUnusedConstantInIfFalseIfTrueNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseIfTrueSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfFalseIfTrueSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2573,7 +2571,7 @@ methodWithUnusedConstantInIfFalseIfTrueSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfFalseNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2597,7 +2595,7 @@ methodWithUnusedConstantInIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfFalseSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfFalseSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2624,7 +2622,7 @@ methodWithUnusedConstantInIfFalseSendInReceiver(SLDeadCodeEliminationTestClass * SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilIfNotNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNilIfNotNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2648,7 +2646,7 @@ methodWithUnusedConstantInIfNilIfNotNilNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilIfNotNillSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNilIfNotNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2673,7 +2671,7 @@ methodWithUnusedConstantInIfNilIfNotNilSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2697,7 +2695,7 @@ methodWithUnusedConstantInIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2722,7 +2720,7 @@ methodWithUnusedConstantInIfNilSendInReceiver(SLDeadCodeEliminationTestClass * s SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilIfNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNotNilIfNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2746,7 +2744,7 @@ methodWithUnusedConstantInIfNotNilIfNilNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilIfNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNotNilIfNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2771,7 +2769,7 @@ methodWithUnusedConstantInIfNotNilIfNilSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNotNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2795,7 +2793,7 @@ methodWithUnusedConstantInIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClas SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfNotNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfNotNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2820,7 +2818,7 @@ methodWithUnusedConstantInIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueIfFalseNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfTrueIfFalseNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2844,7 +2842,7 @@ methodWithUnusedConstantInIfTrueIfFalseNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueIfFalseSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfTrueIfFalseSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2871,7 +2869,7 @@ methodWithUnusedConstantInIfTrueIfFalseSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfTrueNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2895,7 +2893,7 @@ methodWithUnusedConstantInIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedConstantInIfTrueSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantInIfTrueSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -2923,7 +2921,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantReturnInCaseOfAndUnused "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantReturnInCaseOfAndUnusedConstantInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -2963,7 +2961,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedConstantReturnInCaseOfAndUnused "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedConstantReturnInCaseOfAndUnusedConstantInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -3001,7 +2999,7 @@ methodWithUnusedConstantReturnInCaseOfAndUnusedConstantInOtherwiseSendInExpressi SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariable [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUnusedInstanceVariable. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariable. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -3025,7 +3023,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableAndReturnInCase "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableAndReturnInCaseOfNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3063,7 +3061,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableAndReturnInCase "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableAndReturnInCaseOfSendInExpression. tMethod prepareMethodIn: ccg. @@ -3101,7 +3099,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfAndRetu "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCaseOfAndReturnInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3138,7 +3136,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfAndRetu "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCaseOfAndReturnInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -3175,7 +3173,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfAndUnus "we need to use prepareMethodIn to build switch node, the cases and the otherwise have unused instance variable, they are all suppressed" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCaseOfAndUnusedInstanceVariableInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3201,7 +3199,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfAndUnus "we need to use prepareMethodIn to build switch node, the cases and the otherwise all have unused instance variable, the node is suppressed but the message call is kept" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCaseOfAndUnusedInstanceVariableInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -3228,7 +3226,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfNoSendI "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCaseOfNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3262,7 +3260,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCaseOfSendInE "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCaseOfSendInExpression. tMethod prepareMethodIn: ccg. @@ -3296,7 +3294,7 @@ methodWithUnusedInstanceVariableInCaseOfSendInExpression(SLDeadCodeEliminationTe SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInCoerce [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUnusedInstanceVariableInCoerce. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInCoerce. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -3319,7 +3317,7 @@ methodWithUnusedInstanceVariableInCoerce(SLDeadCodeEliminationTestClass * self_i SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseIfTrueNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfFalseIfTrueNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3343,7 +3341,7 @@ methodWithUnusedInstanceVariableInIfFalseIfTrueNoSendInReceiver(SLDeadCodeElimin SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3370,7 +3368,7 @@ methodWithUnusedInstanceVariableInIfFalseIfTrueSendInReceiver(SLDeadCodeEliminat SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfFalseNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3394,7 +3392,7 @@ methodWithUnusedInstanceVariableInIfFalseNoSendInReceiver(SLDeadCodeEliminationT SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfFalseSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfFalseSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3421,7 +3419,7 @@ methodWithUnusedInstanceVariableInIfFalseSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilIfNotNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNilIfNotNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3445,7 +3443,7 @@ methodWithUnusedInstanceVariableInIfNilIfNotNilNoSendInReceiver(SLDeadCodeElimin SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3470,7 +3468,7 @@ methodWithUnusedInstanceVariableInIfNilIfNotNilSendInReceiver(SLDeadCodeEliminat SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3494,7 +3492,7 @@ methodWithUnusedInstanceVariableInIfNilNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3519,7 +3517,7 @@ methodWithUnusedInstanceVariableInIfNilSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilIfNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNotNilIfNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3543,7 +3541,7 @@ methodWithUnusedInstanceVariableInIfNotNilIfNilNoSendInReceiver(SLDeadCodeElimin SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3568,7 +3566,7 @@ methodWithUnusedInstanceVariableInIfNotNilIfNilSendInReceiver(SLDeadCodeEliminat SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNotNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3592,7 +3590,7 @@ methodWithUnusedInstanceVariableInIfNotNilNoSendInReceiver(SLDeadCodeElimination SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfNotNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfNotNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3616,7 +3614,7 @@ methodWithUnusedInstanceVariableInIfNotNilSendInReceiver(SLDeadCodeEliminationTe SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueIfFalseNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfTrueIfFalseNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3640,7 +3638,7 @@ methodWithUnusedInstanceVariableInIfTrueIfFalseNoSendInReceiver(SLDeadCodeElimin SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3667,7 +3665,7 @@ methodWithUnusedInstanceVariableInIfTrueIfFalseSendInReceiver(SLDeadCodeEliminat SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfTrueNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3691,7 +3689,7 @@ methodWithUnusedInstanceVariableInIfTrueNoSendInReceiver(SLDeadCodeEliminationTe SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableInIfTrueSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableInIfTrueSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -3719,7 +3717,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableReturnInCaseOfA "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3758,7 +3756,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedInstanceVariableReturnInCaseOfA "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -3796,7 +3794,7 @@ methodWithUnusedInstanceVariableReturnInCaseOfAndUnusedInstanceVariableInOtherwi SLDeadCodeEliminationTest >> testMethodWithUnusedVariable [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUnusedVariable. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariable. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -3821,7 +3819,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableAndReturnInCaseOfNoSend "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableAndReturnInCaseOfNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3863,7 +3861,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableAndReturnInCaseOfSendIn "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableAndReturnInCaseOfSendInExpression. tMethod prepareMethodIn: ccg. @@ -3904,7 +3902,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfAndReturnInOthe "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCaseOfAndReturnInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -3944,7 +3942,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfAndReturnInOthe "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCaseOfAndReturnInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -3984,7 +3982,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfAndUnusedVariab "we need to use prepareMethodIn to build switch node, the case and the otherwise all have unused variable, the node is suppressed" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -4010,7 +4008,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfAndUnusedVariab "we need to use prepareMethodIn to build switch node, the cases and the otherwise all have unused variable, the node is suppressed but the message call is kept" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCaseOfAndUnusedVariableInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -4037,7 +4035,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfNoSendInExpress "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCaseOfNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -4072,7 +4070,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCaseOfSendInExpressio "we need to use prepareMethodIn to build switch node" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCaseOfSendInExpression. tMethod prepareMethodIn: ccg. @@ -4105,7 +4103,7 @@ methodWithUnusedVariableInCaseOfSendInExpression(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInCoerce [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUnusedVariableInCoerce. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInCoerce. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. @@ -4128,7 +4126,7 @@ methodWithUnusedVariableInCoerce(SLDeadCodeEliminationTestClass * self_in_method SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseIfTrueNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfFalseIfTrueNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4152,7 +4150,7 @@ methodWithUnusedVariableInIfFalseIfTrueNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseIfTrueSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfFalseIfTrueSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4179,7 +4177,7 @@ methodWithUnusedVariableInIfFalseIfTrueSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfFalseNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4203,7 +4201,7 @@ methodWithUnusedVariableInIfFalseNoSendInReceiver(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfFalseSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfFalseSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4231,7 +4229,7 @@ SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilIfNotNilNoSendIn "in case of variable, the definition is still here after the supression process, we need to use removeUnusedTempsAndNilIfRequiredIn: to clean the ast" | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNilIfNotNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4255,7 +4253,7 @@ methodWithUnusedVariableInIfNilIfNotNilNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilIfNotNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNilIfNotNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4280,7 +4278,7 @@ methodWithUnusedVariableInIfNilIfNotNilSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4304,7 +4302,7 @@ methodWithUnusedVariableInIfNilNoSendInReceiver(SLDeadCodeEliminationTestClass * SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4329,7 +4327,7 @@ methodWithUnusedVariableInIfNilSendInReceiver(SLDeadCodeEliminationTestClass * s SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilIfNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNotNilIfNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4353,7 +4351,7 @@ methodWithUnusedVariableInIfNotNilIfNilNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilIfNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNotNilIfNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4378,7 +4376,7 @@ methodWithUnusedVariableInIfNotNilIfNilSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNotNilNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4402,7 +4400,7 @@ methodWithUnusedVariableInIfNotNilNoSendInReceiver(SLDeadCodeEliminationTestClas SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfNotNilSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfNotNilSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4427,7 +4425,7 @@ methodWithUnusedVariableInIfNotNilSendInReceiver(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueIfFalseNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfTrueIfFalseNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4451,7 +4449,7 @@ methodWithUnusedVariableInIfTrueIfFalseNoSendInReceiver(SLDeadCodeEliminationTes SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueIfFalseSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfTrueIfFalseSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4478,7 +4476,7 @@ methodWithUnusedVariableInIfTrueIfFalseSendInReceiver(SLDeadCodeEliminationTestC SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueNoSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfTrueNoSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4502,7 +4500,7 @@ methodWithUnusedVariableInIfTrueNoSendInReceiver(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithUnusedVariableInIfTrueSendInReceiver [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableInIfTrueSendInReceiver. sLDeadCodeElimination currentMethod: tMethod. @@ -4529,7 +4527,7 @@ methodWithUnusedVariableInIfTrueSendInReceiver(SLDeadCodeEliminationTestClass * SLDeadCodeEliminationTest >> testMethodWithUnusedVariableReturnInCaseOfAndUnusedVariableInOtherwiseNoSendInExpression [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableReturnInCaseOfAndUnusedVariableInOtherwiseNoSendInExpression:. tMethod prepareMethodIn: ccg. @@ -4569,7 +4567,7 @@ methodWithUnusedVariableReturnInCaseOfAndUnusedVariableInOtherwiseNoSendInExpres SLDeadCodeEliminationTest >> testMethodWithUnusedVariableReturnInCaseOfAndUnusedVariableInOtherwiseSendInExpression [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUnusedVariableReturnInCaseOfAndUnusedVariableInOtherwiseSendInExpression. tMethod prepareMethodIn: ccg. @@ -4610,7 +4608,7 @@ methodWithUnusedVariableReturnInCaseOfAndUnusedVariableInOtherwiseSendInExpressi SLDeadCodeEliminationTest >> testMethodWithUselessAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUselessAssignment. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessAssignment. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -4641,7 +4639,7 @@ methodWithUselessAssignment(SLDeadCodeEliminationTestClass * self_in_methodWithU SLDeadCodeEliminationTest >> testMethodWithUselessCodeInBinaryIterativeNoSendInLimit [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUselessCodeInBinaryIterativeNoSendInLimit. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessCodeInBinaryIterativeNoSendInLimit. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -4674,7 +4672,7 @@ methodWithUselessCodeInBinaryIterativeNoSendInLimit(SLDeadCodeEliminationTestCla SLDeadCodeEliminationTest >> testMethodWithUselessCodeInBinaryIterativeSendWithNoSideEffectsInLimit [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessCodeInBinaryIterativeSendWithNoSideEffectsInLimit. tMethod prepareMethodIn: ccg. @@ -4706,7 +4704,7 @@ methodWithUselessCodeInBinaryIterativeSendWithNoSideEffectsInLimit(SLDeadCodeEli { #category : 'dead-code-in-do-iterative' } SLDeadCodeEliminationTest >> testMethodWithUselessCodeInBinaryIterativeSendWithSideEffectsInLimit [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessCodeInBinaryIterativeSendWithSideEffectsInLimit. tMethod prepareMethodIn: ccg. @@ -4740,7 +4738,7 @@ methodWithUselessCodeInBinaryIterativeSendWithSideEffectsInLimit(SLDeadCodeElimi SLDeadCodeEliminationTest >> testMethodWithUselessLocalsDefinitionsWithSameNameInSubBranches [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessLocalsDefinitionsWithSameNameInSubBranches. tMethod prepareMethodIn: ccg. @@ -4779,7 +4777,7 @@ methodWithUselessLocalsDefinitionsWithSameNameInSubBranches(SLDeadCodeEliminatio SLDeadCodeEliminationTest >> testMethodWithUselessLocalsInSubBranches [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithUselessLocalsInSubBranches. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessLocalsInSubBranches. tMethod prepareMethodIn: ccg. sLDeadCodeElimination currentMethod: tMethod. @@ -4814,7 +4812,7 @@ methodWithUselessLocalsInSubBranches(SLDeadCodeEliminationTestClass * self_in_me SLDeadCodeEliminationTest >> testMethodWithUselessLocalsWithSameNameInSubBranches [ | translation tMethod | - tMethod := ccg methodNamed: + tMethod := self addStructMethodAndMethodCalledBy: #methodWithUselessLocalsWithSameNameInSubBranches. tMethod prepareMethodIn: ccg. @@ -4856,7 +4854,7 @@ methodWithUselessLocalsWithSameNameInSubBranches(SLDeadCodeEliminationTestClass SLDeadCodeEliminationTest >> testMethodWithVariableInAssignment [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithVariableInAssignment. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithVariableInAssignment. sLDeadCodeElimination currentMethod: tMethod. ccg currentMethod: tMethod. @@ -4884,7 +4882,7 @@ methodWithVariableInAssignment(SLDeadCodeEliminationTestClass * self_in_methodWi SLDeadCodeEliminationTest >> testMethodWithVariableInReturn [ | translation tMethod | - tMethod := ccg methodNamed: #methodWithVariableInReturn. + tMethod := self addStructMethodAndMethodCalledBy: #methodWithVariableInReturn. sLDeadCodeElimination currentMethod: tMethod. ccg currentMethod: tMethod. @@ -4912,10 +4910,10 @@ SLDeadCodeEliminationTest >> testSwitchWithOnlyCommentNoSendInReceiver [ "currently the only way to get comments in a methods is through inlining, having only comments is equivalent to being empty so it shouldn't change the behavior of the dead code elimination process" | translation tMethod | - tMethod := ccg methodNamed: #switchWithOnlyCommentNoSendInReceiver:. + tMethod := self addStructMethodAndMethodCalledBy: #switchWithOnlyCommentNoSendInReceiver:. ccg prepareMethods. - ccg doBasicInlining: true. + self doInliningIn: tMethod. sLDeadCodeElimination currentMethod: tMethod. ccg currentMethod: tMethod. @@ -4940,10 +4938,10 @@ SLDeadCodeEliminationTest >> testSwitchWithOnlyCommentSendInReceiver [ "currently the only way to get comments in a methods is through inlining, having only comments is equivalent to being empty so it shouldn't change the behavior of the dead code elimination process" | translation tMethod | - tMethod := ccg methodNamed: #switchWithOnlyCommentSendInReceiver. + tMethod := self addStructMethodAndMethodCalledBy: #switchWithOnlyCommentSendInReceiver. ccg prepareMethods. - ccg doBasicInlining: true. + self doInliningIn: tMethod. sLDeadCodeElimination currentMethod: tMethod. sLDeadCodeElimination removeDeadCodeInCurrentMethod. diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 7daf64c78f2..42a584e9c36 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -30,9 +30,8 @@ SLInliningTest >> inliningStrategy: aString [ SLInliningTest >> test2ChainInlining [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodC. + method := self addMethodAndMethodCalledBy: #methodC. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -58,9 +57,8 @@ methodC(void) SLInliningTest >> test2ChainInliningAssignOnReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodCAssignOnReturn. + method := self addMethodAndMethodCalledBy: #methodCAssignOnReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -87,9 +85,8 @@ SLInliningTest >> test2ChainInliningEmptyMethodAWithArgumentsInlined [ "a bug present since old version, vm works but it would be nice to fix it one day" | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #emptyMethodAWithArgumentsInlined. + method := self addMethodAndMethodCalledBy: #emptyMethodAWithArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -122,9 +119,9 @@ emptyMethodAWithArgumentsInlined(void) SLInliningTest >> test2ChainInliningEmptyMethodAWithSimpleArgumentsInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #emptyMethodAWithSimpleArgumentsInlined. + method := self addMethodAndMethodCalledBy: + #emptyMethodAWithSimpleArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -149,9 +146,9 @@ emptyMethodAWithSimpleArgumentsInlined(void) SLInliningTest >> test2ChainInliningMethodAWithArgumentsInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodAWithArgumentsInlined. + method := self addMethodAndMethodCalledBy: + #methodAWithArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -171,9 +168,9 @@ methodAWithArgumentsInlined(void) SLInliningTest >> test2ChainInliningMethodAWithReturningSendArgumentsInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodAWithReturningSendArgumentsInlined. + method := self addMethodAndMethodCalledBy: + #methodAWithReturningSendArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -193,10 +190,9 @@ methodAWithReturningSendArgumentsInlined(void) SLInliningTest >> test2ChainInliningMethodAWithReturningSendReturningArgumentsInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -216,9 +212,10 @@ methodAWithReturningSendWithMethodWithMultipleReturnInArgumentsInlined(void) SLInliningTest >> test2ChainInliningMethodAWithSimpleArgumentsInlined [ | method translation | - ccg doBasicInlining: true. + method := self addMethodAndMethodCalledBy: + #methodAWithSimpleArgumentsInlined. - method := ccg methodNamed: #methodAWithSimpleArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -239,9 +236,9 @@ methodAWithSimpleArgumentsInlined(void) SLInliningTest >> test2ChainInliningMultipleReturn [ | method translation | - ccg doBasicInlining: true. + method := self addMethodAndMethodCalledBy: #methodAMultipleReturn. - method := ccg methodNamed: #methodAMultipleReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -276,10 +273,9 @@ methodAMultipleReturn(void) SLInliningTest >> test2ChainInliningMultipleReturnAsAssignmentExpression [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAMultipleReturnAsAssignmentExpression. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -318,9 +314,9 @@ methodAMultipleReturnAsAssignmentExpression(void) SLInliningTest >> test2ChainInliningMultipleReturnAsReturnExpression [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodAMultipleReturnAsReturnExpression. + method := self addMethodAndMethodCalledBy: + #methodAMultipleReturnAsReturnExpression. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -353,10 +349,9 @@ methodAMultipleReturnAsReturnExpression(void) SLInliningTest >> test2ChainInliningMultipleReturnAsReturnExpressionAndStatement [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAMultipleReturnAsReturnExpressionAndStatement. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -385,9 +380,8 @@ methodAMultipleReturnAsReturnExpressionAndStatement(void) SLInliningTest >> test2ChainInliningReturnOnAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodCReturnOnAssignment. + method := self addMethodAndMethodCalledBy: #methodCReturnOnAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -417,9 +411,8 @@ methodCReturnOnAssignment(void) SLInliningTest >> test2ChainInliningSimpleReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodASimpleReturn. + method := self addMethodAndMethodCalledBy: #methodASimpleReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -443,13 +436,14 @@ SLInliningTest >> testCollectStatementsForInliningInMethodWithAvoidedSelector [ "this method collect statements list for inlining, some specific selectors have special comportment regarding inlining strategy like avoiding inlining he receiver or the some arguments" | method statementsForInlining | - method := ccg methodNamed: #methodWithAvoidedSelectors. + method := self addMethodAndMethodCalledBy: + #methodWithAvoidedSelectors. sLInliner currentMethod: method. sLInliner codeGenerator: ccg. statementsForInlining := sLInliner statementsListsForInliningInCurrentMethod. - self assert: statementsForInlining size equals: 6. + self assert: statementsForInlining size equals: 6 ] { #category : 'inlining-simple' } @@ -483,10 +477,9 @@ SLInliningTest >> testInlineInSwitchRemovesReturnStatement [ SLInliningTest >> testMethodAInlineIfFalseReturningIfTrueInAssignement [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineIfFalseReturningIfTrueInAssignement. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -518,10 +511,9 @@ methodAInlineIfFalseReturningIfTrueInAssignement(void) SLInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineMultipleIfFalseReturningIfTrueInAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -567,10 +559,9 @@ methodAInlineMultipleIfFalseReturningIfTrueInAssignment(void) SLInliningTest >> testMethodAInlineMultipleIfFalseReturningIfTrueInReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineMultipleIfFalseReturningIfTrueInReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -607,9 +598,9 @@ methodAInlineMultipleIfFalseReturningIfTrueInReturn(void) SLInliningTest >> testMethodAInlineReturningIfTrue [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodAInlineReturningIfTrueInReturn. + method := self addMethodAndMethodCalledBy: + #methodAInlineReturningIfTrueInReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -631,10 +622,9 @@ methodAInlineReturningIfTrueInReturn(void) SLInliningTest >> testMethodAInlineReturningIfTrueIfFalseInAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningIfTrueIfFalseInAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -659,10 +649,9 @@ methodAInlineReturningIfTrueIfFalseInAssignment(void) SLInliningTest >> testMethodAInlineReturningIfTrueIfFalseInReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningIfTrueIfFalseInReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -684,10 +673,9 @@ methodAInlineReturningIfTrueIfFalseInReturn(void) SLInliningTest >> testMethodAInlineReturningIfTrueInAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningIfTrueInAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -712,10 +700,9 @@ methodAInlineReturningIfTrueInAssignment(void) SLInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningInlinedIfTrueIfFalseInAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -749,10 +736,9 @@ methodAInlineReturningInlinedIfTrueIfFalseInAssignment(void) SLInliningTest >> testMethodAInlineReturningInlinedIfTrueIfFalseInReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningInlinedIfTrueIfFalseInReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -783,10 +769,9 @@ methodAInlineReturningInlinedIfTrueIfFalseInReturn(void) SLInliningTest >> testMethodAInlineReturningInlinedIfTrueInAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningInlinedIfTrueInAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -815,10 +800,9 @@ methodAInlineReturningInlinedIfTrueInAssignment(void) SLInliningTest >> testMethodAInlineReturningInlinedIfTrueInReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineReturningInlinedIfTrueInReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -843,10 +827,9 @@ methodAInlineReturningInlinedIfTrueInReturn(void) SLInliningTest >> testMethodAInlineifFalseReturningIfTrueInReturn [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAInlineIfFalseReturningIfTrueInReturn. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -872,9 +855,8 @@ methodAInlineIfFalseReturningIfTrueInReturn(void) SLInliningTest >> testMethodAReturnAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodAReturnAssignment. + method := self addMethodAndMethodCalledBy: #methodAReturnAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -895,9 +877,9 @@ methodAReturnAssignment(void) SLInliningTest >> testMethodAReturnBlockAssignment [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodAReturnBlockAssignment. + method := self addMethodAndMethodCalledBy: + #methodAReturnBlockAssignment. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -921,10 +903,9 @@ methodAReturnBlockAssignment(void) SLInliningTest >> testMethodAWithReturningSendWithReturningIfInArgumentsInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAWithReturningSendWithReturningIfInArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -946,10 +927,9 @@ methodAWithReturningSendWithReturningIfInArgumentsInlined(void) SLInliningTest >> testMethodAWithReturningSendWithReturningIfInSendInArgumentsInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodAWithReturningSendWithReturningIfInSendInArgumentsInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -971,9 +951,9 @@ methodAWithReturningSendWithReturningIfInSendInArgumentsInlined(void) SLInliningTest >> testMethodInlineVariableWithSameName [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodInlineVariableWithSameName. + method := self addMethodAndMethodCalledBy: + #methodInlineVariableWithSameName. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1000,9 +980,9 @@ methodInlineVariableWithSameName(void) SLInliningTest >> testMethodWithAssignmentOnRepeatInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithAssignmentOnRepeatInlined. + method := self addMethodAndMethodCalledBy: + #methodWithAssignmentOnRepeatInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1037,9 +1017,9 @@ methodWithAssignmentOnRepeatInlined(void) SLInliningTest >> testMethodWithAssignmentOnToByDoInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithAssignmentOnToByDoInlined. + method := self addMethodAndMethodCalledBy: + #methodWithAssignmentOnToByDoInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1074,9 +1054,9 @@ methodWithAssignmentOnToByDoInlined(void) SLInliningTest >> testMethodWithAssignmentOnToDoInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithAssignmentOnToDoInlined. + method := self addMethodAndMethodCalledBy: + #methodWithAssignmentOnToDoInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1111,10 +1091,9 @@ methodWithAssignmentOnToDoInlined(void) SLInliningTest >> testMethodWithAssignmentOnWhileTrueBinaryInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodWithAssignmentOnWhileTrueBinaryInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1151,9 +1130,9 @@ methodWithAssignmentOnWhileTrueBinaryInlined(void) SLInliningTest >> testMethodWithAssignmentOnWhileTrueInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithAssignmentOnWhileTrueInlined. + method := self addMethodAndMethodCalledBy: + #methodWithAssignmentOnWhileTrueInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1188,9 +1167,9 @@ methodWithAssignmentOnWhileTrueInlined(void) SLInliningTest >> testMethodWithBlockArgumentInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithBlockArgumentInlined. + method := self addMethodAndMethodCalledBy: + #methodWithBlockArgumentInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1217,10 +1196,9 @@ methodWithBlockArgumentInlined(void) SLInliningTest >> testMethodWithBlockWithReturningIfArgumentInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodWithBlockWithReturningIfArgumentInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1258,10 +1236,9 @@ methodWithBlockWithReturningIfArgumentInlined(void) SLInliningTest >> testMethodWithBlockWithReturningIfWithGotoInlinedArgumentInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodWithBlockWithReturningIfWithGotoInlinedArgumentInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1304,10 +1281,9 @@ methodWithBlockWithReturningIfWithGotoInlinedArgumentInlined(void) SLInliningTest >> testMethodWithBlockWithReturningIfWithInlinedArgumentInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodWithBlockWithReturningIfWithInlinedArgumentInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1346,9 +1322,9 @@ methodWithBlockWithReturningIfWithInlinedArgumentInlined(void) SLInliningTest >> testMethodWithIfTrueReceiverInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithIfTrueReceiverInlined. + method := self addMethodAndMethodCalledBy: + #methodWithIfTrueReceiverInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1360,28 +1336,25 @@ SLInliningTest >> testMethodWithIfTrueReceiverInlined [ static sqInt methodWithIfTrueReceiverInlined(void) { - { - /* begin methodWithMultipleBooleanReturn */ - if (methodB()) { - goto l2; - } - /* begin methodCMultipleReturn */ - if (methodC()) { - goto l3; - } - 3 + 3; - l3: - ; - /* end methodCMultipleReturn */ - /* end methodWithMultipleBooleanReturn; fall through */ - /* end methodWithMultipleBooleanReturn */ - /* begin methodB */ - 2 + 2; - /* end methodB */ - l2: - ; + /* begin methodWithMultipleBooleanReturn */ + if (methodB()) { + goto l2; } - return 1; + /* begin methodCMultipleReturn */ + if (methodC()) { + goto l3; + } + 3 + 3; + l3: + ; + /* end methodCMultipleReturn */ + /* end methodWithMultipleBooleanReturn; fall through */ + /* end methodWithMultipleBooleanReturn */ + /* begin methodB */ + 2 + 2; + /* end methodB */ + l2: + return 1; }' ] @@ -1389,9 +1362,9 @@ methodWithIfTrueReceiverInlined(void) SLInliningTest >> testMethodWithReturningIfTrueReceiverInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: #methodWithReturningIfTrueReceiverInlined. + method := self addMethodAndMethodCalledBy: + #methodWithReturningIfTrueReceiverInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1403,29 +1376,26 @@ SLInliningTest >> testMethodWithReturningIfTrueReceiverInlined [ static sqInt methodWithReturningIfTrueReceiverInlined(void) { - { - /* begin methodWithMultipleBooleanReturn */ - if (methodB()) { - goto l2; - } - /* begin methodCMultipleReturn */ - if (methodC()) { - goto l3; - } - 3 + 3; - l3: - ; - /* end methodCMultipleReturn */ - /* end methodWithMultipleBooleanReturn; fall through */ - /* end methodWithMultipleBooleanReturn */ - /* begin methodB */ - 2 + 2; - /* end methodB */ - return 0; - l2: - ; + /* begin methodWithMultipleBooleanReturn */ + if (methodB()) { + goto l2; } - return 1; + /* begin methodCMultipleReturn */ + if (methodC()) { + goto l3; + } + 3 + 3; + l3: + ; + /* end methodCMultipleReturn */ + /* end methodWithMultipleBooleanReturn; fall through */ + /* end methodWithMultipleBooleanReturn */ + /* begin methodB */ + 2 + 2; + /* end methodB */ + return 0; + l2: + return 1; }' ] @@ -1433,10 +1403,9 @@ methodWithReturningIfTrueReceiverInlined(void) SLInliningTest >> testMethodWithReturningIfWithReturnPartiallyPushedDownInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodWithReturningIfWithReturnPartiallyPushedDownInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1459,10 +1428,9 @@ methodWithReturningIfWithReturnPartiallyPushedDownInlined(void) SLInliningTest >> testMethodWithReturningIfWithReturnPushedDownInlined [ | method translation | - ccg doBasicInlining: true. - - method := ccg methodNamed: + method := self addMethodAndMethodCalledBy: #methodWithReturningIfWithReturnPushedDownInlined. + self doInliningIn: method. translation := self translate: method. translation := translation trimBoth. @@ -1485,16 +1453,13 @@ methodWithReturningIfWithReturnPushedDownInlined(void) SLInliningTest >> testSimpleInlining [ | method sendStatements | - ccg doBasicInlining: true. + method := self addMethodAndMethodCalledBy: #methodA. + self doInliningIn: method. - method := ccg methodNamed: #methodA. - sendStatements := method parseTree statements select: #isSend. self assert: sendStatements size equals: 2. self assert: sendStatements first selector equals: #+. self assert: sendStatements second selector equals: #+ - - ] { #category : 'parameters' } diff --git a/smalltalksrc/VMMaker/SLInliningTest.extension.st b/smalltalksrc/VMMaker/SLInliningTest.extension.st index 91016faeb37..b9a6fbc68fc 100644 --- a/smalltalksrc/VMMaker/SLInliningTest.extension.st +++ b/smalltalksrc/VMMaker/SLInliningTest.extension.st @@ -2,15 +2,15 @@ Extension { #name : 'SLInliningTest' } { #category : '*VMMaker' } SLInliningTest >> setUp [ - super setUp. - + + super setUp. + self perform: inliningStrategy. inliner codeGenerator: ccg. ccg inliner: inliner. - - ccg addClass: SLMockInliningTestClass. + testClass := SLMockInliningTestClass. + "necessary to get the type of sqInt" SpurMemoryManager initBytesPerWord: 8. - ccg inferTypes. sLInliner := SLInlinerWithAnnotation new ] From 469204eb190dc134ac81f480f935f929d9424c2b Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 5 Aug 2025 15:43:47 +0200 Subject: [PATCH 68/86] optimize a bit for conditional inlining + fix a potential bug with TDefine node being the same node every where + cleaning --- smalltalksrc/Slang/SLInliner.class.st | 3 + .../Slang/SLInlinerWithAnnotation.class.st | 216 ++++++++++++------ .../Slang/SLNodeAnnotatorVisitor.class.st | 14 +- smalltalksrc/Slang/TVariableNode.class.st | 3 +- 4 files changed, 150 insertions(+), 86 deletions(-) diff --git a/smalltalksrc/Slang/SLInliner.class.st b/smalltalksrc/Slang/SLInliner.class.st index 1cb309b7c2f..a38724dd8fa 100644 --- a/smalltalksrc/Slang/SLInliner.class.st +++ b/smalltalksrc/Slang/SLInliner.class.st @@ -465,6 +465,7 @@ SLInliner >> isInlineableFunctionCall: aNode [ codeGenerator maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. + ^ (codeGenerator methodNamed: aNode selector) ifNil: [ aNode asTransformedConstantPerform @@ -488,7 +489,9 @@ SLInliner >> isInlineableSend: aNode [ | m | codeGenerator maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. + m := codeGenerator methodNamed: aNode selector. "nil if builtin or external function" + ^ m isNotNil and: [ m ~~ currentMethod and: [ m mayBeInlined and: [ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index fb2eacdc109..d427cce2317 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -181,12 +181,15 @@ SLInlinerWithAnnotation >> initialize [ sLNodeAnnotatorVisitor := SLNodeAnnotatorVisitor new ] -{ #category : 'inlining-support' } +{ #category : 'inlining-support-function-call' } SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ | sel meth inlinedReplacement | (aSendNode selector beginsWith: 'perform:') ifTrue: [ - ^ self inlineFunctionCall: aSendNode asTransformedConstantPerform ]. + ^ self + inlineFunctionCall: aSendNode asTransformedConstantPerform + fromBuiltin: aSendNode ]. + aSendNode receiver isSend ifFalse: [ ^ nil ]. sel := aSendNode receiver selector. meth := codeGenerator methodNamed: sel. (meth notNil and: [ meth inline == true ]) ifFalse: [ ^ nil ]. @@ -201,7 +204,7 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ ^ nil ] -{ #category : 'inlining-support' } +{ #category : 'inlining-support-statement' } SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ "If the given statement node can be inlined, answer if anything was done." @@ -221,7 +224,7 @@ SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ ^ false ] -{ #category : 'inlining-support-condtional' } +{ #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> inlineConditional: aSendNode [ "If possible answer the inlining of a conditional, otherwise answer nil. Currently the only pattern we support is @@ -236,7 +239,7 @@ SLInlinerWithAnnotation >> inlineConditional: aSendNode [ ifFalse: [ self inlineGuardingConditional: aSendNode ] ] -{ #category : 'inlining-support-condtional' } +{ #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: isGuarding [ "init data for inlining conditional" @@ -265,14 +268,35 @@ SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode ^ data ] -{ #category : 'inlining-support' } +{ #category : 'inlining-support-function-call' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ - ^ self inlineFunctionCall: aSendNode inInlineableConditional: false + ^ self + inlineFunctionCall: aSendNode + inInlineableConditional: false + fromBuiltin: nil ] -{ #category : 'inlining-support' } +{ #category : 'inlining-support-function-call' } +SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode fromBuiltin: aNOdeOrNil [ + + ^ self + inlineFunctionCall: aSendNode + inInlineableConditional: false + fromBuiltin: aNOdeOrNil +] + +{ #category : 'inlining-support-function-call' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional: aBoolean [ + + ^ self + inlineFunctionCall: aSendNode + inInlineableConditional: aBoolean + fromBuiltin: nil +] + +{ #category : 'inlining-support-function-call' } +SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional: aBoolean fromBuiltin: aNodeOrNil [ "Answer the body of the called function, substituting the actual parameters for the formal argument variables in the method body. Assume caller has established that: @@ -313,24 +337,31 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional "same 'optimization' as before but with comments if possible" parseTree children size = 1 - ifTrue: [ meth parseTree: parseTree last ] + ifTrue: [ + meth parseTree: parseTree last. + parseTree := parseTree last ] ifFalse: [ self addBeginAndEndCommentsFor: parseTree selector: selector exitLabel: nil ]. - ^ meth parseTree + sLNodeAnnotatorVisitor + visitFromBranchStartingAt: (aNodeOrNil ifNil: [ aSendNode ]) + replacement: parseTree. + + ^ parseTree ] -{ #category : 'inlining-support-condtional' } +{ #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ "Inline aSend ifTrue:/ifFalse: [statements] where aSend is inlineable and always answers booleans. We convert the boolean returns in aSend to jumps." - | data evaluateIfTrue replacementTree map lastNode evaluateLabel skipLabel | + | data evaluateIfTrue replacementTree map lastNode evaluateLabel skipLabel selector inlineStmts | + selector := aSendNode receiver selector. data := self inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: true. @@ -356,7 +387,7 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ node == lastNode ifTrue: [ TLabeledCommentNode new setComment: - 'end ' , aSendNode receiver selector , '; fall through' ] + 'end ' , selector , '; fall through' ] ifFalse: [ evaluateLabel ifNil: [ evaluateLabel := TLabeledCommentNode new setLabel: @@ -367,25 +398,34 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ data at: #map put: map. self replaceNodeIn: aSendNode with: data. - ^ TStatementListNode new - setArguments: #( ) - statements: (evaluateLabel - ifNil: [ - replacementTree statements - , aSendNode arguments first statements , { skipLabel } ] - ifNotNil: [ - replacementTree statements , { evaluateLabel } - , aSendNode arguments first statements , { skipLabel } ]) + inlineStmts := TStatementListNode new + setArguments: #( ) + statements: (evaluateLabel + ifNil: [ + replacementTree statements + , aSendNode arguments first statements + , { skipLabel } ] + ifNotNil: [ + replacementTree statements , { evaluateLabel } + , aSendNode arguments first statements + , { skipLabel } ]). + + sLNodeAnnotatorVisitor + visitFromBranchStartingAt: aSendNode + replacement: inlineStmts. + + ^ inlineStmts ] -{ #category : 'inlining-support-condtional' } +{ #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ "Inline aSend ifTrue:/ifFalse: [^expr] where aSend is inlineable and always answers booleans. We inline ^expr into aSend." - | data returnIfTrue returnNode replacementTree map lastNode label | + | data returnIfTrue returnNode replacementTree map lastNode label selector inlineStmts | + selector := aSendNode receiver selector. data := self inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: false. @@ -418,23 +458,32 @@ SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ node == lastNode ifTrue: [ TLabeledCommentNode new setComment: - 'end ' , aSendNode receiver selector , '; fall through' ] + 'end ' , selector , '; fall through' ] ifFalse: [ label ifNil: [ label := TLabeledCommentNode new setLabel: - (currentMethod unusedLabelForInlining: (data at: #method)) ]. + (currentMethod unusedLabelForInlining: + (data at: #method)) ]. TGoToNode label: label label ] ]) ] ]. - + data at: #map put: map. self replaceNodeIn: aSendNode with: data. - ^ label ifNil: [ replacementTree ] ifNotNil: [ - TStatementListNode new setArguments: #( ) statements: { - replacementTree. - label } ] + inlineStmts := label ifNil: [ replacementTree ] ifNotNil: [ + TStatementListNode new + setArguments: #( ) + statements: { + replacementTree. + label } ]. + + sLNodeAnnotatorVisitor + visitFromBranchStartingAt: aSendNode + replacement: inlineStmts. + + ^ inlineStmts ] -{ #category : 'inlining-support' } +{ #category : 'inlining-support-statement' } SLInlinerWithAnnotation >> inlineSend: aSendNode [ "Answer a collection of statements to replace the given send. directReturn indicates that the send is the expression in a return statement, so returns can be left in the @@ -522,15 +571,6 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ ^ inlineStmts ] -{ #category : 'transformation' } -SLInlinerWithAnnotation >> invert: aSendNode replacement: aTStatementList [ - - | parent | - parent := aSendNode parent. - - parent replaceChild: aSendNode with: aTStatementList -] - { #category : 'inlining-decision' } SLInlinerWithAnnotation >> isConditionalToBeTransformedForAssignment: aSend [ "Answer if a send is of the form @@ -560,16 +600,18 @@ SLInlinerWithAnnotation >> isFunctional: aTMethod [ Answer false for methods with return types other than the simple integer types to work around bugs in the inliner." - | parseTree last | + | parseTree last size | parseTree := aTMethod parseTree. - parseTree statements size = 1 ifFalse: [ - (parseTree statements size = 2 and: [ - parseTree statements first isSend and: [ - parseTree statements first selector == #flag: or: [ - (codeGenerator isAssertSelector: - parseTree statements first selector) and: [ - parseTree statements first selector ~~ #asserta: ] ] ] ]) - ifFalse: [ ^ false ] ]. + size := parseTree sizeWithoutComments. + + size = 1 ifFalse: [ + (size = 2 and: [ + | first | + first := parseTree firstNonCommentStatement. + first isSend and: [ + first selector == #flag: or: [ + (codeGenerator isAssertSelector: first selector) and: [ + first selector ~~ #asserta: ] ] ] ]) ifFalse: [ ^ false ] ]. last := parseTree lastNonCommentStatement. (last isReturn or: [ sLNodeAnnotatorVisitor @@ -618,6 +660,7 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr nodeForVisitorInfo := anOtherNodeOrNil ifNil: [ aNode ] ifNotNil: [ anOtherNodeOrNil ]. + ^ (codeGenerator methodNamed: aNode selector) ifNil: [ aNode asTransformedConstantPerform @@ -647,8 +690,10 @@ SLInlinerWithAnnotation >> isInlineableSend: aNode [ | m | codeGenerator maybeBreakForTestToInline: aNode in: self. aNode isSend ifFalse: [ ^ false ]. + m := codeGenerator methodNamed: aNode selector. "nil if builtin or external function" m ifNil: [ ^ false ]. + ^ m ~~ currentMethod and: [ m mayBeInlined and: [ (m isComplete and: [ @@ -756,7 +801,6 @@ SLInlinerWithAnnotation >> label: exitLabel for: aTMethod [ { #category : 'transformation' } SLInlinerWithAnnotation >> moveDownReturnsAndAssignmentsFor: aSendNode including: aTStatementList [ - self invert: aSendNode replacement: aTStatementList. sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode replacement: aTStatementList. @@ -796,21 +840,37 @@ SLInlinerWithAnnotation >> propagateReturnTypeDirectReturn: directReturn exitVar callee propagateReturnIn: codeGenerator ] ] -{ #category : 'inlining-support-condtional' } +{ #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ | replacementTree method | replacementTree := data at: #replacementTree. method := data at: #method. - replacementTree replaceNodesIn: (data at: #map). - replacementTree comment: - { ('inline ' , aSendNode receiver selector) }. + self replaceNodesIn: (data at: #map) for: replacementTree. + currentMethod addVarsDeclarationsAndLabelsOf: method except: method args ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> replaceNodesIn: aReplacementDictionary for: aNode [ + + | replacement | + aReplacementDictionary isEmpty ifTrue: [ ^ self ]. + aNode nodesDo: [ :node | + (aReplacementDictionary + at: node + ifPresent: [ true ] + ifAbsent: [ false ]) ifTrue: [ + replacement := (aReplacementDictionary at: node) copy. + + sLNodeAnnotatorVisitor + visitFromBranchStartingAt: node + replacement: replacement ] ] +] + { #category : 'transformation' } SLInlinerWithAnnotation >> replaceReturnWithGoToIfNeededIn: callee directReturn: directReturn [ @@ -837,13 +897,29 @@ SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aTMeth ifPresent: [ true ] ifAbsent: [ false ] ]) ifTrue: [ replacement := (aReplacementDictionary at: node name) copy. - node parent replaceChild: node with: replacement. + sLNodeAnnotatorVisitor visitFromBranchStartingAt: node replacement: replacement. sLNodeAnnotatorVisitor moveDownAssigningParentFrom: replacement ] ] ] +{ #category : 'inlining-preparation' } +SLInlinerWithAnnotation >> sendForInliningInCurrentMethod [ + + | sendsForInlining | + sendsForInlining := OrderedCollection new: 10. + currentMethod parseTree + nodesDo: [ :node | + (self isInlineableFunctionCall: node) ifTrue: [ + sendsForInlining add: node ] ] + unless: [ :node | "Don't inline the arguments to asserts to keep the asserts readable" + node isSend and: [ + node selector == #cCode:inSmalltalk: or: [ + codeGenerator isAssertSelector: node selector ] ] ]. + ^ sendsForInlining +] + { #category : 'inlining-preparation' } SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. @@ -957,23 +1033,16 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ "Expand any (complete) inline methods sent by this method as receivers or parameters. Answer if anything was inlined." - | sendsToInline | - sendsToInline := Dictionary new: 100. + | sendsForInlining didSomething | + didSomething := false. + sendsForInlining := self sendForInliningInCurrentMethod. + codeGenerator pushScope: currentMethod while: [ - currentMethod parseTree - nodesDo: [ :node | - (self isInlineableFunctionCall: node) ifTrue: [ - (self inlineFunctionCall: node) ifNotNil: [ :replacement | - sendsToInline at: node put: replacement ] ] ] - unless: [ :node | "Don't inline the arguments to asserts to keep the asserts readable" - node isSend and: [ - node selector == #cCode:inSmalltalk: or: [ - codeGenerator isAssertSelector: node selector ] ] ] ]. - - sendsToInline isEmpty ifTrue: [ ^ false ]. - currentMethod replaceNodesIn: sendsToInline. - ^ true + sendsForInlining do: [ :node | + (self inlineFunctionCall: node) ifNotNil: [ didSomething := true ] ] ]. + + ^ didSomething ] { #category : 'inlining' } @@ -1007,9 +1076,8 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ self ensureConditionalAssignmentsAreTransformedInCurrentMethod. self annotateCurrentMethod. didSomething := self tryToInlineMethodStatementsListsInCurrentMethod. - self tryToInlineMethodExpressionsInCurrentMethod ifTrue: [ - self annotateCurrentMethod. - didSomething := true ]. + didSomething := self tryToInlineMethodExpressionsInCurrentMethod or: [ + didSomething ]. didSomething ifTrue: [ currentMethod writtenToGlobalVarsCache: nil ]. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 8b3a195f45d..3e1c4ba8548 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -518,10 +518,6 @@ SLNodeAnnotatorVisitor >> moveDownAssigningParentFrom: aNode [ self moveDownAssignmentFrom: assignment assignment: assignment. - assignment parent - replaceChild: assignment - with: assignment expression. - self visitFromBranchStartingAt: assignment replacement: assignment expression @@ -574,13 +570,7 @@ SLNodeAnnotatorVisitor >> moveDownReturningParentFrom: aNode [ returningParent := self returningParentFor: aNode. returningParent ifNil: [ ^ self ]. - self - moveDownReturnFrom: returningParent - return: returningParent. - - returningParent parent - replaceChild: returningParent - with: returningParent expression. + self moveDownReturnFrom: returningParent return: returningParent. self visitFromBranchStartingAt: returningParent @@ -954,6 +944,8 @@ SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode [ SLNodeAnnotatorVisitor >> visitFromBranchStartingAt: aNode replacement: aReplacementNode [ | restoredStacks | + aNode parent replaceChild: aNode with: aReplacementNode. + self cleanBranchInfoFrom: aNode. restoredStacks := self restoreStackStateFrom: aNode. self cleanInfoFrom: aNode. diff --git a/smalltalksrc/Slang/TVariableNode.class.st b/smalltalksrc/Slang/TVariableNode.class.st index 7ed69453894..31ed6c2e4db 100644 --- a/smalltalksrc/Slang/TVariableNode.class.st +++ b/smalltalksrc/Slang/TVariableNode.class.st @@ -130,6 +130,7 @@ TVariableNode >> renameLocalVariablesGivenClassVariables: classVariables globalV "Answer either the receiver, if it is not a reference to one of the given variables, or the replacement if it is. classVariables is a Dictionary and globalsVariables is a collection" "'self', 'self_in...', 'super' and 'cascade are reserved variable names" + (name = #super or: [ (name beginsWith: #self) or: [ name beginsWith: #cascade ] ]) ifTrue: [ ^ self ]. @@ -145,7 +146,7 @@ TVariableNode >> renameLocalVariablesGivenClassVariables: classVariables globalV classVariables at: name ifPresent: [ :replacement | - replacement shouldBeGenerated ifTrue: [ ^ replacement ]. + replacement shouldBeGenerated ifTrue: [ ^ replacement copy ]. "in case of library name don't redefine them" ^ self ] ifAbsent: [ "the variable is local, we update its name with an _ and add it to the changedVariables collection which will change all its appearance in the TMethods/parseTree " From 747383f24fe8b5a81a56e92034ccc776983a55c6 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 5 Aug 2025 16:06:30 +0200 Subject: [PATCH 69/86] supress dead code --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index d427cce2317..971349304a6 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -105,7 +105,7 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ in: codeGenerator) ifTrue: [ (self isInlineableFunctionCall: node) ifTrue: [ - aTMethod complete: false. "more inlining to do" + "more inlining to do" ^ self ] ] ifFalse: [ foundIncompleteSend := true ] ] ] unless: [ :node | From 1c7e6ec0cb4b876dabc95d170739bdc2883e79c9 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 5 Aug 2025 18:21:24 +0200 Subject: [PATCH 70/86] refactoring --- .../SLMockInliningTestClass.class.st | 12 +- .../Slang/SLInlinerWithAnnotation.class.st | 153 +++++++++--------- .../Slang/SLNodeAnnotatorVisitor.class.st | 1 - 3 files changed, 85 insertions(+), 81 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 1de92af2a63..2e07d73133d 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -446,13 +446,13 @@ SLMockInliningTestClass >> methodWithAssignmentOnWhileTrueInlined [ { #category : 'collect-statements-for-inlining' } SLMockInliningTestClass >> methodWithAvoidedSelectors [ - self cCode: [ ] inSmalltalk: [ ]. + self cCode: [ self methodB ] inSmalltalk: [ self methodB ]. self cCall: 'method'. - self cCall: 'method' withArguments: { 1. 2. 3 }. - self cppIf: ['cppCond'. true] ifTrue: [ 'in cppIfTrue'. false ] ifFalse: [ 'in cppIfFalse'. true ]. - [ 'and receiver'. true ] and: [ 'and argument'. false ]. - [ 'or receiver' . false ] or: [ 'or argument'. true ]. - [ 'ifTrue receiver'. false ] ifTrue: ['ifTrue argument' . true ] + self cCall: 'method' withArguments: { self methodB. 2. 3 }. + self cppIf: ['cppCond'. self methodB] ifTrue: [ 'in cppIfTrue'. false ] ifFalse: [ 'in cppIfFalse'. self methodB ]. + [ 'and receiver'. self methodB ] and: [ 'and argument'. false ]. + [ 'or receiver' . false ] or: [ 'or argument'. self methodB ]. + [ 'ifTrue receiver'. self methodB ] ifTrue: ['ifTrue argument' . self methodB ] ] { #category : 'inlining-block-helpers' } diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 971349304a6..c61470a8258 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -93,10 +93,12 @@ SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ "Set the complete flag if the parse tree contains no further candidates for inlining." - | foundIncompleteSend | + | foundIncompleteSend stmtsForInlining | codeGenerator maybeBreakForTestOfInliningOf: aTMethod selector. - + stmtsForInlining := self statementsListsForInliningInCurrentMethod. foundIncompleteSend := false. + + aTMethod parseTree nodesDo: [ :node | node isSend ifTrue: [ @@ -104,8 +106,7 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ methodIsEffectivelyComplete: node selector in: codeGenerator) ifTrue: [ - (self isInlineableFunctionCall: node) ifTrue: [ - "more inlining to do" + (self isInlineableFunctionCall: node) ifTrue: [ "more inlining to do" ^ self ] ] ifFalse: [ foundIncompleteSend := true ] ] ] unless: [ :node | @@ -174,6 +175,24 @@ SLInlinerWithAnnotation >> ensureConditionalAssignmentsAreTransformedInCurrentMe true ] ] whileTrue ] +{ #category : 'inlining-preparation' } +SLInlinerWithAnnotation >> filterStmtForInliningIn: tStmtListNodeLists [ + + | stmtsList | + stmtsList := OrderedCollection new. + tStmtListNodeLists do: [ :tStmtList | + tStmtList statements do: [ :stmt | + | expression | + expression := stmt. + (stmt isReturn or: [ stmt isAssignment ]) ifTrue: [ + expression := stmt expression ]. + + (self isInlineableSend: expression) ifTrue: [ + stmtsList add: expression ] ] ]. + + ^ stmtsList +] + { #category : 'initialization' } SLInlinerWithAnnotation >> initialize [ @@ -204,26 +223,6 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ ^ nil ] -{ #category : 'inlining-support-statement' } -SLInlinerWithAnnotation >> inlineCodeOrNilForStatement: aNode [ - "If the given statement node can be inlined, answer if anything was done." - - (aNode isReturn and: [ self isInlineableSend: aNode expression ]) - ifTrue: [ - self inlineSend: aNode expression. - ^ true ]. - - (aNode isAssignment and: [ self isInlineableSend: aNode expression ]) - ifTrue: [ - self inlineSend: aNode expression. - ^ true ]. - - (aNode isSend and: [ self isInlineableSend: aNode ]) ifTrue: [ - self inlineSend: aNode. - ^ true ]. - ^ false -] - { #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> inlineConditional: aSendNode [ "If possible answer the inlining of a conditional, otherwise answer nil. @@ -567,8 +566,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ isUsedExpression ifFalse: [ inlineStmts nodesDo: [ :node | - node isGoTo ifTrue: [ node metInInlining ] ] ]. - ^ inlineStmts + node isGoTo ifTrue: [ node metInInlining ] ] ] ] { #category : 'inlining-decision' } @@ -840,6 +838,48 @@ SLInlinerWithAnnotation >> propagateReturnTypeDirectReturn: directReturn exitVar callee propagateReturnIn: codeGenerator ] ] +{ #category : 'inlining-preparation' } +SLInlinerWithAnnotation >> removeNonSupportedStmtForInliningIn: stmtLists [ + + currentMethod parseTree nodesDo: [ :node | + node isSend ifTrue: [ + node selector = #cCode:inSmalltalk: ifTrue: [ + node nodesDo: [ :ccisNode | + stmtLists remove: ccisNode ifAbsent: [ ] ] ]. + node selector = #cCall: ifTrue: [ + node nodesDo: [ :ccisNode | + stmtLists remove: ccisNode ifAbsent: [ ] ] ]. + node selector = #cCall:withArguments: ifTrue: [ + node nodesDo: [ :ccisNode | + stmtLists remove: ccisNode ifAbsent: [ ] ] ]. + (#( #cppIf:ifTrue:ifFalse: #cppIf:ifTrue: ) includes: node selector) + ifTrue: [ + node arguments first nodesDo: [ :inCondNode | + stmtLists remove: inCondNode ifAbsent: [ ] ] ]. + (#( #and: #or: ) includes: node selector) ifTrue: [ "Note: the PP 2.3 compiler produces two arg nodes for these selectors" + stmtLists remove: node arguments first ifAbsent: [ ]. + stmtLists remove: node arguments last ifAbsent: [ ] ]. + (#( #ifTrue: #ifFalse: #ifTrue:ifFalse: #ifFalse:ifTrue: + #ifNil: #ifNotNil: #ifNil:ifNotNil: #ifNotNil:ifNil: ) + includes: node selector) ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ] ]. + (#( whileTrue whileTrue: whilefalse whileFalse: ) includes: + node selector) ifTrue: [ "Allow inlining if it is a [...] whileTrue/whileFalse. + This is identified by having more than one statement in the + receiver block in which case the C code wouldn't work anyways" + node receiver sizeWithoutComments = 1 ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ] ] ]. + node selector = #to:do: ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ]. + stmtLists remove: node args first ifAbsent: [ ] ]. + node selector = #to:by:do: ifTrue: [ + stmtLists remove: node receiver ifAbsent: [ ]. + stmtLists remove: node arguments first ifAbsent: [ ]. + stmtLists remove: node arguments second ifAbsent: [ ] ] ]. + node isCaseStmt ifTrue: [ "don't inline cases" + node cases do: [ :case | stmtLists remove: case ifAbsent: [ ] ] ] ] +] + { #category : 'inlining-support-conditional' } SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ @@ -928,56 +968,22 @@ SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ proper block for the cCode: argument are inlined in MessageNode>>asTranslatorNodeIn:). We do not want to inline code within assert: sends (because we want the assert to read nicely)." - | stmtLists | - stmtLists := OrderedCollection new: 10. + | tStmtListNodeLists | + tStmtListNodeLists := OrderedCollection new: 10. currentMethod parseTree nodesDo: [ :node | - node isStatementList ifTrue: [ stmtLists add: node ] ] + node isStatementList ifTrue: [ tStmtListNodeLists add: node ] ] unless: [ :node | node isSend and: [ node selector == #cCode:inSmalltalk: or: [ node selector == #cCall: or: [ node selector == #cCall:withArguments: or: [ codeGenerator isAssertSelector: node selector ] ] ] ] ]. - currentMethod parseTree nodesDo: [ :node | - node isSend ifTrue: [ - node selector = #cCode:inSmalltalk: ifTrue: [ - node nodesDo: [ :ccisNode | - stmtLists remove: ccisNode ifAbsent: [ ] ] ]. - node selector = #cCall: ifTrue: [ - node nodesDo: [ :ccisNode | - stmtLists remove: ccisNode ifAbsent: [ ] ] ]. - node selector = #cCall:withArguments: ifTrue: [ - node nodesDo: [ :ccisNode | - stmtLists remove: ccisNode ifAbsent: [ ] ] ]. - (#( #cppIf:ifTrue:ifFalse: #cppIf:ifTrue: ) includes: node selector) - ifTrue: [ - node arguments first nodesDo: [ :inCondNode | - stmtLists remove: inCondNode ifAbsent: [ ] ] ]. - (#( #and: #or: ) includes: node selector) ifTrue: [ "Note: the PP 2.3 compiler produces two arg nodes for these selectors" - stmtLists remove: node arguments first ifAbsent: [ ]. - stmtLists remove: node arguments last ifAbsent: [ ] ]. - (#( #ifTrue: #ifFalse: #ifTrue:ifFalse: #ifFalse:ifTrue: - #ifNil: #ifNotNil: #ifNil:ifNotNil: #ifNotNil:ifNil: ) - includes: node selector) ifTrue: [ - stmtLists remove: node receiver ifAbsent: [ ] ]. - (#( whileTrue whileTrue: whilefalse whileFalse: ) includes: - node selector) ifTrue: [ "Allow inlining if it is a [...] whileTrue/whileFalse. - This is identified by having more than one statement in the - receiver block in which case the C code wouldn't work anyways" - node receiver sizeWithoutComments = 1 ifTrue: [ - stmtLists remove: node receiver ifAbsent: [ ] ] ]. - node selector = #to:do: ifTrue: [ - stmtLists remove: node receiver ifAbsent: [ ]. - stmtLists remove: node args first ifAbsent: [ ] ]. - node selector = #to:by:do: ifTrue: [ - stmtLists remove: node receiver ifAbsent: [ ]. - stmtLists remove: node arguments first ifAbsent: [ ]. - stmtLists remove: node arguments second ifAbsent: [ ] ] ]. - node isCaseStmt ifTrue: [ "don't inline cases" - node cases do: [ :case | stmtLists remove: case ifAbsent: [ ] ] ] ]. - ^ stmtLists + + self removeNonSupportedStmtForInliningIn: tStmtListNodeLists. + + ^ self filterStmtForInliningIn: tStmtListNodeLists ] { #category : 'inlining-preparation' } @@ -1050,16 +1056,13 @@ SLInlinerWithAnnotation >> tryToInlineMethodStatementsListsInCurrentMethod [ "Expand any (complete) inline methods sent by this method as top-level statements. Answer if anything was inlined." - | stmtLists didSomething | - didSomething := false. + | stmtLists | stmtLists := self statementsListsForInliningInCurrentMethod. + stmtLists isEmpty ifTrue: [ ^ false ]. - stmtLists do: [ :stmtList | - stmtList statements do: [ :stmt | - (self inlineCodeOrNilForStatement: stmt) ifTrue: [ - didSomething := true ] ] ]. + stmtLists do: [ :stmt | self inlineSend: stmt ]. - ^ didSomething + ^ true ] { #category : 'inlining' } @@ -1081,6 +1084,8 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ didSomething ifTrue: [ currentMethod writtenToGlobalVarsCache: nil ]. + "1 haltIf: [ didSomething and: [ currentMethod isComplete ] ]." + "marking a method complete is progress" currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 3e1c4ba8548..e133916a4b1 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -279,7 +279,6 @@ SLNodeAnnotatorVisitor >> handleConditionalSend: aSendNode [ self visitCppConditional: aSendNode. ^ true ]. - ^ false ] From c349a85428e4f8441fcfc09afb3b9b0cd9dacaee Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 6 Aug 2025 11:15:11 +0200 Subject: [PATCH 71/86] remove redundant operation --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index c61470a8258..1b993d87d48 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -203,7 +203,7 @@ SLInlinerWithAnnotation >> initialize [ { #category : 'inlining-support-function-call' } SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ - | sel meth inlinedReplacement | + | sel meth | (aSendNode selector beginsWith: 'perform:') ifTrue: [ ^ self inlineFunctionCall: aSendNode asTransformedConstantPerform @@ -212,12 +212,7 @@ SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ sel := aSendNode receiver selector. meth := codeGenerator methodNamed: sel. (meth notNil and: [ meth inline == true ]) ifFalse: [ ^ nil ]. - (self isFunctional: meth) ifTrue: [ - inlinedReplacement := self inlineFunctionCall: aSendNode receiver. - ^ TSendNode new - setSelector: aSendNode selector - receiver: inlinedReplacement - arguments: aSendNode arguments copy ]. + (self isInlineableConditional: aSendNode) ifTrue: [ ^ self inlineConditional: aSendNode ]. ^ nil From 1318243afa303333bea3a3c46bd812c7a432351e Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 8 Aug 2025 15:09:24 +0200 Subject: [PATCH 72/86] refactoring + optimize inlining by using isComplete --- .../Slang-Tests/SLInliningTest.class.st | 2 +- .../Slang/SLInlinerWithAnnotation.class.st | 318 +++++++++++++----- 2 files changed, 227 insertions(+), 93 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 42a584e9c36..afe110951d8 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -16,7 +16,7 @@ SLInliningTest class >> testParameters [ ^ ParametrizedTestMatrix new forSelector: #inliningStrategy addOptions: - { "#useOldInlining. #useExtractedInlining." #useNewInlining }; + { "#useExtractedInlining." #useNewInlining }; yourself ] diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 1b993d87d48..9821dcb998d 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -3,7 +3,10 @@ Class { #superclass : 'SLAbstractInlineStrategy', #instVars : [ 'currentMethod', - 'sLNodeAnnotatorVisitor' + 'sLNodeAnnotatorVisitor', + 'sendsForInlining', + 'conditionalForInlining', + 'performSendForInlining' ], #category : 'Slang', #package : 'Slang' @@ -25,7 +28,43 @@ SLInlinerWithAnnotation >> addBeginAndEndCommentsFor: aTStatementList selector: (TLabeledCommentNode new setComment: 'end ' , aSelector) ] ] -{ #category : 'inlining-preparation' } +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> addToConditionalInlining: aSendNode [ + + | indexesToRemove | + (self isInlineableConditional: aSendNode) ifFalse: [ ^ self ]. + + indexesToRemove := OrderedCollection new. + sendsForInlining doWithIndex: [ :dictionary :index | + (dictionary values includes: aSendNode receiver) ifTrue: [ + indexesToRemove add: index ] ]. + + sendsForInlining := sendsForInlining reject: [ :dictionary | + dictionary values includes: aSendNode receiver ]. + + sendsForInlining add: (IdentityDictionary new + at: self conditionalInliningString put: aSendNode; + yourself) +] + +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> addToPerformSendlInlining: aSendNode from: anOtherSendNode [ + + sendsForInlining add: (IdentityDictionary new + at: self functionCallFromBuitinInliningString + put: anOtherSendNode -> aSendNode; + yourself) +] + +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> addToSendlInlining: aSendNode [ + + sendsForInlining add: (IdentityDictionary new + at: self functionCallInliningString put: aSendNode; + yourself) +] + +{ #category : 'preparation' } SLInlinerWithAnnotation >> annotateCurrentMethod [ sLNodeAnnotatorVisitor cleanInfo. @@ -76,7 +115,13 @@ SLInlinerWithAnnotation >> argAssignmentsFor: meth send: aSendNode except: elide ^ substitutionDict ] -{ #category : 'inlining-decision' } +{ #category : 'preparation-function-call-string' } +SLInlinerWithAnnotation >> builtinString [ + + ^ #builtinString +] + +{ #category : 'testing-node' } SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod [ "for now" @@ -89,26 +134,26 @@ SLInlinerWithAnnotation >> canBeInlineInExpression: aNode replacement: aTMethod visit: parseTree ] -{ #category : 'testing' } +{ #category : 'testing-method' } SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ "Set the complete flag if the parse tree contains no further candidates for inlining." | foundIncompleteSend stmtsForInlining | codeGenerator maybeBreakForTestOfInliningOf: aTMethod selector. stmtsForInlining := self statementsListsForInliningInCurrentMethod. + self fillSendForInliningInCurrentMethod. + (sendsForInlining isNotEmpty or: [ stmtsForInlining isNotEmpty ]) + ifTrue: [ ^ self ]. foundIncompleteSend := false. aTMethod parseTree nodesDo: [ :node | - node isSend ifTrue: [ - (aTMethod - methodIsEffectivelyComplete: node selector - in: codeGenerator) - ifTrue: [ - (self isInlineableFunctionCall: node) ifTrue: [ "more inlining to do" - ^ self ] ] - ifFalse: [ foundIncompleteSend := true ] ] ] + (node isSend and: [ node selector ~= currentMethod selector ]) + ifTrue: [ + (aTMethod + methodIsEffectivelyComplete: node selector + in: codeGenerator) ifFalse: [ foundIncompleteSend := true ] ] ] unless: [ :node | node isSend and: [ node selector == #cCode:inSmalltalk: or: [ @@ -116,7 +161,7 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ foundIncompleteSend ifFalse: [ aTMethod complete: true ] ] -{ #category : 'testing' } +{ #category : 'testing-method' } SLInlinerWithAnnotation >> checkForFlagIn: aTStatementList [ (aTStatementList sizeWithoutComments > 1 and: [ @@ -131,6 +176,12 @@ SLInlinerWithAnnotation >> codeGenerator [ ^ codeGenerator ] +{ #category : 'preparation-function-call-string' } +SLInlinerWithAnnotation >> conditionalInliningString [ + + ^ #conditionalInliningString +] + { #category : 'accessing' } SLInlinerWithAnnotation >> currentMethod [ @@ -150,7 +201,7 @@ SLInlinerWithAnnotation >> doInliningIn: aTMethod [ ^ self tryToInlineMethodsInCurrentMethod ] -{ #category : 'inlining-preparation' } +{ #category : 'preparation' } SLInlinerWithAnnotation >> ensureConditionalAssignmentsAreTransformedInCurrentMethod [ "Make passes transforming foo := expr ifTrue: [a] ifFalse: [b] @@ -175,7 +226,90 @@ SLInlinerWithAnnotation >> ensureConditionalAssignmentsAreTransformedInCurrentMe true ] ] whileTrue ] -{ #category : 'inlining-preparation' } +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> fillConditionalSendOrTryPerformSearchForInlining: aNode transformedConstantFrom: originalNodeForVisitorInfo [ + + aNode asTransformedConstantPerform + ifNil: [ self addToConditionalInlining: aNode ] + ifNotNil: [ :node | "when going through transformation, the new node n take the place of aNode but not in the visitor so we keep track of the old node " + self + fillSendForInlining: node + transformedConstantFrom: originalNodeForVisitorInfo ] +] + +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> fillSendForInlining: aNode [ + + ^ self fillSendForInlining: aNode transformedConstantFrom: nil +] + +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> fillSendForInlining: aNode transformedConstantFrom: anOtherNodeOrNil [ + "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted. + if a method is marked as always inlined and complete, the method also returns true" + + | originalNodeForVisitorInfo tMethod | + codeGenerator maybeBreakForTestToInline: aNode in: self. + aNode isSend ifFalse: [ ^ false ]. + + originalNodeForVisitorInfo := anOtherNodeOrNil + ifNil: [ aNode ] + ifNotNil: [ anOtherNodeOrNil ]. + tMethod := codeGenerator methodNamed: aNode selector. + tMethod ifNil: [ + self + fillConditionalSendOrTryPerformSearchForInlining: aNode + transformedConstantFrom: originalNodeForVisitorInfo. + ^ self ]. + self + fillSendOrPerformSendForInlining: aNode + method: tMethod + transformedConstantFrom: originalNodeForVisitorInfo +] + +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> fillSendForInliningInCurrentMethod [ + + sendsForInlining := OrderedCollection new. + + currentMethod parseTree + nodesDo: [ :node | self fillSendForInlining: node ] + unless: [ :node | "Don't inline the arguments to asserts to keep the asserts readable" + node isSend and: [ + node selector == #cCode:inSmalltalk: or: [ + codeGenerator isAssertSelector: node selector ] ] ] +] + +{ #category : 'preparation-function-call' } +SLInlinerWithAnnotation >> fillSendOrPerformSendForInlining: aNode method: aTMethod transformedConstantFrom: originalNodeForVisitorInfo [ + + | isInlineableSend | + isInlineableSend := (aTMethod ~~ currentMethod and: [ + ((self isFunctional: aTMethod) or: [ + aTMethod mustBeInlined and: [ + aTMethod isComplete ] ]) and: [ + aTMethod mayBeInlined and: [ + (codeGenerator mayInline: aTMethod selector) + and: [ + (aNode arguments allSatisfy: [ :a | + self + isSubstitutableNode: a + intoMethod: aTMethod ]) and: [ + self + canBeInlineInExpression: + originalNodeForVisitorInfo + replacement: aTMethod ] ] ] ] ]) or: [ + aTMethod checkForRequiredInlinability ]. + isInlineableSend ifFalse: [ ^ self ]. + aNode == originalNodeForVisitorInfo + ifTrue: [ self addToSendlInlining: aNode ] + ifFalse: [ + self + addToPerformSendlInlining: aNode + from: originalNodeForVisitorInfo ] +] + +{ #category : 'preparation-statement' } SLInlinerWithAnnotation >> filterStmtForInliningIn: tStmtListNodeLists [ | stmtsList | @@ -193,6 +327,18 @@ SLInlinerWithAnnotation >> filterStmtForInliningIn: tStmtListNodeLists [ ^ stmtsList ] +{ #category : 'preparation-function-call-string' } +SLInlinerWithAnnotation >> functionCallFromBuitinInliningString [ + + ^ #functionCallFromBuitinInliningString +] + +{ #category : 'preparation-function-call-string' } +SLInlinerWithAnnotation >> functionCallInliningString [ + + ^ #functionCallInliningString +] + { #category : 'initialization' } SLInlinerWithAnnotation >> initialize [ @@ -200,25 +346,7 @@ SLInlinerWithAnnotation >> initialize [ sLNodeAnnotatorVisitor := SLNodeAnnotatorVisitor new ] -{ #category : 'inlining-support-function-call' } -SLInlinerWithAnnotation >> inlineBuiltin: aSendNode [ - - | sel meth | - (aSendNode selector beginsWith: 'perform:') ifTrue: [ - ^ self - inlineFunctionCall: aSendNode asTransformedConstantPerform - fromBuiltin: aSendNode ]. - aSendNode receiver isSend ifFalse: [ ^ nil ]. - sel := aSendNode receiver selector. - meth := codeGenerator methodNamed: sel. - (meth notNil and: [ meth inline == true ]) ifFalse: [ ^ nil ]. - - (self isInlineableConditional: aSendNode) ifTrue: [ - ^ self inlineConditional: aSendNode ]. - ^ nil -] - -{ #category : 'inlining-support-conditional' } +{ #category : 'conditional' } SLInlinerWithAnnotation >> inlineConditional: aSendNode [ "If possible answer the inlining of a conditional, otherwise answer nil. Currently the only pattern we support is @@ -233,7 +361,7 @@ SLInlinerWithAnnotation >> inlineConditional: aSendNode [ ifFalse: [ self inlineGuardingConditional: aSendNode ] ] -{ #category : 'inlining-support-conditional' } +{ #category : 'conditional' } SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: isGuarding [ "init data for inlining conditional" @@ -262,7 +390,7 @@ SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode ^ data ] -{ #category : 'inlining-support-function-call' } +{ #category : 'function-call' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ ^ self @@ -271,7 +399,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode [ fromBuiltin: nil ] -{ #category : 'inlining-support-function-call' } +{ #category : 'function-call' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode fromBuiltin: aNOdeOrNil [ ^ self @@ -280,7 +408,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode fromBuiltin: aNOdeOrNil fromBuiltin: aNOdeOrNil ] -{ #category : 'inlining-support-function-call' } +{ #category : 'function-call' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional: aBoolean [ ^ self @@ -289,7 +417,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional fromBuiltin: nil ] -{ #category : 'inlining-support-function-call' } +{ #category : 'function-call' } SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional: aBoolean fromBuiltin: aNodeOrNil [ "Answer the body of the called function, substituting the actual parameters for the formal argument variables in the method body. @@ -300,7 +428,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional | selector meth parametersToReplaceByArgument argsForInlining substitutionDict parseTree | selector := aSendNode selector. meth := (codeGenerator methodNamed: selector) copy. - meth ifNil: [ ^ self inlineBuiltin: aSendNode ]. + parseTree := meth parseTree. parametersToReplaceByArgument := Set withAll: currentMethod args. argsForInlining := aSendNode argumentsForInliningCodeGenerator: @@ -330,7 +458,7 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional aBoolean ifFalse: [ SLNodeAnnotatorVisitor copyWithoutReturn: meth ]. "same 'optimization' as before but with comments if possible" - parseTree children size = 1 + (parseTree children size = 1 and: [ aBoolean not ]) ifTrue: [ meth parseTree: parseTree last. parseTree := parseTree last ] @@ -347,15 +475,16 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional ^ parseTree ] -{ #category : 'inlining-support-conditional' } +{ #category : 'conditional' } SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ "Inline aSend ifTrue:/ifFalse: [statements] where aSend is inlineable and always answers booleans. We convert the boolean returns in aSend to jumps." - | data evaluateIfTrue replacementTree map lastNode evaluateLabel skipLabel selector inlineStmts | + | data evaluateIfTrue replacementTree map lastNode evaluateLabel skipLabel selector inlineStmts skipLabelStmt | selector := aSendNode receiver selector. + data := self inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: true. @@ -364,10 +493,9 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ skipLabel := data at: #returnReplacement. map := Dictionary new. - (replacementTree lastNonCommentStatement isReturn and: [ - replacementTree lastNonCommentStatement expression value - = evaluateIfTrue ]) ifTrue: [ - lastNode := replacementTree lastNonCommentStatement ]. + (replacementTree lastExpression allSatisfy: [ :node | + node isReturn and: [ node expression value = evaluateIfTrue ] ]) + ifTrue: [ lastNode := replacementTree lastExpression ]. replacementTree nodesDo: [ :node | | expr | @@ -378,7 +506,7 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ map at: node put: (expr value ~~ evaluateIfTrue ifTrue: [ TGoToNode label: skipLabel label ] ifFalse: [ - node == lastNode + (lastNode isNotNil and: [ lastNode includes: node ]) ifTrue: [ TLabeledCommentNode new setComment: 'end ' , selector , '; fall through' ] @@ -392,17 +520,30 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ data at: #map put: map. self replaceNodeIn: aSendNode with: data. + replacementTree isComment ifTrue: [ + replacementTree statements: { }. + skipLabel := nil ]. + (replacementTree sizeWithoutComments = 1 and: [ + replacementTree lastNonCommentStatement label = skipLabel label ]) + ifTrue: [ + replacementTree statements: { }. + aSendNode arguments first statements: { }. + skipLabel := nil ]. + + skipLabelStmt := skipLabel + ifNotNil: [ { skipLabel } ] + ifNil: [ { } ]. inlineStmts := TStatementListNode new setArguments: #( ) statements: (evaluateLabel ifNil: [ replacementTree statements , aSendNode arguments first statements - , { skipLabel } ] + , skipLabelStmt ] ifNotNil: [ replacementTree statements , { evaluateLabel } , aSendNode arguments first statements - , { skipLabel } ]). + , skipLabelStmt ]). sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode @@ -411,7 +552,7 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ ^ inlineStmts ] -{ #category : 'inlining-support-conditional' } +{ #category : 'conditional' } SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ "Inline aSend ifTrue:/ifFalse: [^expr] @@ -477,7 +618,7 @@ SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ ^ inlineStmts ] -{ #category : 'inlining-support-statement' } +{ #category : 'statement' } SLInlinerWithAnnotation >> inlineSend: aSendNode [ "Answer a collection of statements to replace the given send. directReturn indicates that the send is the expression in a return statement, so returns can be left in the @@ -564,7 +705,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ node isGoTo ifTrue: [ node metInInlining ] ] ] ] -{ #category : 'inlining-decision' } +{ #category : 'testing-node' } SLInlinerWithAnnotation >> isConditionalToBeTransformedForAssignment: aSend [ "Answer if a send is of the form e1 @@ -584,7 +725,7 @@ SLInlinerWithAnnotation >> isConditionalToBeTransformedForAssignment: aSend [ self isConditionalToBeTransformedForAssignment: stmt ] ] ] ] ] ] ] -{ #category : 'testing' } +{ #category : 'testing-method' } SLInlinerWithAnnotation >> isFunctional: aTMethod [ "Answer if the receiver is a functional method. That is, if it consists of a single return statement of an expression or an assert or flag followed by @@ -618,7 +759,7 @@ SLInlinerWithAnnotation >> isFunctional: aTMethod [ #'FILE *' ) includes: aTMethod returnType ] -{ #category : 'inlining-decision' } +{ #category : 'testing-node' } SLInlinerWithAnnotation >> isInlineableConditional: aSendNode [ "Answer if the given send node is of the form aSend [ifTrue:|ifFalse:] [statements] where the method for aSend is marked as inline and all returns within it answer booleans." @@ -632,17 +773,18 @@ SLInlinerWithAnnotation >> isInlineableConditional: aSendNode [ method parseTree lastNonCommentStatement isReturn and: [ method parseTree allSatisfy: [ :node | node isReturn not or: [ - node expression isConstant and: [ - #( true false ) includes: node expression value ] ] ] ] ] ] ] ] + node expression isDefine not and: [ + node expression isConstant and: [ + #( true false ) includes: node expression value ] ] ] ] ] ] ] ] ] ] -{ #category : 'inlining-decision' } +{ #category : 'testing-node' } SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode [ ^ self isInlineableFunctionCall: aNode transformedConstantFrom: nil ] -{ #category : 'inlining-decision' } +{ #category : 'testing-node' } SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFrom: anOtherNodeOrNil [ "Answer if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted. if a method is marked as always inlined and complete, the method also returns true" @@ -653,7 +795,6 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr nodeForVisitorInfo := anOtherNodeOrNil ifNil: [ aNode ] ifNotNil: [ anOtherNodeOrNil ]. - ^ (codeGenerator methodNamed: aNode selector) ifNil: [ aNode asTransformedConstantPerform @@ -664,7 +805,7 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr transformedConstantFrom: nodeForVisitorInfo ] ] ifNotNil: [ :m | (m ~~ currentMethod and: [ - ((self isFunctional: m) or: [ + ((m isFunctionalIn: codeGenerator) or: [ m mustBeInlined and: [ m isComplete ] ]) and: [ m mayBeInlined and: [ (codeGenerator mayInline: m selector) and: [ @@ -676,7 +817,7 @@ SLInlinerWithAnnotation >> isInlineableFunctionCall: aNode transformedConstantFr m checkForRequiredInlinability ] ] ] -{ #category : 'inlining-decision' } +{ #category : 'testing-node' } SLInlinerWithAnnotation >> isInlineableSend: aNode [ "Answer if the given expresssion node is a call to a method that can be inlined." @@ -695,7 +836,7 @@ SLInlinerWithAnnotation >> isInlineableSend: aNode [ m checkForRequiredInlinability ] ] ] ] -{ #category : 'inlining-decision' } +{ #category : 'testing-node' } SLInlinerWithAnnotation >> isSubstitutableNode: aNode intoMethod: targetMeth [ "Answer true if the given parameter node is either a constant, a local variable, or a formal parameter of the receiver. Such parameter nodes may be substituted directly into the body of the method during inlining. Note that global variables cannot be subsituted into methods with possible side effects (i.e., methods that may assign to global variables) because the inlined method might depend on having the value of the global variable captured when it is passed in as an argument." @@ -833,7 +974,7 @@ SLInlinerWithAnnotation >> propagateReturnTypeDirectReturn: directReturn exitVar callee propagateReturnIn: codeGenerator ] ] -{ #category : 'inlining-preparation' } +{ #category : 'preparation-statement' } SLInlinerWithAnnotation >> removeNonSupportedStmtForInliningIn: stmtLists [ currentMethod parseTree nodesDo: [ :node | @@ -875,7 +1016,7 @@ SLInlinerWithAnnotation >> removeNonSupportedStmtForInliningIn: stmtLists [ node cases do: [ :case | stmtLists remove: case ifAbsent: [ ] ] ] ] ] -{ #category : 'inlining-support-conditional' } +{ #category : 'conditional' } SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ | replacementTree method | @@ -939,23 +1080,7 @@ SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aTMeth sLNodeAnnotatorVisitor moveDownAssigningParentFrom: replacement ] ] ] -{ #category : 'inlining-preparation' } -SLInlinerWithAnnotation >> sendForInliningInCurrentMethod [ - - | sendsForInlining | - sendsForInlining := OrderedCollection new: 10. - currentMethod parseTree - nodesDo: [ :node | - (self isInlineableFunctionCall: node) ifTrue: [ - sendsForInlining add: node ] ] - unless: [ :node | "Don't inline the arguments to asserts to keep the asserts readable" - node isSend and: [ - node selector == #cCode:inSmalltalk: or: [ - codeGenerator isAssertSelector: node selector ] ] ]. - ^ sendsForInlining -] - -{ #category : 'inlining-preparation' } +{ #category : 'preparation-statement' } SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ "Answer a collection of statement list nodes that are candidates for inlining. Currently, we cannot inline into the argument blocks of and: and or: messages. @@ -981,7 +1106,7 @@ SLInlinerWithAnnotation >> statementsListsForInliningInCurrentMethod [ ^ self filterStmtForInliningIn: tStmtListNodeLists ] -{ #category : 'inlining-preparation' } +{ #category : 'preparation' } SLInlinerWithAnnotation >> transformConditionalAssignment: node [ "If possible answer the transformation of code of the form var := e1 @@ -1034,16 +1159,25 @@ SLInlinerWithAnnotation >> tryToInlineMethodExpressionsInCurrentMethod [ "Expand any (complete) inline methods sent by this method as receivers or parameters. Answer if anything was inlined." - | sendsForInlining didSomething | - didSomething := false. - sendsForInlining := self sendForInliningInCurrentMethod. - + self fillSendForInliningInCurrentMethod. + sendsForInlining isEmpty ifTrue: [ ^ false ]. codeGenerator pushScope: currentMethod while: [ - sendsForInlining do: [ :node | - (self inlineFunctionCall: node) ifNotNil: [ didSomething := true ] ] ]. + sendsForInlining do: [ :dictionary | + dictionary at: self functionCallInliningString ifPresent: [ + self inlineFunctionCall: + (dictionary at: self functionCallInliningString) ]. + dictionary + at: self functionCallFromBuitinInliningString + ifPresent: [ + | nodes | + nodes := dictionary at: self functionCallFromBuitinInliningString. + self inlineFunctionCall: nodes value fromBuiltin: nodes key ]. + dictionary at: self conditionalInliningString ifPresent: [ + self inlineConditional: + (dictionary at: self conditionalInliningString) ] ] ]. - ^ didSomething + ^ true ] { #category : 'inlining' } @@ -1071,6 +1205,8 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ currentMethod isComplete ifTrue: [ ^ false ]. ^ currentMethod complete: true ]. + currentMethod isComplete ifTrue: [ ^ false ]. + self ensureConditionalAssignmentsAreTransformedInCurrentMethod. self annotateCurrentMethod. didSomething := self tryToInlineMethodStatementsListsInCurrentMethod. @@ -1079,8 +1215,6 @@ SLInlinerWithAnnotation >> tryToInlineMethodsInCurrentMethod [ didSomething ifTrue: [ currentMethod writtenToGlobalVarsCache: nil ]. - "1 haltIf: [ didSomething and: [ currentMethod isComplete ] ]." - "marking a method complete is progress" currentMethod isComplete ifFalse: [ self checkForCompletenessFor: currentMethod. From fdff2b6f04446c25565de60ad3db9c9e34cbf216 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 8 Aug 2025 16:54:09 +0200 Subject: [PATCH 73/86] refactoring --- .../Slang/SLInlinerWithAnnotation.class.st | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 9821dcb998d..759c0ca51ed 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -377,9 +377,9 @@ SLInlinerWithAnnotation >> inlineConditionalGuardingOrConditionalInit: aSendNode at: #method put: (codeGenerator methodNamed: aSendNode receiver selector) copy. - data at: #replacementTree put: (self - inlineFunctionCall: aSendNode receiver - inInlineableConditional: true). + self + inlineFunctionCall: aSendNode receiver + inInlineableConditional: true. data at: #returnReplacement put: (isGuarding ifTrue: [ @@ -468,11 +468,13 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional selector: selector exitLabel: nil ]. + "visiting is done just after" + aBoolean ifTrue: [ + aSendNode parent replaceChild: aSendNode with: parseTree. + ^ self ]. sLNodeAnnotatorVisitor visitFromBranchStartingAt: (aNodeOrNil ifNil: [ aSendNode ]) - replacement: parseTree. - - ^ parseTree + replacement: parseTree ] { #category : 'conditional' } @@ -489,7 +491,7 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: true. evaluateIfTrue := data at: #isIfTrue. - replacementTree := data at: #replacementTree. + replacementTree := aSendNode receiver. skipLabel := data at: #returnReplacement. map := Dictionary new. @@ -547,9 +549,7 @@ SLInlinerWithAnnotation >> inlineGuardingConditional: aSendNode [ sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode - replacement: inlineStmts. - - ^ inlineStmts + replacement: inlineStmts ] { #category : 'conditional' } @@ -565,7 +565,7 @@ SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ inlineConditionalGuardingOrConditionalInit: aSendNode isGuarding: false. returnIfTrue := data at: #isIfTrue. - replacementTree := data at: #replacementTree. + replacementTree := aSendNode receiver. returnNode := data at: #returnReplacement. map := Dictionary new. @@ -613,9 +613,7 @@ SLInlinerWithAnnotation >> inlineReturningConditional: aSendNode [ sLNodeAnnotatorVisitor visitFromBranchStartingAt: aSendNode - replacement: inlineStmts. - - ^ inlineStmts + replacement: inlineStmts ] { #category : 'statement' } @@ -1019,11 +1017,10 @@ SLInlinerWithAnnotation >> removeNonSupportedStmtForInliningIn: stmtLists [ { #category : 'conditional' } SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ - | replacementTree method | - replacementTree := data at: #replacementTree. + | method | method := data at: #method. - self replaceNodesIn: (data at: #map) for: replacementTree. + self replaceNodesIn: (data at: #map) for: aSendNode receiver. currentMethod addVarsDeclarationsAndLabelsOf: method @@ -1041,10 +1038,7 @@ SLInlinerWithAnnotation >> replaceNodesIn: aReplacementDictionary for: aNode [ ifPresent: [ true ] ifAbsent: [ false ]) ifTrue: [ replacement := (aReplacementDictionary at: node) copy. - - sLNodeAnnotatorVisitor - visitFromBranchStartingAt: node - replacement: replacement ] ] + node parent replaceChild: node with: replacement ] ] ] { #category : 'transformation' } From 6fb40abdbd99d69d896efeeafd57ffcb8f7a2dcc Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 8 Aug 2025 17:39:22 +0200 Subject: [PATCH 74/86] fix broken test --- .../Slang-Tests/SLInliningTest.class.st | 63 ++++++++++++++----- .../SLMockInliningTestClass.class.st | 16 +++-- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index afe110951d8..fa5ac9a7820 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -431,21 +431,6 @@ methodASimpleReturn(void) }' ] -{ #category : 'collect-statements-for-inlining' } -SLInliningTest >> testCollectStatementsForInliningInMethodWithAvoidedSelector [ - "this method collect statements list for inlining, some specific selectors have special comportment regarding inlining strategy like avoiding inlining he receiver or the some arguments" - - | method statementsForInlining | - method := self addMethodAndMethodCalledBy: - #methodWithAvoidedSelectors. - sLInliner currentMethod: method. - sLInliner codeGenerator: ccg. - - statementsForInlining := sLInliner - statementsListsForInliningInCurrentMethod. - self assert: statementsForInlining size equals: 6 -] - { #category : 'inlining-simple' } SLInliningTest >> testInlineInSwitchRemovesReturnStatement [ @@ -1163,6 +1148,54 @@ methodWithAssignmentOnWhileTrueInlined(void) }' ] +{ #category : 'collect-statements-for-inlining' } +SLInliningTest >> testMethodWithAvoidedSelectors [ + "this method collect statements list for inlining, some specific selectors have special comportment regarding inlining strategy like avoiding inlining he receiver or the some arguments" + + | method translation | + method := self addMethodAndMethodCalledBy: + #methodWithAvoidedSelectors. + self doInliningIn: method. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: '/* SLMockInliningTestClass>>#methodWithAvoidedSelectors */ +static sqInt +methodWithAvoidedSelectors(void) +{ + /* begin methodB */ + 2 + 2; + /* end methodB */ + method(); + method(methodB(), 2, 3); + +# if methodB() + { + /* begin methodB */ + 2 + 2; + /* end methodB */ + } +# else /* methodB() */ + { + /* begin methodB */ + 2 + 2; + /* end methodB */ + } +# endif /* methodB() */ + ((/* begin methodB */ 2 + 2 /* end methodB */)) && 0; + 0 || (methodB()); + if (methodB()) { + /* begin methodB */ + 2 + 2; + /* end methodB */ + } + return 0; +}' +] + { #category : 'inlining-block' } SLInliningTest >> testMethodWithBlockArgumentInlined [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 2e07d73133d..3e76c0a9569 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -448,11 +448,17 @@ SLMockInliningTestClass >> methodWithAvoidedSelectors [ self cCode: [ self methodB ] inSmalltalk: [ self methodB ]. self cCall: 'method'. - self cCall: 'method' withArguments: { self methodB. 2. 3 }. - self cppIf: ['cppCond'. self methodB] ifTrue: [ 'in cppIfTrue'. false ] ifFalse: [ 'in cppIfFalse'. self methodB ]. - [ 'and receiver'. self methodB ] and: [ 'and argument'. false ]. - [ 'or receiver' . false ] or: [ 'or argument'. self methodB ]. - [ 'ifTrue receiver'. self methodB ] ifTrue: ['ifTrue argument' . self methodB ] + self cCall: 'method' withArguments: { + self methodB. + 2. + 3 }. + self + cppIf: [ self methodB ] + ifTrue: [ self methodB ] + ifFalse: [ self methodB ]. + [ self methodB ] and: [ false ]. + [ false ] or: [ self methodB ]. + [ self methodB ] ifTrue: [ self methodB ] ] { #category : 'inlining-block-helpers' } From e64f0ff466539b3ee1d4461793ed3544e12597e1 Mon Sep 17 00:00:00 2001 From: renaud Date: Fri, 8 Aug 2025 17:55:26 +0200 Subject: [PATCH 75/86] rename goTo methods --- .../Slang/SLInlinerWithAnnotation.class.st | 2 +- .../Slang/SLNodeAnnotatorVisitor.class.st | 2 +- smalltalksrc/Slang/TGoToNode.class.st | 20 +++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 759c0ca51ed..5c608c8cef1 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -700,7 +700,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ isUsedExpression ifFalse: [ inlineStmts nodesDo: [ :node | - node isGoTo ifTrue: [ node metInInlining ] ] ] + node isGoTo ifTrue: [ node indicateAnExitPointInlined ] ] ] ] { #category : 'testing-node' } diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index e133916a4b1..81ee3c6f329 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -965,7 +965,7 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ 'a := [... methodWithReturn … b]' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... c . goTo l ... l ... a := b]' 'a := methodeWithMultipleReturn' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... a := c . goTo l ... l ... a := b]' so after an inlining phases we invalidate goTo generated so that they do not 'indicate an active exit point anymore'" - (aTGotoNode haveBeenMetInInlining or: [ + (aTGotoNode isAnExitPointInlined or: [ searchForStmtListValue top value not ]) ifTrue: [ ^ self ]. searchForStmtListValue top value ifTrue: [ diff --git a/smalltalksrc/Slang/TGoToNode.class.st b/smalltalksrc/Slang/TGoToNode.class.st index 1e7c770bf7e..393af933a44 100644 --- a/smalltalksrc/Slang/TGoToNode.class.st +++ b/smalltalksrc/Slang/TGoToNode.class.st @@ -3,7 +3,7 @@ Class { #superclass : 'TParseNode', #instVars : [ 'label', - 'metInInlining' + 'indicateAnExitPointInlined' ], #category : 'Slang-AST', #package : 'Slang', @@ -41,16 +41,22 @@ TGoToNode >> children [ ] { #category : 'accessing' } -TGoToNode >> haveBeenMetInInlining [ +TGoToNode >> indicateAnExitPointInlined [ - ^ metInInlining + indicateAnExitPointInlined := true ] { #category : 'initialization' } TGoToNode >> initialize [ super initialize. - metInInlining := false + indicateAnExitPointInlined := false +] + +{ #category : 'accessing' } +TGoToNode >> isAnExitPointInlined [ + + ^ indicateAnExitPointInlined ] { #category : 'testing' } @@ -72,12 +78,6 @@ TGoToNode >> label [ ^label ] -{ #category : 'accessing' } -TGoToNode >> metInInlining [ - - metInInlining := true -] - { #category : 'enumerating' } TGoToNode >> nodesDo: aBlock parent: parent [ aBlock value: self value: parent From a0ebfaa92a42c6d57a404382d3ddf190ddd40232 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 18 Aug 2025 14:51:35 +0200 Subject: [PATCH 76/86] add tests for perform sends --- .../Slang-Tests/SLInliningTest.class.st | 87 ++++++++++++++++++- .../SLMockInliningTestClass.class.st | 30 +++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index fa5ac9a7820..2c48fb52d88 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -1150,7 +1150,6 @@ methodWithAssignmentOnWhileTrueInlined(void) { #category : 'collect-statements-for-inlining' } SLInliningTest >> testMethodWithAvoidedSelectors [ - "this method collect statements list for inlining, some specific selectors have special comportment regarding inlining strategy like avoiding inlining he receiver or the some arguments" | method translation | method := self addMethodAndMethodCalledBy: @@ -1495,6 +1494,92 @@ SLInliningTest >> testSimpleInlining [ self assert: sendStatements second selector equals: #+ ] +{ #category : 'inlining-perform-send' } +SLInliningTest >> testmethodWithPerformSend [ + + | method translation | + method := self addMethodAndMethodCalledBy: #methodWithPerformSend. + self doInliningIn: method. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: '/* SLMockInliningTestClass>>#methodWithPerformSend */ +static sqInt +methodWithPerformSend(void) +{ + return 4; +}' +] + +{ #category : 'inlining-perform-send' } +SLInliningTest >> testmethodWithPerformSendAsReceiver [ + + | method translation | + method := self addMethodAndMethodCalledBy: + #methodWithPerformSendAsReceiver. + self doInliningIn: method. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithPerformSendAsReceiver */ +static sqInt +methodWithPerformSendAsReceiver(void) +{ + return 4 + 2; +}' +] + +{ #category : 'inlining-perform-send' } +SLInliningTest >> testmethodWithPerformSendAsReceiverNotInlined [ + + | method translation | + method := self addMethodAndMethodCalledBy: + #methodWithPerformSendAsReceiverNotInlined. + self doInliningIn: method. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithPerformSendAsReceiverNotInlined */ +static sqInt +methodWithPerformSendAsReceiverNotInlined(void) +{ + return (methodB()) + 2; +}' +] + +{ #category : 'inlining-perform-send' } +SLInliningTest >> testmethodWithPerformSendNotInlined [ + + | method translation | + method := self addMethodAndMethodCalledBy: + #methodWithPerformSendNotInlined. + self doInliningIn: method. + + translation := self translate: method. + translation := translation trimBoth. + + self + assert: translation + equals: + '/* SLMockInliningTestClass>>#methodWithPerformSendNotInlined */ +static sqInt +methodWithPerformSendNotInlined(void) +{ + return methodB(); +}' +] + { #category : 'parameters' } SLInliningTest >> useExtractedInlining [ diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 3e76c0a9569..64f30107813 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -23,6 +23,12 @@ SLMockInliningTestClass >> emptyMethodAWithSimpleArgumentsInlined [ self emptyMethod: self methodBAlwaysInlined ] +{ #category : 'helpers' } +SLMockInliningTestClass >> functionalMethod [ + + ^ 4 +] + { #category : 'inlining-simple' } SLMockInliningTestClass >> methodA [ @@ -510,6 +516,30 @@ SLMockInliningTestClass >> methodWithMultipleBooleanReturn [ ^ true ] +{ #category : 'inlining-perform-send' } +SLMockInliningTestClass >> methodWithPerformSend [ + + ^ self perform: #functionalMethod +] + +{ #category : 'inlining-perform-send' } +SLMockInliningTestClass >> methodWithPerformSendAsReceiver [ + + ^ (self perform: #functionalMethod) + 2 +] + +{ #category : 'inlining-perform-send' } +SLMockInliningTestClass >> methodWithPerformSendAsReceiverNotInlined [ + + ^ (self perform: #methodB) + 2 +] + +{ #category : 'inlining-perform-send' } +SLMockInliningTestClass >> methodWithPerformSendNotInlined [ + + ^ self perform: #methodB +] + { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithRepeat [ From a0d925efd00d0cc52c1d2b88bc56eb5554acbeb8 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 18 Aug 2025 17:26:21 +0200 Subject: [PATCH 77/86] small refactoring --- .../Slang/SLInlinerWithAnnotation.class.st | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 5c608c8cef1..39ac7390bdc 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -165,8 +165,10 @@ SLInlinerWithAnnotation >> checkForCompletenessFor: aTMethod [ SLInlinerWithAnnotation >> checkForFlagIn: aTStatementList [ (aTStatementList sizeWithoutComments > 1 and: [ - aTStatementList firstNonCommentStatement isSend and: [ - aTStatementList firstNonCommentStatement selector == #flag: ] ]) + |firstStatement| + firstStatement := aTStatementList firstNonCommentStatement. +firstStatement isSend and: [ + firstStatement selector == #flag: ] ]) ifTrue: [ aTStatementList removeFirstNonCommentStatement ] ] @@ -437,14 +439,11 @@ SLInlinerWithAnnotation >> inlineFunctionCall: aSendNode inInlineableConditional exprNode isLeaf ifTrue: [ parametersToReplaceByArgument add: argName ] ]. self checkForFlagIn: parseTree. - meth - renameVarsForInliningInto: currentMethod - except: parametersToReplaceByArgument - in: codeGenerator. - meth renameLabelsForInliningInto: currentMethod. - currentMethod - addVarsDeclarationsAndLabelsOf: meth + + self + renameVarsAndLabelsForInliningIntoCurrentMethodOf: meth except: parametersToReplaceByArgument. + substitutionDict := Dictionary new: meth args size * 2. meth args with: argsForInlining do: [ :argName :exprNode | substitutionDict at: argName put: exprNode. @@ -652,7 +651,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ omittedParameters := res first. calleeParameters := res second ] value: (self trimArgumentsIn: callee). - calleeParameters size = aSendNode arguments size ifFalse: [ ^ nil ]. + calleeParameters size = aSendNode arguments size ifFalse: [ ^ self ]. callee := callee copy. inlineStmts := callee parseTree. @@ -667,13 +666,8 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ in: callee from: aSendNode. - callee - renameVarsForInliningInto: currentMethod - except: omittedParameters - in: codeGenerator. - callee renameLabelsForInliningInto: currentMethod. - currentMethod - addVarsDeclarationsAndLabelsOf: callee + self + renameVarsAndLabelsForInliningIntoCurrentMethodOf: callee except: omittedParameters. exitLabel := self @@ -696,7 +690,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ self moveDownReturnsAndAssignmentsFor: aSendNode including: inlineStmts. - self replaceVariableIn: argumentsSubstitutionDict for: callee. + self replaceVariableIn: argumentsSubstitutionDict for: inlineStmts. isUsedExpression ifFalse: [ inlineStmts nodesDo: [ :node | @@ -1014,6 +1008,19 @@ SLInlinerWithAnnotation >> removeNonSupportedStmtForInliningIn: stmtLists [ node cases do: [ :case | stmtLists remove: case ifAbsent: [ ] ] ] ] ] +{ #category : 'transformation' } +SLInlinerWithAnnotation >> renameVarsAndLabelsForInliningIntoCurrentMethodOf: aTMethod except: varsToNotRename [ + + aTMethod + renameVarsForInliningInto: currentMethod + except: varsToNotRename + in: codeGenerator. + aTMethod renameLabelsForInliningInto: currentMethod. + currentMethod + addVarsDeclarationsAndLabelsOf: aTMethod + except: varsToNotRename +] + { #category : 'conditional' } SLInlinerWithAnnotation >> replaceNodeIn: aSendNode with: data [ @@ -1056,11 +1063,11 @@ SLInlinerWithAnnotation >> replaceReturnWithGoToIfNeededIn: callee directReturn: ] { #category : 'transformation' } -SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aTMethod [ +SLInlinerWithAnnotation >> replaceVariableIn: aReplacementDictionary for: aNode [ | replacement | aReplacementDictionary isEmpty ifTrue: [ ^ self ]. - aTMethod parseTree nodesDo: [ :node | + aNode nodesDo: [ :node | (node isVariable and: [ aReplacementDictionary at: node name From 9df41f9acf52869c850291f99d4f0c446293c376 Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 18 Aug 2025 17:41:39 +0200 Subject: [PATCH 78/86] small refactoring --- smalltalksrc/Slang/SLInlinerWithAnnotation.class.st | 2 +- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 6 ------ smalltalksrc/Slang/TGoToNode.class.st | 5 +++++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index 39ac7390bdc..b4fd9c2dd34 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -636,7 +636,7 @@ SLInlinerWithAnnotation >> inlineSend: aSendNode [ assigningParent := sLNodeAnnotatorVisitor assigningParentFor: aSendNode. exitVar := assigningParent variable name ]. - "for goTo management" + "for goTo in block management" isUsedExpression := (sLNodeAnnotatorVisitor isEffectiveExpression: aSendNode) and: [ directReturn not and: [ diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 81ee3c6f329..a658128bb93 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -959,12 +959,6 @@ SLNodeAnnotatorVisitor >> visitGoToNode: aTGotoNode [ self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aTGotoNode. - - "goTo only comes from inlining to keep the execution flow correct where a return was. - since inlining goes through multiple phases, static analysis may become flawed, for example : - 'a := [... methodWithReturn … b]' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... c . goTo l ... l ... a := b]' - 'a := methodeWithMultipleReturn' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... a := c . goTo l ... l ... a := b]' - so after an inlining phases we invalidate goTo generated so that they do not 'indicate an active exit point anymore'" (aTGotoNode isAnExitPointInlined or: [ searchForStmtListValue top value not ]) ifTrue: [ ^ self ]. diff --git a/smalltalksrc/Slang/TGoToNode.class.st b/smalltalksrc/Slang/TGoToNode.class.st index 393af933a44..b4af9ffe544 100644 --- a/smalltalksrc/Slang/TGoToNode.class.st +++ b/smalltalksrc/Slang/TGoToNode.class.st @@ -42,6 +42,11 @@ TGoToNode >> children [ { #category : 'accessing' } TGoToNode >> indicateAnExitPointInlined [ + "goTo only comes from inlining to keep the execution flow correct where a return was. + since inlining goes through multiple phases, static analysis may become flawed, for example : + 'a := [... methodWithReturn … b]' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... c . goTo l ... l ... a := b]' + 'a := methodeWithMultipleReturn' gives 'a = [... c . goTo l ... l ... b]' is equivalent (and should be transform to) '[ ... a := c . goTo l ... l ... a := b]' + so after an inlining phases we might invalidate goTo generated so that they do not 'indicate an active exit point' anymore" indicateAnExitPointInlined := true ] From 2f924a9a73479612e7b700dd0a32e30d40b49543 Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 19 Aug 2025 18:16:30 +0200 Subject: [PATCH 79/86] don't consider arguments of condionals to be in expression by default --- .../SLAnnotatorVisitorTest.class.st | 31 ------------------- .../Slang/SLNodeAnnotatorVisitor.class.st | 6 ++-- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 91d096749b4..6432f276794 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -407,22 +407,6 @@ SLAnnotatorVisitorTest >> testMethodWithConstant [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]). - "ifTrue body" - stmtListOfIfTrueAndConstant4AndAndReturn5 := (nonEffectiveExpressionOrStatementCollection - select: [ :node | - node isSend ]) first - arguments first - asOrderedCollection. - stmtListOfIfTrueAndConstant4AndAndReturn5 first statements do: [ - :node | stmtListOfIfTrueAndConstant4AndAndReturn5 add: node ]. - stmtListOfIfTrueAndConstant4AndAndReturn5 do: [ :node | - self assert: (sLNodeAnnotatorVisitor isInExpression: node) ]. - - (nonEffectiveExpressionOrStatementCollection reject: [ :node | - stmtListOfIfTrueAndConstant4AndAndReturn5 includes: node ]) do: [ - :node | - self assert: (sLNodeAnnotatorVisitor isInExpression: node) not ]. - self assert: self checkNonEffectiveExpressionOrStatementCollection. self assert: self checkEffectiveExpressionValueCollection ] @@ -588,21 +572,6 @@ SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ (sLNodeAnnotatorVisitor isInAssignment: node) not and: [ (sLNodeAnnotatorVisitor returningParentFor: node) isNil ] ] ]). - ifTrueIfFalseNode := (nonEffectiveExpressionOrStatementCollection - select: [ :node | node isSend ]) first. - firstStmtList := tMethod parseTree. - { - firstStmtList. - ifTrueIfFalseNode } do: [ :node | - self assert: - (sLNodeAnnotatorVisitor isInExpression: ifTrueIfFalseNode) not ]. - - (nonEffectiveExpressionOrStatementCollection reject: [ :node | - { - firstStmtList. - ifTrueIfFalseNode } includes: node ]) do: [ :node | - self assert: (sLNodeAnnotatorVisitor isInExpression: node) ]. - self assert: self checkNonEffectiveExpressionOrStatementCollection. self assert: self checkEffectiveExpressionValueCollection ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index a658128bb93..fe93ca96bf2 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -848,10 +848,9 @@ SLNodeAnnotatorVisitor >> visitConditionalSend: aSendNode [ self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. - self pushInExpressionStack. - self giveStacksInfoForMultipleBranches: aSendNode arguments. + self pushInExpressionStack. searchForExpressionValueStack push: true. aSendNode receiver accept: self. searchForExpressionValueStack pop. @@ -873,11 +872,10 @@ SLNodeAnnotatorVisitor >> visitCppConditional: aSendNode [ self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. - self pushInExpressionStack. - arguments := aSendNode arguments. self giveStacksInfoForMultipleBranches: arguments allButFirst. + self pushInExpressionStack. searchForExpressionValueStack push: true. aSendNode receiver accept: self. arguments first accept: self. From afba0d8a02b6eca72e5557db3f364328e9abb3a1 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 20 Aug 2025 11:31:34 +0200 Subject: [PATCH 80/86] clean leftover commented code --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 7 ------- 1 file changed, 7 deletions(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index fe93ca96bf2..f2b76d3a7d4 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -624,13 +624,6 @@ SLNodeAnnotatorVisitor >> processValueSourceFor: aNode from: aStack constantStri entry := info at: aNode. isEffectivelyIgnored := false. - - "searchForStmtListValue isNotEmpty ifTrue: [ - searchForStmtListValue top key ifTrue: [ - assoc := true -> assoc value ]. - searchForStmtListValue top value ifTrue: [ - isEffectivelyIgnored := true ] ]." - currentShouldBePartiallyIgnored ifTrue: [ assoc key ifTrue: [ From 9f8103468c27c52930392d2c3e72dbc721b547d0 Mon Sep 17 00:00:00 2001 From: renaud Date: Wed, 20 Aug 2025 11:33:59 +0200 Subject: [PATCH 81/86] clean leftover unused variables in tests --- smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st index 6432f276794..5497140d6c5 100644 --- a/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st +++ b/smalltalksrc/Slang-Tests/SLAnnotatorVisitorTest.class.st @@ -336,7 +336,7 @@ SLAnnotatorVisitorTest >> testMethodWithConditionalAssignment [ { #category : 'constant' } SLAnnotatorVisitorTest >> testMethodWithConstant [ - | tMethod effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet constant5And6 constantTrue returns return5 return6 stmtListOfIfTrueAndConstant4AndAndReturn5 | + | tMethod effectiveConstantExpressionValueSet nonEffectiveExpressionOrStatementConstantSet constant5And6 constantTrue returns return5 return6 | tMethod := ccg methodNamed: #methodWithConstant. sLNodeAnnotatorVisitor visit: tMethod parseTree. @@ -509,7 +509,7 @@ SLAnnotatorVisitorTest >> testMethodWithReturn [ { #category : 'return' } SLAnnotatorVisitorTest >> testMethodWithReturnInConditional [ - | tMethod effectiveConstantExpressionValueSet constant2And4 returnNodes return2 return4 ifTrueIfFalseNode firstStmtList constantTrue | + | tMethod effectiveConstantExpressionValueSet constant2And4 returnNodes return2 return4 constantTrue | tMethod := ccg methodNamed: #methodWithReturnInConditional. sLNodeAnnotatorVisitor visit: tMethod parseTree. From e956716e12f1df57f3cce26d540e2b1fddc3c6be Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 21 Aug 2025 15:24:26 +0200 Subject: [PATCH 82/86] clean leftover comment --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index f2b76d3a7d4..57bd138fbb6 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -383,8 +383,7 @@ SLNodeAnnotatorVisitor >> initialize [ { #category : 'helpers-stack-operation' } SLNodeAnnotatorVisitor >> invalidateSearchStacksAfterReturn [ - "in an expression like a := exp1 ifTrue: [^ exp2] ifFalse: [exp3], the return node must not be simply ignored, it must correct the stacks so the exploration of exp2 does not mark nodes as used expression for the node a := ... - the stack for return and general expression are already being given new elements from the return node so it is not necessary to modify them" + "in an expression like a := exp1 ifTrue: [^ exp2] ifFalse: [exp3], the return node must not be simply ignored, it must correct the stacks so the exploration of exp2 does not mark nodes as used expression for the node a := ..." | top | searchForAssignValueStack isNotEmpty ifTrue: [ From 44a52a4637f776523943eac4d29c71b24e08ef59 Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 21 Aug 2025 15:50:33 +0200 Subject: [PATCH 83/86] clean leftover code for debugging --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 1 - 1 file changed, 1 deletion(-) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 57bd138fbb6..0a8f01646f1 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -986,7 +986,6 @@ SLNodeAnnotatorVisitor >> visitReturnNode: aReturnNode [ self annotateNodeState: aReturnNode. hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. - searchForAssignValueStack isNotEmpty ifTrue: [ ]. searchForExpressionValueStack push: true. searchForReturnValueStack push: true -> aReturnNode. From b55e50c6eb7f1414e711f9b1419c49afb851f59a Mon Sep 17 00:00:00 2001 From: renaud Date: Thu, 21 Aug 2025 16:01:59 +0200 Subject: [PATCH 84/86] add a comment --- smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st | 1 + 1 file changed, 1 insertion(+) diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index 0a8f01646f1..a22576dad60 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -388,6 +388,7 @@ SLNodeAnnotatorVisitor >> invalidateSearchStacksAfterReturn [ | top | searchForAssignValueStack isNotEmpty ifTrue: [ top := searchForAssignValueStack pop. + "keeping the assignment does mean that if a goTo is in the return expression it will incorrectly mark it but Slang/C doesnt authorized goTo in return and will not compile so keeping it is safe" searchForAssignValueStack push: false -> top value ]. searchForReturnValueStack isNotEmpty ifTrue: [ From 53dab74abae782fa2882b0738d8bca5446fd787b Mon Sep 17 00:00:00 2001 From: renaud Date: Mon, 25 Aug 2025 14:16:16 +0200 Subject: [PATCH 85/86] fix a bug with iterative (not in the VM) and better tests --- .../Slang-Tests/SLInliningTest.class.st | 36 ++++++++--------- .../SLMockInliningTestClass.class.st | 17 ++++++-- .../Slang/SLNodeAnnotatorVisitor.class.st | 40 ++++++++++--------- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 2c48fb52d88..2effa3ad199 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -986,10 +986,10 @@ methodWithAssignmentOnRepeatInlined(void) if (methodB()) { a = 5; goto l2; - } else { - a = 0; - goto l2; } + /* begin methodB */ + 2 + 2; + /* end methodB */ } l2: ; @@ -1022,11 +1022,12 @@ methodWithAssignmentOnToByDoInlined(void) /* begin methodWithToByDo */ for (i = 1; i <= 4; i += 2) { if (methodB(i)) { - /* begin methodB */ - a = 2 + 2; + a = 5; goto l2; - /* end methodB */ } + /* begin methodB */ + 2 + 2; + /* end methodB */ } l2: ; @@ -1059,11 +1060,12 @@ methodWithAssignmentOnToDoInlined(void) /* begin methodWithToDo */ for (i = 1; i <= 2; i += 1) { if (methodB(i)) { - /* begin methodB */ - a = 2 + 2; + a = 5; goto l2; - /* end methodB */ } + /* begin methodB */ + 2 + 2; + /* end methodB */ } l2: ; @@ -1095,14 +1097,12 @@ methodWithAssignmentOnWhileTrueBinaryInlined(void) /* begin methodWithWhileTrueBinary */ while (1) { if (methodB()) { - /* begin methodB */ - a = 2 + 2; - goto l2; - /* end methodB */ - } else { - a = 0; + a = 5; goto l2; } + /* begin methodB */ + 2 + 2; + /* end methodB */ } l2: ; @@ -1136,10 +1136,10 @@ methodWithAssignmentOnWhileTrueInlined(void) if (methodB()) { a = 5; goto l2; - } else { - a = 0; - goto l2; } + /* begin methodB */ + 2 + 2; + /* end methodB */ } while (1); l2: ; diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 64f30107813..6112c0cf09e 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -543,7 +543,9 @@ SLMockInliningTestClass >> methodWithPerformSendNotInlined [ { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithRepeat [ - [ self methodB ifTrue: [ ^ 5 ] ] repeat + [ + self methodB ifTrue: [ ^ 5 ]. + self methodB ] repeat ] { #category : 'inlining-conditional-receiver' } @@ -571,13 +573,17 @@ SLMockInliningTestClass >> methodWithReturningIfWithReturnPushedDownInlined [ { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithToByDo [ - 1 to: 4 by: 2 do: [:i | (self methodB: i)ifTrue: [ ^ self methodB ] ] + 1 to: 4 by: 2 do: [ :i | + (self methodB: i) ifTrue: [ ^ 5 ]. + self methodB ] ] { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithToDo [ - 1 to: 2 do: [:i | (self methodB: i) ifTrue: [ ^ self methodB ] ] + 1 to: 2 do: [ :i | + (self methodB: i) ifTrue: [ ^ 5 ]. + self methodB ] ] { #category : 'inlinng-variable-helpers' } @@ -593,11 +599,14 @@ SLMockInliningTestClass >> methodWithWhileTrue [ [ self methodB ifTrue: [ ^ 5 ]. + self methodB. true ] whileTrue ] { #category : 'inlining-iterative-helpers' } SLMockInliningTestClass >> methodWithWhileTrueBinary [ - [ true ] whileTrue: [ self methodB ifTrue: [ ^ self methodB ] ] + [ true ] whileTrue: [ + self methodB ifTrue: [ ^ 5 ]. + self methodB ] ] diff --git a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st index a22576dad60..a3c76c778f9 100644 --- a/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st +++ b/smalltalksrc/Slang/SLNodeAnnotatorVisitor.class.st @@ -83,7 +83,7 @@ SLNodeAnnotatorVisitor >> addToEffectiveExpressionOrNot: aNode [ { #category : 'annotate' } SLNodeAnnotatorVisitor >> addToFromSearchStmtList: aNode [ - "for stacks restoration only, indicate if we are in a statement list and if we are indicate if the statement list was a used expression" + "for stacks restoration only, indicate if we are in a statement or expression list" (info at: aNode) at: self fromSearchStmtListString @@ -284,6 +284,7 @@ SLNodeAnnotatorVisitor >> handleConditionalSend: aSendNode [ { #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> handleIterativeSend: aSendNode [ + "in Slang, iterative are not considered as expression but rather as statement to be closer to C" aSendNode isUnaryIterativeSend ifTrue: [ self visitUnaryIterativeSend: aSendNode. @@ -583,6 +584,7 @@ SLNodeAnnotatorVisitor >> moveDownSingleBranchConditional: aNode newBranch: anOt | newSelector | aNode isSend ifFalse: [ ^ self ]. + aNode selector = #ifTrue: ifTrue: [ newSelector := #ifTrue:ifFalse: ]. aNode selector = #ifFalse: ifTrue: [ newSelector := #ifFalse:ifTrue: ]. @@ -599,7 +601,7 @@ SLNodeAnnotatorVisitor >> moveDownSingleBranchConditional: aNode newBranch: anOt | newArgument last | newArgument := TStatementListNode statements: { anOtherNode }. last := aNode arguments first lastNonCommentStatement. - last isGoTo ifTrue: [ newArgument addLast: last copy ]. + last isGoTo ifTrue: [ ^ self ]. aNode selector: newSelector; arguments: aNode arguments , { newArgument } ] @@ -795,11 +797,13 @@ SLNodeAnnotatorVisitor >> visitAssignmentNode: anAssignmentNode [ { #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitBinaryIterativeSend: aSendNode [ - "in Slang, iterative are not considered as expression but rather as statement to be closer to C" + | hasInvalidateAStmtSearch | self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. + self pushInExpressionStack. searchForExpressionValueStack push: true. @@ -807,7 +811,8 @@ SLNodeAnnotatorVisitor >> visitBinaryIterativeSend: aSendNode [ aSendNode receiver accept: self. searchForExpressionValueStack pop. - inExpressionStack pop + inExpressionStack pop. + self resetSearchStacksAfterReturn: hasInvalidateAStmtSearch ] { #category : 'visiting' } @@ -879,12 +884,13 @@ SLNodeAnnotatorVisitor >> visitCppConditional: aSendNode [ { #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ - "in Slang, iterative are not considered as expression but rather as statement to be closer to C" - | arguments | + | arguments hasInvalidateAStmtSearch | self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. + self pushInExpressionStack. searchForExpressionValueStack push: true. @@ -894,7 +900,8 @@ SLNodeAnnotatorVisitor >> visitDoIterativeSend: aSendNode [ aSendNode receiver accept: self. searchForExpressionValueStack pop. - inExpressionStack pop + inExpressionStack pop. + self resetSearchStacksAfterReturn: hasInvalidateAStmtSearch ] { #category : 'visiting-helpers' } @@ -1096,33 +1103,31 @@ SLNodeAnnotatorVisitor >> visitSwitchStatementNode: aSwitchStatementNode [ { #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitUnaryIterativeSend: aSendNode [ - "in Slang, iterative are not considered as expression but rather as statement to be closer to C" + | hasInvalidateAStmtSearch | self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. + self pushInExpressionStack. searchForExpressionValueStack push: true. aSendNode receiver accept: self. searchForExpressionValueStack pop. - inExpressionStack pop + inExpressionStack pop. + self resetSearchStacksAfterReturn: hasInvalidateAStmtSearch ] { #category : 'visiting-helpers' } SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSendNode [ - "for a 'a := [[ exp . condition ] whileTrue]' case (obtained through inlining for example), the last expression of the block is a condition not the expression value of a := ..." - | block condition | + | hasInvalidateAStmtSearch | self activateCurrentSouldBePartiallyIgnored. self annotateNodeState: aSendNode. - "to not interpret the condition as the effective value, we move the condition to the top" - block := aSendNode receiver. - condition := block lastNonCommentStatement. - block removeLast. - block addAllFirstKeepingFirstComments: { condition }. + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn. self pushInExpressionStack. searchForExpressionValueStack push: true. @@ -1132,8 +1137,7 @@ SLNodeAnnotatorVisitor >> visitUnaryIterativeSendWithImportantLastStatement: aSe searchForExpressionValueStack pop. inExpressionStack pop. - block removeFirstNonCommentStatement. - block addAllLastKeepingEndComments: { condition } + hasInvalidateAStmtSearch := self invalidateSearchStacksAfterReturn ] { #category : 'visiting-helpers' } From 260633573bf0b436d9b6de9487634454cb7c2c1a Mon Sep 17 00:00:00 2001 From: renaud Date: Tue, 26 Aug 2025 17:29:23 +0200 Subject: [PATCH 86/86] better handling of void returning method --- .../Slang-Tests/SLInliningTest.class.st | 39 ++++++++++++------- .../SLMockInliningTestClass.class.st | 17 ++++++++ .../Slang/SLInlinerWithAnnotation.class.st | 20 ++++++---- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/smalltalksrc/Slang-Tests/SLInliningTest.class.st b/smalltalksrc/Slang-Tests/SLInliningTest.class.st index 2effa3ad199..a10b7070bfb 100644 --- a/smalltalksrc/Slang-Tests/SLInliningTest.class.st +++ b/smalltalksrc/Slang-Tests/SLInliningTest.class.st @@ -85,7 +85,8 @@ SLInliningTest >> test2ChainInliningEmptyMethodAWithArgumentsInlined [ "a bug present since old version, vm works but it would be nice to fix it one day" | method translation | - method := self addMethodAndMethodCalledBy: #emptyMethodAWithArgumentsInlined. + method := self addMethodAndMethodCalledBy: + #emptyMethodAWithArgumentsInlined. self doInliningIn: method. translation := self translate: method. @@ -106,9 +107,10 @@ emptyMethodAWithArgumentsInlined(void) /* begin methodA */ 1 + 1; /* begin methodB */ - arg = 2 + 2; + 2 + 2; /* end methodB */ /* end methodA */ + arg = null; /* end methodCAlwaysInlined */ /* end emptyMethod: */ return 0; @@ -160,7 +162,7 @@ SLInliningTest >> test2ChainInliningMethodAWithArgumentsInlined [ static sqInt methodAWithArgumentsInlined(void) { - return methodB((/* begin methodCAlwaysInlined */ 3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */ /* end methodCAlwaysInlined */)); + return methodB((/* begin methodCAlwaysInlined */ 3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */, null /* end methodA */ /* end methodCAlwaysInlined */)); }' ] @@ -182,7 +184,7 @@ SLInliningTest >> test2ChainInliningMethodAWithReturningSendArgumentsInlined [ static sqInt methodAWithReturningSendArgumentsInlined(void) { - return methodB((/* begin methodCAlwaysInlined */ 3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */ /* end methodA */ /* end methodCAlwaysInlined */)); + return methodB((/* begin methodCAlwaysInlined */ 3 + 3, /* begin methodA */ 1 + 1, /* begin methodB */ 2 + 2 /* end methodB */, null /* end methodA */ /* end methodCAlwaysInlined */)); }' ] @@ -703,14 +705,16 @@ methodAInlineReturningInlinedIfTrueIfFalseInAssignment(void) if (methodB()) { /* begin methodB */ - a = 2 + 2; + 2 + 2; + a = null; /* end methodB */ } else { /* begin methodA */ 1 + 1; /* begin methodB */ - a = 2 + 2; + 2 + 2; /* end methodB */ + a = null; /* end methodA */ } return 0; @@ -737,14 +741,16 @@ methodAInlineReturningInlinedIfTrueIfFalseInReturn(void) { if (methodB()) { /* begin methodB */ - return 2 + 2; + 2 + 2; + return null; /* end methodB */ } else { /* begin methodA */ 1 + 1; /* begin methodB */ - return 2 + 2; + 2 + 2; /* end methodB */ + return null; /* end methodA */ } }' @@ -772,7 +778,8 @@ methodAInlineReturningInlinedIfTrueInAssignment(void) if (methodB()) { /* begin methodB */ - a = 2 + 2; + 2 + 2; + a = null; /* end methodB */ } else { a = 0; @@ -801,7 +808,8 @@ methodAInlineReturningInlinedIfTrueInReturn(void) { if (methodB()) { /* begin methodB */ - return 2 + 2; + 2 + 2; + return null; /* end methodB */ } return 0; @@ -879,7 +887,8 @@ methodAReturnBlockAssignment(void) sqInt a; /* begin methodB */ - return (a = 2 + 2); + 2 + 2; + return (a = null); /* end methodB */ }' ] @@ -1184,7 +1193,7 @@ methodWithAvoidedSelectors(void) /* end methodB */ } # endif /* methodB() */ - ((/* begin methodB */ 2 + 2 /* end methodB */)) && 0; + ((/* begin methodB */ 2 + 2, null /* end methodB */)) && 0; 0 || (methodB()); if (methodB()) { /* begin methodB */ @@ -1248,14 +1257,16 @@ methodWithBlockWithReturningIfArgumentInlined(void) { if (methodB()) { /* begin methodB */ - a = 2 + 2; + 2 + 2; + a = null; /* end methodB */ } else { /* begin methodA */ 1 + 1; /* begin methodB */ - a = 2 + 2; + 2 + 2; /* end methodB */ + a = null; /* end methodA */ } } diff --git a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st index 6112c0cf09e..0c96917603f 100644 --- a/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st +++ b/smalltalksrc/Slang-Tests/SLMockInliningTestClass.class.st @@ -500,6 +500,23 @@ SLMockInliningTestClass >> methodWithBlockWithReturningIfWithInlinedArgumentInli self methodBMultipleReturnExpressionAndStatement ] ] +{ #category : 'inlining-assignment-helpers' } +SLMockInliningTestClass >> methodWithConditionalReturnPlusAssignment [ + + | b x y | + b := x > y + ifTrue: [ ^ 7 ] + ifFalse: [ 0 ]. + ^ b + 1 +] + +{ #category : 'inlining-assignment' } +SLMockInliningTestClass >> methodWithDoubleAssignment [ + + | a | + a := self methodWithConditionalReturnPlusAssignment +] + { #category : 'inlining-conditional-receiver' } SLMockInliningTestClass >> methodWithIfTrueReceiverInlined [ diff --git a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st index b4fd9c2dd34..557e4eef390 100644 --- a/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st +++ b/smalltalksrc/Slang/SLInlinerWithAnnotation.class.st @@ -895,7 +895,7 @@ SLInlinerWithAnnotation >> label: exitLabel for: aTMethod [ aTMethod parseTree nodesDo: [ :node | | replacement | node isReturn ifTrue: [ - aTMethod + aTMethod transformReturnSubExpression: node toAssignmentOf: nil andGoto: exitLabel @@ -910,6 +910,7 @@ SLInlinerWithAnnotation >> label: exitLabel for: aTMethod [ map isEmpty ifTrue: [ aTMethod deny: labelUsed. ^ false ]. + "Now do a top-down replacement for all returns that should be mapped to assignments and gotos" aTMethod parseTree replaceNodesIn: map. @@ -1052,13 +1053,16 @@ SLInlinerWithAnnotation >> replaceNodesIn: aReplacementDictionary for: aNode [ SLInlinerWithAnnotation >> replaceReturnWithGoToIfNeededIn: callee directReturn: directReturn [ | exitLabel | - callee hasReturn ifTrue: [ - directReturn ifFalse: [ - exitLabel := currentMethod unusedLabelForInliningInto: - currentMethod. - (self label: exitLabel for: callee) - ifTrue: [ currentMethod labels add: exitLabel ] - ifFalse: [ "is label used?" ^ nil ] ] ]. + callee hasReturn + ifTrue: [ + directReturn ifFalse: [ + exitLabel := currentMethod unusedLabelForInliningInto: + currentMethod. + (self label: exitLabel for: callee) + ifTrue: [ currentMethod labels add: exitLabel ] + ifFalse: [ "is label used?" ^ nil ] ] ] + ifFalse: [ + callee parseTree addLast: (TConstantNode new setValue: nil) ]. ^ exitLabel ]