Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-eem.1850.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.1850 Author: eem Time: 27 April 2016, 7:56:19.331347 pm UUID: 5a13ad57-d069-44d3-b7c3-855c926f66ad Ancestors: VMMaker.oscog-eem.1849
In 1850, Harriet Tubman becomes an official conductor of the Underground Railroad in the United States. The Potato Famine is ongoing.
Partially fix inlining of returning ifs. inlineCodeOrNilForStatement:in: is wrong to assume that a send is never a direct return; it is if it is in a returning if. So refactor it to inlineCodeOrNilForStatement:returningNodes:in: and have its caller tryToInlineMethodsIn: compute the set of returning nodes appropriately. This could all be cleaned up somewhat but better to get it working first. Ficxing this bug then uncovers another in exitVar:label:. exitVar:label: uses replaceNodesIn: to replace ^exprs with exitVar := expr. goto exitLabel, but 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].
This isn't finished yet; e.g.
SpurMemoryManager>>firstFixedFieldOfMaybeImmediate: oop "for the message send breakpoint; selectors can be immediates." <inline: false> ^(self isImmediate: oop) ifTrue: [oop asVoidPointer] ifFalse: [self firstFixedField: oop] is mistranslated as
static void * NoDbgRegParms firstFixedFieldOfMaybeImmediate(sqInt oop) { return (oop & (tagMask()) ? ((void *)oop) : (/* begin firstFixedField: */ return pointerForOop(oop + BaseHeaderSize))); }
but it's close enough, given that the generated code from the previous version is wrong yet compiles silently. This versiomn correctly translates e.g. inlines of positiveMachineIntegerFor: and inlines of positive32BitIntegerFor: into it. Hopefully we'll fix the remaining issues PDQ.
Change statement list printing to use KB style I can't read anything else, forgive me).
=============== Diff against VMMaker.oscog-eem.1849 ===============
Item was added: + ----- Method: TMethod>>deny: (in category 'error handling') ----- + deny: aBooleanOrBlock + <doNotGenerate> + aBooleanOrBlock value ifTrue: [AssertionFailure signal: 'Assertion failed']!
Item was changed: ----- Method: TMethod>>exitVar:label: (in category 'inlining') ----- exitVar: exitVar label: exitLabel "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 | - | labelUsed map eliminateReturnSelfs | 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" eliminateReturnSelfs := ((definingClass inheritsFrom: VMClass) and: [definingClass isStructClass]) not and: [returnType = #void or: [returnType = #sqInt]]. parseTree nodesDo: [:node | | replacement | node isReturn ifTrue: + [self 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 args reject: [:arg| arg endsWithReturn]) do: + [:nodeNeedingReturn| + self transformReturnSubExpression: nodeNeedingReturn statements last + toAssignmentOf: exitVar + andGoto: exitLabel + unless: eliminateReturnSelfs + into: [:rep :labelWasUsed| + replacement := rep. + labelWasUsed ifTrue: [labelUsed := true]]. + map + at: nodeNeedingReturn statements last + put: replacement]] + ifFalse: + [map + at: node + put: (replacement ifNil: + [TLabeledCommentNode new setComment: 'return ', node expression printString])]]]. + map isEmpty ifTrue: + [self deny: labelUsed. + ^false]. + "Now do a top-down replacement for all returns that should be mapped to assignments and gotos" - [replacement := (node expression isVariable "Eliminate ^self's" - and: [node expression name = 'self' - and: [eliminateReturnSelfs]]) - ifTrue: [nil] - ifFalse: - [exitVar - ifNil: [node expression] - ifNotNil: [TAssignmentNode new - setVariable: (TVariableNode new setName: exitVar) - expression: node expression]]. - node ~~ parseTree statements last ifTrue: - [replacement := replacement - ifNil: [TGoToNode new setLabel: exitLabel; yourself] - ifNotNil: - [TStmtListNode new - setArguments: #() - statements: {replacement. - TGoToNode new setLabel: exitLabel; yourself}; - yourself]. - labelUsed := true]. - map - at: node - put: (replacement ifNil: - [TLabeledCommentNode new setComment: 'return ', node expression printString])]]. 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]. + parseTree replaceNodesIn: elisionMap]. + "Afterwards all returns should be gone." + self deny: parseTree hasExplicitReturn. "Now flatten any new statement lists..." parseTree nodesDo: [:node| | list | (node isStmtList and: [node statements notEmpty and: [node statements last isStmtList]]) ifTrue: [list := node statements last statements. node statements removeLast; addAllLast: list]]. ^labelUsed!
Item was removed: - ----- Method: TMethod>>inlineCodeOrNilForStatement:in: (in category 'inlining') ----- - inlineCodeOrNilForStatement: aNode in: aCodeGen - "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." - - | stmts | - aNode isReturn ifTrue: - [(self inlineableSend: aNode expression in: aCodeGen) ifTrue: - [stmts := self inlineSend: aNode expression - directReturn: true exitVar: nil in: aCodeGen. - ^stmts]]. - (aNode isAssignment and: [aNode expression isSend]) ifTrue: - [(self inlineableSend: aNode expression in: aCodeGen) ifTrue: - [^self inlineSend: aNode expression - directReturn: false exitVar: aNode variable name in: aCodeGen]]. - aNode isSend ifTrue: - [(self inlineableSend: aNode in: aCodeGen) ifTrue: - [^self inlineSend: aNode - directReturn: false exitVar: nil in: aCodeGen]]. - ^nil!
Item was added: + ----- Method: TMethod>>inlineCodeOrNilForStatement:returningNodes:in: (in category 'inlining') ----- + inlineCodeOrNilForStatement: aNode returningNodes: returningNodes in: aCodeGen + "If the given statement node can be inlined, answer the statements that replace it. Otherwise, answer nil." + + | stmts | + aNode isReturn ifTrue: + [(self inlineableSend: aNode expression in: aCodeGen) ifTrue: + [stmts := self inlineSend: aNode expression + directReturn: true exitVar: nil in: aCodeGen. + ^stmts]]. + (aNode isAssignment and: [aNode expression isSend]) ifTrue: + [(self inlineableSend: aNode expression in: aCodeGen) ifTrue: + [^self inlineSend: aNode expression + directReturn: false exitVar: aNode variable name in: aCodeGen]]. + aNode isSend ifTrue: + [(self inlineableSend: aNode in: aCodeGen) ifTrue: + [^self inlineSend: aNode + directReturn: (returningNodes includes: aNode) exitVar: nil in: aCodeGen]]. + ^nil!
Item was added: + ----- Method: TMethod>>transformReturnSubExpression:toAssignmentOf:andGoto:unless:into: (in category 'inlining') ----- + 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 statements last + ifTrue: + [aBinaryBlock value: replacement value: false] + ifFalse: + [replacement := replacement + ifNil: [TGoToNode new setLabel: exitLabel; yourself] + ifNotNil: + [TStmtListNode new + setArguments: #() + statements: {replacement. + TGoToNode new setLabel: exitLabel; yourself}; + yourself]. + aBinaryBlock value: replacement value: true]!
Item was changed: ----- Method: TMethod>>tryToInlineMethodsIn: (in category 'inlining') ----- tryToInlineMethodsIn: aCodeGen "Expand any (complete) inline methods called by this method. Set the complete bit when all inlining has been done. Return true if something was inlined."
+ | stmtLists didSomething newStatements sendsToInline returningNodes | - | stmtLists didSomething newStatements sendsToInline | self definedAsMacro ifTrue: [complete := true. ^false]. didSomething := false. sendsToInline := Dictionary new: 100. parseTree nodesDo: [:node| (self transformConditionalAssignment: node in: aCodeGen) ifNotNil: [:replacement| sendsToInline at: node put: replacement]. (self inlineableFunctionCall: node in: aCodeGen) ifTrue: [(self inlineFunctionCall: node in: aCodeGen) ifNotNil: [:replacement| sendsToInline at: node put: replacement]]] unless: "Don't inline the arguments to asserts to keep the asserts readable" [:node| node isSend and: [node selector == #cCode:inSmalltalk: or: [aCodeGen isAssertSelector: node selector]]].
sendsToInline isEmpty ifFalse: [didSomething := true. parseTree := parseTree replaceNodesIn: sendsToInline].
didSomething ifTrue: [writtenToGlobalVarsCache := nil. ^didSomething].
+ returningNodes := Set new. + parseTree nodesDo: + [:node| + node isReturn ifTrue: + [returningNodes add: node expression. + node expression isConditionalSend ifTrue: + [returningNodes addAll: (node expression args collect: [:stmtList| stmtList statements last])]]]. stmtLists := self statementsListsForInliningIn: aCodeGen. stmtLists do: [:stmtList| newStatements := OrderedCollection new: 100. stmtList statements do: [:stmt| + (self inlineCodeOrNilForStatement: stmt returningNodes: returningNodes in: aCodeGen) - (self inlineCodeOrNilForStatement: stmt in: aCodeGen) ifNil: [newStatements addLast: stmt] ifNotNil: [:inlinedStmts| didSomething := true. newStatements addAllLast: inlinedStmts]]. stmtList setStatements: newStatements asArray].
didSomething ifTrue: [writtenToGlobalVarsCache := nil. ^didSomething].
complete ifFalse: [self checkForCompleteness: stmtLists in: aCodeGen. complete ifTrue: [ didSomething := true ]]. "marking a method complete is progress" ^didSomething!
Item was added: + ----- Method: TParseNode>>isConditionalSend (in category 'testing') ----- + isConditionalSend + "Answer if the receiver is a send of any of the conditionals, ifTrue: ifTrue:ifFalse: et al" + ^false!
Item was added: + ----- Method: TSendNode>>isConditionalSend (in category 'testing') ----- + isConditionalSend + "Answer if the receiver is a send of any of the conditionals, ifTrue: ifTrue:ifFalse: et al" + ^#( ifTrue:ifFalse: ifFalse:ifTrue: ifTrue: ifFalse: + ifNil:ifNotNil: ifNotNil:ifNil: ifNil: ifNotNil) includes: selector!
Item was changed: ----- Method: TSendNode>>isReturningIf (in category 'testing') ----- isReturningIf + ^(#(ifTrue:ifFalse: ifFalse:ifTrue: ifNil:ifNotNil: ifNotNil:ifNil:) includes: selector) - ^(#(ifTrue:ifFalse: ifFalse:ifTrue:) includes: selector) and: [arguments allSatisfy: [:arg| arg endsWithReturn]]!
Item was changed: ----- Method: TStmtListNode>>printOn:level: (in category 'printing') ----- printOn: aStream level: level
+ statements size > 1 ifTrue: [ aStream crtab: level + 1 ]. aStream nextPut: $[. (arguments notNil and: [arguments notEmpty]) ifTrue: [arguments do: [ :arg | aStream nextPutAll: ' :'; nextPutAll: arg]. aStream nextPutAll: ' | ']. self printStatementsOn: aStream level: level. aStream nextPut: $]!
Item was changed: ----- Method: TStmtListNode>>printStatementsOn:level: (in category 'printing') ----- printStatementsOn: aStream level: level
+ statements + do: [:s| s printOn: aStream level: level] + separatedBy: [aStream nextPut: $.; crtab: level + 1]! - statements size > 1 ifTrue: [ aStream crtab: level + 1 ]. - 1 to: statements size do: [ :i | - (statements at: i) printOn: aStream level: level. - i = statements size ifTrue: [ - (statements size > 1) ifTrue: [ - aStream crtab: level. - ]. - ] ifFalse: [ - aStream nextPut: $.; crtab: level + 1. - ]. - ].!
vm-dev@lists.squeakfoundation.org