VM Maker Inbox: VMMaker.oscog-eem.2764.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

VM Maker Inbox: VMMaker.oscog-eem.2764.mcz

commits-2
 
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!