Eliot Miranda uploaded a new version of VMMaker to project VM Maker Inbox: http://source.squeak.org/VMMakerInbox/VMMaker.oscog-eem.2764.mcz ==================== Summary ==================== Name: VMMaker.oscog-eem.2764 Author: eem Time: 23 June 2020, 12:43:04.297105 pm UUID: 1e841de6-39e5-4db6-9634-97e0b3f80bbe Ancestors: VMMaker.oscog-eem.2763 Interim refactored VMMaker.oscog-nice.2762. Code generation is still broken, but on its way to being repaired. Badic refactoring is to add a codeGen parameter to transformInAssignmentTo: so that isAnRValueIn: can be used there-in. There is still a bug that assignments are getting added after the error in a case with no otherwise. That's bogus. error never returns. Fixed isSameAs: to do an equality check. Fixed the fixed point in ensureConditionalAssignmentsAreTransformedIn: so that iteration stops if no change is made to the parse tree on transforming assignments. =============== Diff against VMMaker.oscog-eem.2763 =============== Item was changed: ----- Method: CogARMCompiler>>concretizeAndCqRR (in category 'generate machine code - concretize') ----- concretizeAndCqRR "Will get inlined into concretizeAt: switch." "AND is very important since it's used to mask all sorts of flags in the jit. We take special care to try to find compact ways to make the masks" <inline: true> | val srcReg dstReg | val := operands at: 0. srcReg := operands at: 1. dstReg := operands at: 2. self rotateable8bitBitwiseImmediate: val ifTrue: [:rot :immediate :invert| self machineCodeAt: 0 put: (invert ifTrue: [self bics: dstReg rn: srcReg imm: immediate ror: rot] ifFalse: [self ands: dstReg rn: srcReg imm: immediate ror: rot]). ^4] ifFalse: [| hb | hb := (operands at: 0) highBit. "First see if the constant can be made from a simple shift of 0xFFFFFFFF" 1 << hb = (val +1) ifTrue: "MVN temp reg, 0, making 0xffffffff" [self machineCodeAt: 0 put:(self mvn: ConcreteIPReg imm: 0 ror: 0). "Then AND reg, temp reg, lsr #(32-hb)" self machineCodeAt: 4 put: (self dataOpType: AndOpcode rd: dstReg rn: srcReg rm: ConcreteIPReg lsr: 32 - hb). + ^8]]. + ^self concretizeDataOperationCwR: AndOpcode R: dstReg! - ^8]. - ^self concretizeDataOperationCwR: AndOpcode R: dstReg]! Item was changed: ----- Method: TAssignmentNode>>bindVariablesIn: (in category 'transformations') ----- bindVariablesIn: aDictionary + self setVar: (variable bindVariablesIn: aDictionary) + exp: (expression bindVariablesIn: aDictionary)! - variable := variable bindVariablesIn: aDictionary. - expression := expression bindVariablesIn: aDictionary.! Item was changed: ----- Method: TAssignmentNode>>emitStatementListExpansion:on:level:generator: (in category 'C code generation') ----- emitStatementListExpansion: stmtList on: aStream level: level generator: aCodeGen + self deny: (aCodeGen hasVariable: variable name). stmtList statements last = variable ifTrue: [^expression emitCCodeOn: aStream level: level generator: aCodeGen]. + (stmtList copy transformInAssignmentTo: variable codeGen: aCodeGen) - (stmtList copy transformInAssignmentTo: variable) emitCCodeOn: aStream level: level generator: aCodeGen! Item was changed: ----- Method: TAssignmentNode>>emitStatementListExpansionAsExpression:on:level:generator: (in category 'C code generation') ----- emitStatementListExpansionAsExpression: stmtList on: aStream level: level generator: aCodeGen + self deny: (aCodeGen hasVariable: variable name). stmtList statements last = variable ifTrue: [^expression emitCCodeAsExpressionOn: aStream level: level generator: aCodeGen]. + (stmtList copy transformInAssignmentTo: variable codeGen: aCodeGen) - (stmtList copy transformInAssignmentTo: variable) emitCCodeAsExpressionOn: aStream level: level generator: aCodeGen! Item was changed: ----- Method: TAssignmentNode>>emitValueExpansionOn:level:generator: (in category 'C code generation') ----- emitValueExpansionOn: aStream level: level generator: aCodeGen | stmtList lastStmt copiedStatements | self assert: (expression isSend and: [expression isValueExpansion]). stmtList := expression receiver. lastStmt := stmtList statements last. + self deny: (aCodeGen hasVariable: variable name). (lastStmt = variable or: [lastStmt isReturn]) ifTrue: [^expression emitCCodeOn: aStream level: level generator: aCodeGen]. + copiedStatements := stmtList copy transformInAssignmentTo: variable codeGen: aCodeGen. - copiedStatements := stmtList copy transformInAssignmentTo: variable. expression copy receiver: copiedStatements; emitCCodeOn: aStream level: level generator: aCodeGen! Item was changed: ----- Method: TAssignmentNode>>isSameAs: (in category 'testing') ----- isSameAs: aTParseNode + ^self == aTParseNode + or: [aTParseNode isAssignment + and: [(variable isSameAs: aTParseNode variable) + and: [expression isSameAs: aTParseNode expression]]]! - ^aTParseNode isAssignment - and: [(variable isSameAs: aTParseNode variable) - and: [expression isSameAs: aTParseNode expression]]! Item was changed: ----- Method: TAssignmentNode>>postCopy (in category 'copying') ----- postCopy + self setVar: variable copy exp: expression copy! - variable := variable copy. - expression := expression copy! Item was changed: ----- Method: TAssignmentNode>>replaceNodesIn: (in category 'transformations') ----- replaceNodesIn: aDictionary + ^aDictionary + at: self + ifAbsent: + [self setVar: (variable replaceNodesIn: aDictionary) + exp: (expression replaceNodesIn: aDictionary)]! - ^aDictionary at: self ifAbsent: [ - variable := variable replaceNodesIn: aDictionary. - expression := expression replaceNodesIn: aDictionary. - self]! Item was added: + ----- Method: TAssignmentNode>>setVar:exp: (in category 'private') ----- + setVar: varNode exp: expressionNode + "This is a private setter, just for breakpointing..." + variable := varNode. + expression := expressionNode! Item was changed: ----- Method: TAssignmentNode>>setVariable:expression: (in category 'accessing') ----- setVariable: varNode expression: expressionNode self assert: (expressionNode isGoTo or: [expressionNode isLabel]) not. + variable := varNode. + expression := expressionNode! - expression := expressionNode.! Item was removed: - ----- Method: TAssignmentNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "Avoid transforming: - x := expression - into: - x := x := expression" - - aTVariableNode = variable ifTrue: [^self]. - ^super transformInAssignmentTo: aTVariableNode! Item was added: + ----- Method: TAssignmentNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: aCodeGen + "Avoid transforming: + x := expression + into: + x := x := expression" + + aTVariableNode = variable ifTrue: [^self]. + ^super transformInAssignmentTo: aTVariableNode codeGen: aCodeGen! Item was changed: ----- Method: TCaseStmtNode>>processSharedCodeBlocks:forCase:in:method:expandedCases: (in category 'transformations') ----- + processSharedCodeBlocks: caseTree forCase: caseIndex in: aCodeGen method: aTMethod expandedCases: seen - processSharedCodeBlocks: caseTree forCase: caseIndex in: codeGen method: aTMethod expandedCases: seen "Process any shared code blocks in the case parse tree for the given case, either inlining them or making them a 'goto sharedLabel'." | caseMethod map meth sharedNode exitLabel | exitLabel := nil. "caseTree is expected to be a TStmtListNode whose first element is a comment and whose second element is a TInlineNode for a method." caseMethod := caseTree statements second method. [sharedNode := nil. map := IdentityDictionary new. caseTree nodesDo: [:node| (sharedNode isNil and: [node isSend + and:[(meth := aCodeGen methodNamed: node selector) notNil - and:[(meth := codeGen methodNamed: node selector) notNil and:[meth sharedCase notNil]]]) ifTrue: [(meth sharedCase = (meth sharedCase isSymbol ifTrue: [caseMethod selector] ifFalse: [caseIndex]) and: [(seen includes: meth sharedLabel) not]) ifTrue: ["If the bytecode (the caseMethod) ends with a message that has a lastCase (and lastLabel) then that will be converted into a goto and control will continue to that code, If the bytecode does /not/ end with a message that has a lastCase (and lastLabel) then control should not continue to that shared case. expandViaFallThrough captures this, true for the former, false for the latter." | expandViaFallThrough | expandViaFallThrough := false. caseMethod statements last isSend ifTrue: + [(aCodeGen methodNamed: caseMethod statements last selector) ifNotNil: - [(codeGen methodNamed: caseMethod statements last selector) ifNotNil: [:m| expandViaFallThrough := m sharedCase notNil]]. seen add: meth sharedLabel. map at: node put: (expandViaFallThrough ifTrue: [sharedNode := meth. TLabeledCommentNode new setComment: 'goto ', meth sharedLabel] ifFalse: ["Still need recursive expansjon to continue but don't want to duplicate the node, so substitue an empty method." sharedNode := TLabeledCommentNode new setComment: 'null '. meth copy renameLabelsForInliningInto: aTMethod; addLabelsTo: aTMethod; asInlineNode])] ifFalse: [map at: node put: (TGoToNode new setLabel: meth sharedLabel)]]]. caseTree replaceNodesIn: map. "recursively expand" sharedNode notNil] whileTrue: [sharedNode isTMethod ifTrue: [meth := sharedNode copy. meth hasReturn ifTrue: [exitLabel ifNil: [exitLabel := aTMethod unusedLabelForInliningInto: aTMethod. aTMethod labels add: exitLabel]. + meth exitVar: nil label: exitLabel in: aCodeGen]. - meth exitVar: nil label: exitLabel]. meth renameLabelsForInliningInto: aTMethod; addLabelsTo: aTMethod. caseTree setStatements: (caseTree statements copyWith: meth asInlineNode)]]. exitLabel ifNotNil: [caseTree setStatements: (caseTree statements copyWith: (TLabeledCommentNode new setLabel: exitLabel comment: 'end case'))]! Item was removed: - ----- Method: TCaseStmtNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "Destructively transform the receiver so that each case is transformed into an assignment." - cases := cases collect: [:node | node copy transformInAssignmentTo: aTVariableNode]. - otherwiseOrNil := otherwiseOrNil isNil - ifTrue: [TStmtListNode new setArguments: #() statements: - {TSendNode new setSelector: #error - receiver: (TConstantNode new setValue: 'Case not found') - arguments: #()}] - ifFalse: [otherwiseOrNil copy transformInAssignmentTo: aTVariableNode]. - ^self! Item was added: + ----- Method: TCaseStmtNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: aCodeGen + "Destructively transform the receiver so that each case is transformed into an assignment." + + cases := cases collect: [:node | node copy transformInAssignmentTo: aTVariableNode codeGen: aCodeGen]. + otherwiseOrNil := otherwiseOrNil + ifNil: + [TStmtListNode new + setArguments: #() + statements: + {(TSendNode new + setSelector: #error + receiver: (TConstantNode new setValue: 'Case not found') arguments: #())}] + ifNotNil: + [otherwiseOrNil copy transformInAssignmentTo: aTVariableNode codeGen: aCodeGen]! Item was changed: ----- Method: TConstantNode>>isSameAs: (in category 'comparing') ----- isSameAs: aTParseNode + ^self == aTParseNode + or: [aTParseNode isConstant + and: [value class == aTParseNode value class + and: [value = aTParseNode value]]]! - ^aTParseNode isConstant - and: [value class == aTParseNode value class - and: [value = aTParseNode value]]! Item was changed: ----- Method: TDefineNode>>isSameAs: (in category 'comparing') ----- isSameAs: aTParseNode + ^self == aTParseNode + or: [self class == aTParseNode class + and: [value class == aTParseNode value class + and: [value = aTParseNode value + and: [name = aTParseNode nameOrValue]]]]! - ^self class == aTParseNode class - and: [value class == aTParseNode value class - and: [value = aTParseNode value - and: [name = aTParseNode nameOrValue]]]! Item was added: + ----- Method: TLabeledCommentNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: anObject + "These nodes should *not* be transformed." + ^self! Item was changed: ----- Method: TMethod>>deny: (in category 'error handling') ----- deny: aBooleanOrBlock - <doNotGenerate> aBooleanOrBlock value ifTrue: [AssertionFailure signal: 'Assertion failed']! Item was changed: ----- Method: TMethod>>ensureConditionalAssignmentsAreTransformedIn: (in category 'inlining') ----- ensureConditionalAssignmentsAreTransformedIn: aCodeGen "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 newTree | - | transformedAssignments | [transformedAssignments := Dictionary new. parseTree nodesDo: [:node| (self transformConditionalAssignment: node in: aCodeGen) ifNotNil: [:replacement| transformedAssignments 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]]]. + transformedAssignments isEmpty + or: [(newTree := parseTree replaceNodesIn: transformedAssignments) isSameAs: parseTree]] whileFalse: + [self deny: newTree printString = parseTree printString. + parseTree := newTree] - transformedAssignments notEmpty - and: [self replaceNodesIn: transformedAssignments. - true]] whileTrue ! Item was removed: - ----- 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 := 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" - 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]. - "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 added: + ----- Method: TMethod>>exitVar:label:in: (in category 'inlining') ----- + exitVar: exitVar label: exitLabel in: aCodeGen + "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 := 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]] + in: aCodeGen. + "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" + 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]. + "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 changed: ----- Method: TMethod>>inlineCaseStatementBranchesIn:localizingVars: (in category 'inlining') ----- inlineCaseStatementBranchesIn: aCodeGen localizingVars: varsList | maxTemp usedVars v exitLabel | maxTemp := 0. parseTree nodesDo: [:n | n isCaseStmt ifTrue: [n cases do: [:stmtNode | | newStatements stmt meth | (stmt := stmtNode statements first) isSend ifTrue: [(meth := (aCodeGen methodNamed: stmt selector)) isNil ifFalse: [(meth hasUnrenamableCCode or: [meth args notEmpty]) ifFalse: [meth := meth copy. meth hasReturn ifTrue: [exitLabel := meth unusedLabelForInliningInto: self. + meth exitVar: nil label: exitLabel in: aCodeGen. - meth exitVar: nil label: exitLabel. labels add: exitLabel] ifFalse: [exitLabel := nil]. meth renameLabelsForInliningInto: self. labels addAll: meth labels. newStatements := stmtNode statements asOrderedCollection allButFirst. exitLabel ifNotNil: [newStatements addFirst: (TLabeledCommentNode new setLabel: exitLabel comment: 'end case')]. newStatements addFirst: meth asInlineNode; addFirst: (TLabeledCommentNode new setComment: meth selector). stmtNode setStatements: newStatements]]]]]]. usedVars := (locals , args) asSet. 1 to: maxTemp do: [:i | v := 't' , i printString. (usedVars includes: v) ifTrue: [self error: 'temp variable name conflicts with an existing local or arg']. locals addLast: v]. "make local versions of the given globals" locals addAll: (varsList reject: [:var | usedVars includes: var])! Item was changed: ----- Method: TMethod>>inlineSend:directReturn:exitVar:in: (in category 'inlining') ----- inlineSend: aSendNode directReturn: directReturn exitVar: exitVar in: aCodeGen "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 meth methArgs exitLabel inlineStmts label exitType elidedArgs | sel := aSendNode selector. meth := aCodeGen methodNamed: sel. methArgs := meth args. "convenient for debugging..." aCodeGen maybeBreakForInlineOf: aSendNode in: self. elidedArgs := #(). (methArgs notEmpty and: [methArgs first beginsWith: 'self_in_']) ifTrue: "If the first arg is not used we can and should elide it." [| varNode | varNode := TVariableNode new setName: methArgs first. (meth parseTree noneSatisfy: [:node| varNode isSameAs: node]) ifTrue: [elidedArgs := {methArgs first}]. methArgs := methArgs allButFirst]. methArgs size = aSendNode args size ifFalse: [^nil]. meth := meth copy. (meth statements size > 1 and: [meth statements first isSend and: [meth statements first selector == #flag:]]) ifTrue: [meth statements removeFirst]. "Propagate the return type of an inlined method" (directReturn or: [exitVar notNil]) ifTrue: [exitType := directReturn ifTrue: [returnType] ifFalse: [(self typeFor: exitVar in: aCodeGen) ifNil: [#sqInt]]. (exitType = #void or: [exitType = meth returnType]) ifFalse: [meth propagateReturnIn: aCodeGen]]. "Propagate any unusual argument types to untyped argument variables" methArgs with: aSendNode args do: [:formal :actual| (meth declarationAt: formal ifAbsent: nil) ifNil: [(self typeFor: actual in: aCodeGen) ifNotNil: [:type| type ~= #sqInt ifTrue: [meth declarationAt: formal put: (type last = $* ifTrue: [type, formal] ifFalse: [type, ' ', formal])]]]]. meth renameVarsForInliningInto: self except: elidedArgs in: aCodeGen. meth renameLabelsForInliningInto: self. self addVarsDeclarationsAndLabelsOf: meth except: elidedArgs. meth hasReturn ifTrue: [directReturn ifFalse: [exitLabel := self unusedLabelForInliningInto: self. + (meth exitVar: exitVar label: exitLabel in: aCodeGen) "is label used?" - (meth exitVar: exitVar label: exitLabel) "is label used?" ifTrue: [ labels add: exitLabel ] ifFalse: [ exitLabel := nil ]]]. (inlineStmts := OrderedCollection new: meth statements size + meth args size + 2) add: (label := TLabeledCommentNode new setComment: 'begin ', sel); addAll: (self argAssignmentsFor: meth send: aSendNode except: elidedArgs in: aCodeGen); addAll: meth statements. "method body" directReturn ifTrue: [meth endsWithReturn ifTrue: [exitVar ifNotNil: "don't remove the returns if being invoked in the context of a return" [inlineStmts at: inlineStmts size put: inlineStmts last copyWithoutReturn]] ifFalse: [inlineStmts add: (TReturnNode new setExpression: (TVariableNode new setName: 'nil'))]]. exitLabel ifNotNil: [inlineStmts add: (TLabeledCommentNode new setLabel: exitLabel comment: 'end ', meth selector)]. inlineStmts size = 1 ifTrue: "Nuke empty methods; e.g. override of flushAtCache" [self assert: inlineStmts first isComment. inlineStmts removeFirst]. ^inlineStmts! Item was changed: ----- Method: TMethod>>transformConditionalAssignment:in: (in category 'inlining') ----- transformConditionalAssignment: node in: aCodeGen "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. Also apply to various C constructs like switch" ^(node isAssignment and: [node expression mustTransformWhenAssignedIn: aCodeGen]) ifTrue: + [node expression copy transformInAssignmentTo: node variable codeGen: aCodeGen]! - [node expression copy transformInAssignmentTo: node variable]! Item was removed: - ----- 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: [expr transformInAssignmentTo: (TVariableNode new setName: exitVar)]]. - 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 added: + ----- Method: TMethod>>transformReturnSubExpression:toAssignmentOf:andGoto:unless:into:in: (in category 'inlining') ----- + transformReturnSubExpression: node toAssignmentOf: exitVar andGoto: exitLabel unless: eliminateReturnSelfs into: aBinaryBlock in: aCodeGen + | 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: [expr transformInAssignmentTo: (TVariableNode new setName: exitVar) codeGen: aCodeGen]]. + 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 added: + ----- Method: TParseNode>>deny: (in category 'as yet unclassified') ----- + deny: aBooleanOrBlock + aBooleanOrBlock value ifTrue: [AssertionFailure signal: 'Assertion failed']! Item was changed: ----- Method: TParseNode>>isSameAs: (in category 'comparing') ----- isSameAs: aTParseNode "Answer if the ParseTree rooted at this node is the same as aTParseNode. By default answer false and have subclasses override as appropriate." + ^self == aTParseNode! - ^false! Item was removed: - ----- Method: TParseNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "Default behavior is to transform an expression into an assignement - var := expression. - This message has to be redefined in subclasses which are not rvalues." - ^TAssignmentNode new - setVariable: aTVariableNode - expression: self! Item was added: + ----- Method: TParseNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: anObject + "Default behavior is to transform an expression into an assignement + var := expression. + This message has to be redefined in subclasses which are not rvalues." + ^TAssignmentNode new + setVariable: aTVariableNode + expression: self! Item was added: + ----- Method: TReturnNode>>isSameAs: (in category 'comparing') ----- + isSameAs: aTParseNode + ^self == aTParseNode + or: [aTParseNode isReturn + and: [expression isSameAs: aTParseNode expression]]! Item was removed: - ----- Method: TReturnNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "a return shall not be assigned: - x := condition ifTrue: [^nil] ifFalse: [2] - shall be transformed into: - condition ifTrue: [^nil] ifFalse: [x := 2]" - - ^self! Item was added: + ----- Method: TReturnNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: anObject + "a return shall not be assigned: + x := condition ifTrue: [^nil] ifFalse: [2] + shall be transformed into: + condition ifTrue: [^nil] ifFalse: [x := 2]" + + ^self! Item was changed: ----- Method: TSendNode>>isSameAs: (in category 'comparing') ----- isSameAs: aTParseNode + self == aTParseNode ifTrue: [^true]. (aTParseNode isSend and: [selector == aTParseNode selector and: [receiver isSameAs: aTParseNode receiver]]) ifFalse: [^false]. arguments with: aTParseNode args do: [:a :b| (a isSameAs: b) ifFalse: [^false]]. ^true! Item was removed: - ----- Method: TSendNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "transform a conditional: - condition ifTrue: [stmt1. stmt2] ifFalse: [stmt3. stmt4]. - into: - condition ifTrue: [stmt1. var := stmt2] ifFalse: [stmt3. var := stmt4]. - If the last expression is itself not an rvalue, it will be transformed recursively" - - self isConditionalSend ifTrue: [^self copy - arguments: - (self args collect: - [:stmtList| stmtList copy transformInAssignmentTo: aTVariableNode]); - yourself]. - "don't attempt to assign aTVariableNode with the error condition (like default switch missing)" - selector = #error ifTrue: [^self]. - ^super transformInAssignmentTo: aTVariableNode! Item was added: + ----- Method: TSendNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: aCodeGen + "transform a conditional: + condition ifTrue: [stmt1. stmt2] ifFalse: [stmt3. stmt4]. + into: + condition ifTrue: [stmt1. var := stmt2] ifFalse: [stmt3. var := stmt4]. + Iff any of the arms is returning, since the assignment may be eliminated in that case, + or any of the arms must be transformed because it is not an r-value." + + selector = #error ifTrue: [^self]. + (self isConditionalSend + and: [arguments anySatisfy: [:arg | (arg isAnRValueIn: aCodeGen) not or: [arg endsWithReturn]]]) ifTrue: + [^self copy + arguments: (arguments collect: [ :stmtList | stmtList copy transformInAssignmentTo: aTVariableNode codeGen: aCodeGen]); + yourself]. "don't attempt to assign aTVariableNode with the error condition (like default switch missing)" + ^super transformInAssignmentTo: aTVariableNode codeGen: aCodeGen! Item was changed: ----- Method: TStmtListNode>>isSameAs: (in category 'testing') ----- isSameAs: aTParseNode + self == aTParseNode ifTrue: [^true]. (aTParseNode isStmtList and: [statements size = aTParseNode statements size]) ifFalse: [^false]. statements with: aTParseNode statements do: [:mine :theirs| (mine isSameAs: theirs) ifFalse: [^false]]. ^true! Item was removed: - ----- Method: TStmtListNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "Destructively transform the receiver so that its last expression is assigned to the argument." - | index | - index := statements findLast: [:expr| (expr isGoTo or: [expr isLabel]) not]. - statements - at: index - put: ((statements at: index) copy transformInAssignmentTo: aTVariableNode). - ^self! Item was added: + ----- Method: TStmtListNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: aCodeGen + "Destructively transform the receiver so that its last expression is assigned to the argument." + | index | + index := statements findLast: [:expr| (expr isGoTo or: [expr isLabel]) not]. + statements + at: index + put: ((statements at: index) copy transformInAssignmentTo: aTVariableNode codeGen: aCodeGen)! Item was removed: - ----- Method: TSwitchStmtNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "Destructively transform the receiver so that each case is transformed into an assignment." - cases := cases collect: [:pair | {pair first. pair last copy transformInAssignmentTo: aTVariableNode}]. - otherwiseOrNil := otherwiseOrNil isNil - ifTrue: [TStmtListNode new setArguments: #() statements: - {TSendNode new setSelector: #error - receiver: (TConstantNode new setValue: 'Case not found and no otherwise clause') - arguments: #()}] - ifFalse: [otherwiseOrNil copy transformInAssignmentTo: aTVariableNode]. - ^self! Item was added: + ----- Method: TSwitchStmtNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: aCodeGen + "Destructively transform the receiver so that each case is transformed into an assignment." + + cases := cases collect: [:pair | {pair first. pair last copy transformInAssignmentTo: aTVariableNode codeGen: aCodeGen}]. + otherwiseOrNil := otherwiseOrNil + ifNil: [TStmtListNode new setArguments: #() statements: + {TSendNode new setSelector: #error + receiver: (TConstantNode new setValue: 'Case not found and no otherwise clause') + arguments: #()}] + ifNotNil: [otherwiseOrNil copy transformInAssignmentTo: aTVariableNode codeGen: aCodeGen]! Item was changed: ----- Method: TVariableNode>>isSameAs: (in category 'comparing') ----- isSameAs: aTParseNode + ^self == aTParseNode + or: [aTParseNode isVariable + and: [name = aTParseNode name]]! - ^aTParseNode isVariable - and: [name = aTParseNode name]! Item was removed: - ----- Method: TVariableNode>>transformInAssignmentTo: (in category 'transformations') ----- - transformInAssignmentTo: aTVariableNode - "Avoid transforming: - x - into: - x := x" - - aTVariableNode = self ifTrue: [^self]. - ^super transformInAssignmentTo: aTVariableNode! Item was added: + ----- Method: TVariableNode>>transformInAssignmentTo:codeGen: (in category 'transformations') ----- + transformInAssignmentTo: aTVariableNode codeGen: aCodeGen + "Avoid transforming: + x + into: + x := x" + + aTVariableNode = self ifTrue: [^self]. + ^super transformInAssignmentTo: aTVariableNode codeGen: aCodeGen! |
Free forum by Nabble | Edit this page |