The Trunk: Compiler-eem.345.mcz

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

The Trunk: Compiler-eem.345.mcz

commits-2
Eliot Miranda uploaded a new version of Compiler to project The Trunk:
http://source.squeak.org/trunk/Compiler-eem.345.mcz

==================== Summary ====================

Name: Compiler-eem.345
Author: eem
Time: 5 April 2017, 11:10:14.91524 pm
UUID: 8ee5b263-f80b-47b2-86c3-1fc462750b6b
Ancestors: Compiler-eem.342

Have generate:using: use the new generation method that lives in BytecodeEncoder.
Recategorize all "code generation (closures)" methods as "closure analysis".

=============== Diff against Compiler-eem.342 ===============

Item was changed:
+ ----- Method: AssignmentNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: AssignmentNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>"  rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  "N.B.  since assigment happens _after_ the value is evaluated the value is sent the message _first_."
  value analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools.
  variable beingAssignedToAnalyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools!

Item was changed:
+ ----- Method: BlockNode>>actualScope (in category 'closure analysis') -----
- ----- Method: BlockNode>>actualScope (in category 'code generation (closures)') -----
  actualScope
  "Answer the actual scope for the receiver.  If this is an unoptimized block then it is its
  actual scope, but if this is an optimized block then the actual scope is some outer block."
  ^actualScopeIfOptimized ifNil: [self]!

Item was changed:
+ ----- Method: BlockNode>>addHoistedTemps: (in category 'closure analysis') -----
- ----- Method: BlockNode>>addHoistedTemps: (in category 'code generation (closures)') -----
  addHoistedTemps: additionalTemporaries "<SequenceableCollection>"
  | tempsToBeMerged additionalTempsToAdd |
  additionalTemporaries do:
  [:temp|
  temp definingScope ifNil:
  [temp definingScope: self]].
  (temporaries isNil or: [temporaries isEmpty]) ifTrue:
  [temporaries := additionalTemporaries copy.
  ^self].
  tempsToBeMerged := additionalTemporaries select:
  [:t|
  t isBlockArg
  and: [temporaries anySatisfy: [:existing| existing isBlockArg and: [existing key = t key]]]].
  additionalTempsToAdd := tempsToBeMerged isEmpty
  ifTrue: [additionalTemporaries copy]
  ifFalse: [additionalTemporaries reject: [:temp| tempsToBeMerged identityIncludes: temp]].
  temporaries := (temporaries isNil or: [temporaries isEmpty])
  ifTrue: [additionalTempsToAdd]
  ifFalse:
  [temporaries last isIndirectTempVector
  ifTrue: [temporaries allButLast, additionalTempsToAdd, { temporaries last }]
  ifFalse: [temporaries, additionalTempsToAdd]].
  tempsToBeMerged do:
  [:t| | merge |
  merge := temporaries detect: [:existing| existing isBlockArg and: [existing key = t key]].
  merge absorbHoistedTemp: t]!

Item was changed:
+ ----- Method: BlockNode>>addRemoteTemp:rootNode: (in category 'closure analysis') -----
- ----- Method: BlockNode>>addRemoteTemp:rootNode: (in category 'code generation (closures)') -----
  addRemoteTemp: aTempVariableNode rootNode: rootNode "<MethodNode>"
  "Add aTempVariableNode to my actualScope's sequence of
  remote temps.  If I am an optimized block then the actual
  scope is my actualScopeIfOptimized, otherwise it is myself."
  remoteTempNode == nil ifTrue:
  [remoteTempNode := RemoteTempVectorNode new
  name: self remoteTempNodeName
  index: arguments size + temporaries size
  type: LdTempType
  scope: 0.
  actualScopeIfOptimized
  ifNil:
  [self addTempNode: remoteTempNode.
  remoteTempNode definingScope: self]
  ifNotNil: [actualScopeIfOptimized addHoistedTemps: { remoteTempNode }]].
  remoteTempNode addRemoteTemp: aTempVariableNode encoder: rootNode encoder.
  "use remove:ifAbsent: because the deferred analysis for optimized
  loops can result in the temp has already been hoised into the root."
  self removeTempNode: aTempVariableNode ifAbsent: [
  self actualScope removeTempNode: aTempVariableNode ifAbsent: ["should not happen"]].
  ^remoteTempNode!

Item was changed:
+ ----- Method: BlockNode>>addTempNode: (in category 'closure analysis') -----
- ----- Method: BlockNode>>addTempNode: (in category 'code generation (closures)') -----
  addTempNode: aTempVariableNode
  "Utilities for when we want to add some temporaries."
 
  self makeTemporariesRemovable.
  ^temporaries add: aTempVariableNode!

Item was changed:
+ ----- Method: BlockNode>>analyseArguments:temporaries:rootNode: (in category 'closure analysis') -----
- ----- Method: BlockNode>>analyseArguments:temporaries:rootNode: (in category 'code generation (closures)') -----
  analyseArguments: methodArguments temporaries: methodTemporaries rootNode: rootNode "<MethodNode>" "^<Sequence of: <TempVarNade>>"
  "Top level entry-point for analysing temps within the hierarchy of blocks in the receiver's method.
  Answer the (possibly modified) sequence of temp vars.
  Need to hoist temps out of macro-optimized blocks into their actual blocks.
  Need to note reads and writes to temps from blocks other than their actual blocks to determine
  whether blocks can be local (simple slots within a block/method context) or remote (slots in
  indirection vectors that are shared between contexts by sharing indirection vectors).
 
  The algorithm is based on numbering temporary reads and writes and block extents.
  The index used for numbering starts at zero and is incremented on every block entry
  and block exit.  So the following
  | a b blk r1 r2 t |
  a := 1. b := 2. t := 0.
  blk := [ | s | s := a + b. t := t + s].
  r1 := blk value.
  b := -100.
  r2 := blk value.
  r1 -> r2 -> t
  is numbered as
  method block 0 to: 6:
  | a b blk r1 r2 t |
  a w@1 := 1. b w@1 := 2. t w@1 := 0.
  blk w@5 := [entry@2 | s |
  t  w@3 := t r@3 + a r@3 + b r@3
  ] exit@4.
  r1 w@5 := blk r@5 value.
  b w@5 := nil.
  r2 w@5 := blk r@5 value.
  r1 r@5 -> r2 r@5 -> t r@5
  So:
  b and blk cannot be copied because for both there exists a write @5 that follows a
  read @4 within block 2 through 4
  t must be remote because there exists a write @3 within block (2 to: 4)
  Complications are introduced by optimized blocks.  In the following temp is written to
  after it is closed over by [ temp ] since the inlined block is executed more than once.
  | temp coll |
  coll := OrderedCollection new.
  1 to: 5 do: [ :index |
  temp := index.
  coll add: [ temp ] ].
  self assert: (coll collect: [:ea| ea value]) asArray = #(5 5 5 5 5)
  In the following i is local to the block and must be initialized each time around the loop
  but if the block is inlined it must be declared at method level.
  | col |
  col := OrderedCollection new.
  1 to: 3 do: [ :each | | i | i := each. col add: [ i ]. i := i + 1 ].
  self assert: (col collect: [ :each | each value ]) asArray = #(2 3 4)"
  self assert: (arguments isEmpty or: [arguments hasEqualElements: methodArguments]).
  arguments := methodArguments asArray. "won't change"
  self assert: (temporaries isNil or: [temporaries isEmpty or: [temporaries hasEqualElements: methodTemporaries]]).
  temporaries := OrderedCollection withAll: methodTemporaries.
 
  self assert: optimized not. "the top-level block should not be optimized."
  self analyseTempsWithin: self rootNode: rootNode assignmentPools: Dictionary new.
 
  "The top-level block needs to reindex temporaries since analysis may have rearranged them.
  This happens when temps are made remote and/or a remote node is added."
  temporaries withIndexDo:
  [:temp :offsetPlusOne| temp index: arguments size + offsetPlusOne - 1].
 
  "Answer the (possibly modified) sequence of temps."
  ^temporaries asArray!

Item was changed:
+ ----- Method: BlockNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: BlockNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  | effectiveScope blockStart |
  effectiveScope := optimized
  ifTrue: [actualScopeIfOptimized := scopeBlock]
  ifFalse: [self].
 
  arguments ifNotNil:
  [arguments do: [:temp| temp definingScope: self]].
  temporaries ifNotNil:
  [temporaries do: [:temp| temp definingScope: self]].
 
  optimized ifFalse: "if optimized this isn't an actual scope"
  [rootNode noteBlockEntry:
  [:entryNumber|
  blockExtent := (blockStart := entryNumber) to: 0]].
 
  "Need to enumerate a copy because closure analysis can add a statement
  via ifHasRemoteTempNodeEnsureInitializationStatementExists:."
  statements copy do:
  [:statement|
  statement analyseTempsWithin: effectiveScope rootNode: rootNode assignmentPools: assignmentPools].
 
  optimized
  ifTrue: "if optimized loop need to add nils for any temps read before written"
  [optimizedMessageNode isOptimizedLoop ifTrue:
  [self nilReadBeforeWrittenTemps]]
  ifFalse: "if optimized this isn't an actual scope"
  [rootNode noteBlockExit:
  [:exitNumber|
  blockExtent := blockStart to: exitNumber]].
 
  "Now that the analysis is done move any temps that need to be moved."
  self postNumberingProcessTempsWithin: effectiveScope rootNode: rootNode.
 
  "This is simply a nicety for compiler developers..."
  temporaries do:
  [:temp|
  (temp isIndirectTempVector and: [temp name includes: $?]) ifTrue:
  [temp name: temp definingScope remoteTempNodeName]]!

Item was changed:
+ ----- Method: BlockNode>>blockExtent (in category 'closure analysis') -----
- ----- Method: BlockNode>>blockExtent (in category 'code generation (closures)') -----
  blockExtent "^<Interval>"
  ^blockExtent!

Item was changed:
+ ----- Method: BlockNode>>computeCopiedValues: (in category 'closure analysis') -----
- ----- Method: BlockNode>>computeCopiedValues: (in category 'code generation (closures)') -----
  computeCopiedValues: rootNode
  | referencedValues |
  referencedValues := rootNode referencedValuesWithinBlockExtent: blockExtent.
  ^(referencedValues reject: [:temp| temp isDefinedWithinBlockExtent: blockExtent])
  asArray sort: ParseNode tempSortBlock!

Item was changed:
+ ----- Method: BlockNode>>deoptimize (in category 'closure analysis') -----
- ----- Method: BlockNode>>deoptimize (in category 'code generation (closures)') -----
  deoptimize
  optimized := false.
  optimizedMessageNode := nil!

Item was changed:
+ ----- Method: BlockNode>>ifHasRemoteTempNodeEnsureInitializationStatementExists: (in category 'closure analysis') -----
- ----- Method: BlockNode>>ifHasRemoteTempNodeEnsureInitializationStatementExists: (in category 'code generation (closures)') -----
  ifHasRemoteTempNodeEnsureInitializationStatementExists: rootNode
  "If a remoteTempNode has been added ensure a statement exists to initialize it."
  remoteTempNode ~~ nil ifTrue:
  [(statements notEmpty
   and: [statements first isAssignmentNode
   and: [statements first variable isTemp
   and: [statements first variable isIndirectTempVector]]])
  ifTrue: "If this is a decompiled tree, or if a temporary has been added later in
  the analysis then there already is a temp vector initialization node."
  [(statements first variable ~~ remoteTempNode) ifTrue:
  [statements first variable become: remoteTempNode].
  statements first value numElements: remoteTempNode remoteTemps size]
  ifFalse:
  [statements addFirst: (remoteTempNode nodeToInitialize: rootNode encoder)]].!

Item was changed:
+ ----- Method: BlockNode>>makeTemporariesRemovable (in category 'closure analysis') -----
- ----- Method: BlockNode>>makeTemporariesRemovable (in category 'code generation (closures)') -----
  makeTemporariesRemovable
  "Utilities for when we want to remove some temporaries."
 
  temporaries isArray ifTrue:
  [temporaries := temporaries asOrderedCollection].!

Item was changed:
+ ----- Method: BlockNode>>nilReadBeforeWrittenTemps (in category 'closure analysis') -----
- ----- Method: BlockNode>>nilReadBeforeWrittenTemps (in category 'code generation (closures)') -----
  nilReadBeforeWrittenTemps
  | visitor readBeforeWritten |
  temporaries isEmpty ifTrue:
  [^self].
  self accept: (visitor := OptimizedBlockLocalTempReadBeforeWrittenVisitor new).
  readBeforeWritten := visitor readBeforeWritten.
  temporaries reverseDo:
  [:temp|
  ((readBeforeWritten includes: temp)
  and: [temp isRemote not]) ifTrue:
  [statements addFirst: (AssignmentNode new variable: temp value: NodeNil)]]!

Item was changed:
+ ----- Method: BlockNode>>noteOptimizedIn: (in category 'closure analysis') -----
- ----- Method: BlockNode>>noteOptimizedIn: (in category 'code generation (closures)') -----
  noteOptimizedIn: anOptimizedMessageNode
  optimized := true.
  optimizedMessageNode := anOptimizedMessageNode!

Item was changed:
+ ----- Method: BlockNode>>optimizedBlockHoistTempsInto: (in category 'closure analysis') -----
- ----- Method: BlockNode>>optimizedBlockHoistTempsInto: (in category 'code generation (closures)') -----
  optimizedBlockHoistTempsInto: scopeBlock "<BlockNode>"
  "This is a No-op for all nodes except non-optimized BlockNodes."
  "Let's assume the special > 0 guard in MessageNode>>analyseTempsWithin:forValue:encoder: is correct.
  Then we can simply hoist our temps up."
  self assert: (arguments isNil or: [arguments size <= 1]).
  (arguments notNil and: [arguments notEmpty]) ifTrue:
  [scopeBlock addHoistedTemps: arguments.
  arguments := #()].
  temporaries notEmpty ifTrue:
  [scopeBlock addHoistedTemps: temporaries.
  temporaries := #()]!

Item was changed:
+ ----- Method: BlockNode>>postNumberingProcessTempsWithin:rootNode: (in category 'closure analysis') -----
- ----- Method: BlockNode>>postNumberingProcessTempsWithin:rootNode: (in category 'code generation (closures)') -----
  postNumberingProcessTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>"
  "A temp can be local (and copied) if it is not written to after it is captured.
  A temp cannot be local if it is written to remotely.
  Need to enumerate a copy of the temporaries because any temps becoming remote
  will be removed from temporaries in analyseClosure: (and a single remote temp node
  will get added)"
  temporaries copy do:
  [:each|
  each isIndirectTempVector ifFalse:
  [each analyseClosure: rootNode]].
 
  "If this is an optimized node we need to hoist temporaries up into the relevant block scope."
  optimized ifTrue:
  [self optimizedBlockHoistTempsInto: scopeBlock].
 
  "Now we may have added a remoteTempNode.  So we need a statement to initialize it."
  self ifHasRemoteTempNodeEnsureInitializationStatementExists: rootNode.
 
  "Now add all arguments and locals to the pool so that copiedValues can be computed during sizing."
  rootNode
  addLocalsToPool: arguments;
  addLocalsToPool: temporaries!

Item was changed:
+ ----- Method: BlockNode>>reindexingLocalsDo:encoder: (in category 'closure analysis') -----
- ----- Method: BlockNode>>reindexingLocalsDo:encoder: (in category 'code generation (closures)') -----
  reindexingLocalsDo: aBlock encoder: encoderOrNil
  "Evaluate aBlock wih arguments, temporaries and copiedValues reindexed for
  their positions within the receiver's block, restoring the correct indices afterwards.
  If encoder is not nil remember the temps for this block's extent."
  | tempIndices result tempsToReindex |
  self assert: copiedValues notNil.
  tempsToReindex := arguments asArray, copiedValues, temporaries.
  tempIndices := tempsToReindex collect: [:temp| temp index].
  tempsToReindex withIndexDo:
  [:temp :newIndex| temp index: newIndex - 1. self assert: temp index + 1 = newIndex].
  encoderOrNil ifNotNil:
  [encoderOrNil noteBlockExtent: blockExtent hasLocals: tempsToReindex].
  result := aBlock ensure:
  ["Horribly pragmatic hack.  The copiedValues will have completely
   unrelated indices within the closure method and sub-method.
   Avoiding the effort of rebinding temps in the inner scope simply
   update the indices to their correct ones during the generation of
   the closure method and restore the indices immedately there-after."
  tempsToReindex with: tempIndices do:
  [:temp :oldIndex| temp index: oldIndex. self assert: temp index = oldIndex]].
  ^result!

Item was changed:
+ ----- Method: BlockNode>>remoteTempNodeName (in category 'closure analysis') -----
- ----- Method: BlockNode>>remoteTempNodeName (in category 'code generation (closures)') -----
  remoteTempNodeName
  "Answer a useful name for a RemoteTempVectorNode in the receiver."
  | prefix scope extent |
  prefix := actualScopeIfOptimized ifNil: ['<'] ifNotNil: [ '<...'].
  scope := self.
  [extent := scope blockExtent.
  extent == nil
  and: [scope actualScope ~~ scope]] whileTrue:
  [scope := scope actualScope].
  ^extent
  ifNil: [prefix, '?-?>']
  ifNotNil:
  [prefix, extent first printString, '-',
  (extent last isZero
  ifTrue: ['?']
  ifFalse: [extent last printString]), '>']!

Item was changed:
+ ----- Method: BlockNode>>removeTempNode:ifAbsent: (in category 'closure analysis') -----
- ----- Method: BlockNode>>removeTempNode:ifAbsent: (in category 'code generation (closures)') -----
  removeTempNode: aTempVariableNode ifAbsent: aBlock
  "Utilities for when we want to remove some temporaries."
 
  self makeTemporariesRemovable.
  ^temporaries remove: aTempVariableNode ifAbsent: aBlock
  !

Item was changed:
+ ----- Method: BraceNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: BraceNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  elements do:
  [:node|
  node analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools]!

Item was changed:
+ ----- Method: CascadeNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: CascadeNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  { receiver }, messages do:
  [:node| node analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools]!

Item was changed:
+ ----- Method: FutureNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: FutureNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  { receiver. futureDelta }, (futureArgs ifNil: [#()]) do:
  [:node|
  node == nil ifFalse:
  [node analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools]]!

Item was changed:
+ ----- Method: LeafNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: LeafNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  "This is a no-op except in TempVariableNode"
  ^self!

Item was changed:
+ ----- Method: MessageNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: MessageNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  "Assignments within optimized loops are tricky.  Because a loop repeats a
  write to a temporary in an optimized loop effectively occurs after the loop.
  To handle this collect the set of temps assigned to in optimized loops and
  add extra writes after traversing the optimized loop constituents."
  | writtenToTemps |
  self isOptimizedLoop ifTrue:
  [{ receiver }, arguments do:
  [:node|
  (node notNil and: [node isBlockNode and: [node optimized]]) ifTrue:
  [assignmentPools at: node put: Set new]]].
  "receiver is nil in cascades"
  receiver == nil ifFalse:
  [receiver analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools].
  arguments do:
  [:node|
  node == nil ifFalse: "last argument of optimized to:do: can be nil"
  [node analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools]].
  "Add assignments representing subsequent iterations
  and redo the closure analysis for the written-to temps."
  self isOptimizedLoop ifTrue:
  [writtenToTemps := Set new.
  { receiver }, arguments do:
  [:node|
  (node notNil and: [node isBlockNode and: [node optimized]]) ifTrue:
  [(assignmentPools removeKey: node) do:
  [:temp|
  temp isBlockArg ifFalse: "ignore added assignments to to:do: loop args"
  [writtenToTemps add: temp.
  temp addWriteWithin: node at: rootNode locationCounter]]]].
  writtenToTemps isEmpty ifFalse:
  [(writtenToTemps sorted: ParseNode tempSortBlock) do:
  [:each| each analyseClosure: rootNode].
  (writtenToTemps collect: [:each| each definingScope]) do:
  [:blockNode|
  blockNode ifHasRemoteTempNodeEnsureInitializationStatementExists: rootNode]]]!

Item was changed:
+ ----- Method: MethodNode>>addLocalsToPool: (in category 'closure analysis') -----
- ----- Method: MethodNode>>addLocalsToPool: (in category 'code generation (closures)') -----
  addLocalsToPool: locals "<Set of: TempVariableNode>"
  localsPool isNil ifTrue:
  [localsPool := IdentitySet new].
  localsPool addAll: locals!

Item was changed:
+ ----- Method: MethodNode>>ensureClosureAnalysisDone (in category 'closure analysis') -----
- ----- Method: MethodNode>>ensureClosureAnalysisDone (in category 'code generation (closures)') -----
  ensureClosureAnalysisDone
  block blockExtent ifNil:
  [temporaries := block analyseArguments: arguments temporaries: temporaries rootNode: self]!

Item was changed:
  ----- Method: MethodNode>>generate:using: (in category 'code generation') -----
  generate: trailer using: aCompiledMethodClass
  "The receiver is the root of a parse tree. Answer an instance of aCompiledMethodClass.
  The argument, trailer, is arbitrary but is typically either the reference to the source code
  that is stored with every CompiledMethod, or an encoding of the method's temporary names."
 
+ | method |
- | primErrNode blkSize nLits locals literals stack header method |
  self generate: trailer
  using: aCompiledMethodClass
  ifQuick:
  [:m |
+  m literalAt: 2 put: encoder associationForClass;
- encoder noteBlockExtent: (0 to: 2) hasLocals: arguments.
- m literalAt: 2 put: encoder associationForClass;
  properties: properties.
+ ^m].
+ method := encoder generateMethodOfClass: aCompiledMethodClass trailer: trailer from: self.
- ^m].
- primErrNode := self primitiveErrorVariableName ifNotNil:
- [encoder fixTemp: self primitiveErrorVariableName].
- self ensureClosureAnalysisDone.
- encoder rootNode: self. "this is for BlockNode>>sizeCodeForClosureValue:"
- blkSize := (block sizeCodeForEvaluatedValue: encoder)
- + (primitive > 0
- ifTrue: [encoder sizeCallPrimitive: primitive]
- ifFalse: [0])
- + (primErrNode
- ifNil: [0]
- ifNotNil:
- [primErrNode
- index: arguments size + temporaries size;
- sizeCodeForStore: encoder "The VM relies on storeIntoTemp: (129)"]).
- locals := arguments, temporaries, (primErrNode ifNil: [#()] ifNotNil: [{primErrNode}]).
- encoder noteBlockExtent: block blockExtent hasLocals: locals.
- header := encoder computeMethodHeaderForNumArgs: arguments size
- numTemps: locals size
- numLits: (nLits := (literals := encoder allLiterals) size)
- primitive: primitive.
- method := trailer
- createMethod: blkSize
- class: aCompiledMethodClass
- header: header.
- 1 to: nLits do: [:lit | method literalAt: lit put: (literals at: lit)].
- encoder streamToMethod: method.
- stack := ParseStack new init.
- primitive > 0 ifTrue:
- [encoder genCallPrimitive: primitive.
- primErrNode ifNotNil:
- [primErrNode emitCodeForStore: stack encoder: encoder]].
- stack position: method numTemps.
- [block emitCodeForEvaluatedValue: stack encoder: encoder]
- on: Error "If an attempt is made to write too much code the method will be asked"
- do: [:ex|  "to grow, and the grow attempt will fail in CompiledMethod class>>#new:"
- ex signalerContext sender method = (CompiledMethod class>>#new:)
- ifTrue: [^self error: 'Compiler code size discrepancy']
- ifFalse: [ex pass]].
- stack position ~= (method numTemps + 1) ifTrue:
- [^self error: 'Compiler stack discrepancy'].
- encoder methodStreamPosition ~= (method size - trailer size) ifTrue:
- [^self error: 'Compiler code size discrepancy'].
- method needsFrameSize: stack size - method numTemps.
  method properties: properties.
  ^method!

Item was changed:
+ ----- Method: MethodNode>>locationCounter (in category 'closure analysis') -----
- ----- Method: MethodNode>>locationCounter (in category 'code generation (closures)') -----
  locationCounter
  ^locationCounter!

Item was changed:
+ ----- Method: MethodNode>>noteBlockEntry: (in category 'closure analysis') -----
- ----- Method: MethodNode>>noteBlockEntry: (in category 'code generation (closures)') -----
  noteBlockEntry: aBlock
  "Evaluate aBlock with the numbering for the block entry."
  locationCounter isNil ifTrue:
  [locationCounter := -1].
  aBlock value: locationCounter + 1.
  locationCounter := locationCounter + 2!

Item was changed:
+ ----- Method: MethodNode>>noteBlockExit: (in category 'closure analysis') -----
- ----- Method: MethodNode>>noteBlockExit: (in category 'code generation (closures)') -----
  noteBlockExit: aBlock
  "Evaluate aBlock with the numbering for the block exit."
  aBlock value: locationCounter + 1.
  locationCounter := locationCounter + 2!

Item was changed:
+ ----- Method: MethodNode>>referencedValuesWithinBlockExtent: (in category 'closure analysis') -----
- ----- Method: MethodNode>>referencedValuesWithinBlockExtent: (in category 'code generation (closures)') -----
  referencedValuesWithinBlockExtent: anInterval
  ^(localsPool select:
  [:temp|
  temp isReferencedWithinBlockExtent: anInterval]) collect:
  [:temp|
  temp isRemote ifTrue: [temp remoteNode] ifFalse: [temp]]!

Item was changed:
+ ----- Method: NewArrayNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: NewArrayNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  "This is a no-op except in TempVariableNode"
  ^self!

Item was changed:
+ ----- Method: RemoteTempVectorNode>>addRemoteTemp:encoder: (in category 'closure analysis') -----
- ----- Method: RemoteTempVectorNode>>addRemoteTemp:encoder: (in category 'code generation (closures)') -----
  addRemoteTemp: aTempVariableNode encoder: encoder
  remoteTemps ifNil:
  [remoteTemps := OrderedCollection new].
  remoteTemps addLast: aTempVariableNode.
  aTempVariableNode referenceScopesAndIndicesDo:
  [:scopeBlock "<BlockNode>" :location "<Integer>"|
  self addReadWithin: scopeBlock at: location]!

Item was changed:
+ ----- Method: RemoteTempVectorNode>>isIndirectTempVector (in category 'closure analysis') -----
- ----- Method: RemoteTempVectorNode>>isIndirectTempVector (in category 'code generation (closures)') -----
  isIndirectTempVector
  ^true!

Item was changed:
+ ----- Method: RemoteTempVectorNode>>referenceScopesAndIndicesDo: (in category 'closure analysis') -----
- ----- Method: RemoteTempVectorNode>>referenceScopesAndIndicesDo: (in category 'code generation (closures)') -----
  referenceScopesAndIndicesDo: aBinaryBlock
  self shouldNotImplement!

Item was changed:
+ ----- Method: ReturnNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: ReturnNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  "Note we could do this:
  scopeBlock ~~ rootNode block ifTrue:
  [scopeBlock noteNonLocalReturn].
  and pass up the flag in <BlockNode>>>analyseTempsWithin:rootNode:
  which may be fast but will also give less information the debugger.
  For now we consider clean blocks a premature optimization."
  self flag: 'consider clean blocks'.
  expr analyseTempsWithin: scopeBlock rootNode: rootNode assignmentPools: assignmentPools!

Item was changed:
+ ----- Method: TempVariableNode>>absorbHoistedTemp: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>absorbHoistedTemp: (in category 'code generation (closures)') -----
  absorbHoistedTemp: aTempVar
  "Collapse aTempVar into the receiver, being sure to update any closure analysis."
  aTempVar copyScopeAccessTo: self.
  aTempVar becomeForward: self!

Item was changed:
+ ----- Method: TempVariableNode>>addReadWithin:at: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>addReadWithin:at: (in category 'code generation (closures)') -----
  addReadWithin: scopeBlock "<BlockNode>" at: location "<Integer>"
  readingScopes ifNil: [readingScopes := Dictionary new].
  (readingScopes at: scopeBlock ifAbsentPut: [Set new]) add: location.
  remoteNode ifNotNil:
  [remoteNode addReadWithin: scopeBlock at: location]!

Item was changed:
+ ----- Method: TempVariableNode>>addWriteWithin:at: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>addWriteWithin:at: (in category 'code generation (closures)') -----
  addWriteWithin: scopeBlock "<BlockNode>" at: location "<Integer>"
  writingScopes ifNil: [writingScopes := Dictionary new].
  (writingScopes at: scopeBlock ifAbsentPut: [Set new]) add: location.
  remoteNode ifNotNil:
  [remoteNode addReadWithin: scopeBlock at: location]!

Item was changed:
+ ----- Method: TempVariableNode>>analyseClosure: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>analyseClosure: (in category 'code generation (closures)') -----
  analyseClosure: rootNode "<MethodNode>"
  "Analyse whether the temporary needs to be made remote
  or not, and answer whether it was made remote.
  A temp cannot be local if it is written to remotely,
  or if it is written to after it is closed-over.  An exception
  is an inlined block argument that appears to be written
  remotely but is actually local to a block."
  | latestWrite |
  self isBlockArg ifTrue: [^false].
  remoteNode ifNotNil: [^false]. "If already remote, don't remote a second time"
  latestWrite := 0.
  ((writingScopes notNil
  and: [writingScopes associations anySatisfy: [:assoc|
  [:blockScope :refs|
  refs do: [:write| latestWrite := write max: latestWrite].
  "A temp cannot be local if it is written to remotely."
  blockScope actualScope ~~ definingScope actualScope]
  value: assoc key value: assoc value]])
  or: [readingScopes notNil
  and: [readingScopes associations anySatisfy: [:assoc|
  [:blockScope :refs|
  "A temp cannot be local if it is written to after it is closed-over."
  blockScope actualScope ~~ definingScope actualScope
  and: [refs anySatisfy: [:read| read < latestWrite]]]
  value: assoc key value: assoc value]]]) ifTrue:
  [remoteNode := definingScope addRemoteTemp: self rootNode: rootNode.
  ^true].
  ^false!

Item was changed:
+ ----- Method: TempVariableNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>analyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  analyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  self addReadWithin: scopeBlock at: rootNode locationCounter!

Item was changed:
+ ----- Method: TempVariableNode>>beingAssignedToAnalyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>beingAssignedToAnalyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  beingAssignedToAnalyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  self addWriteWithin: scopeBlock at: rootNode locationCounter.
  "For analysis of optimized blocks also record the set of temporaries written to
  within optimized blocks so that additional writes can be added at locations that
  represent subsequent iterations of the loop. e.g. testInlineBlockCollectionSD1"
  assignmentPools keysAndValuesDo:
  [:outerScopeBlock :set|
  "definingScope can be nil in expr in expr ifNil: [:arg|...] expressions because
  arg gets its definingScope set when [:arg|...] is analysed."
  outerScopeBlock actualScope
  = (definingScope
  ifNil: [scopeBlock]
  ifNotNil: [definingScope actualScope]) ifTrue:
  [set add: self]]!

Item was changed:
+ ----- Method: TempVariableNode>>copyScopeAccessTo: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>copyScopeAccessTo: (in category 'code generation (closures)') -----
  copyScopeAccessTo: aTempVar
  "For absorbHoistedTemp:, copy the receiver's reads and writes into the record in aTempVar."
  readingScopes ifNotNil:
  [readingScopes keysAndValuesDo:
  [:scopeBlock :reads|
  reads do:
  [:location|
  aTempVar addReadWithin: scopeBlock "<BlockNode>" at: location]]].
  writingScopes ifNotNil:
  [writingScopes keysAndValuesDo:
  [:scopeBlock :writes|
  writes do:
  [:location|
  aTempVar addWriteWithin: scopeBlock "<BlockNode>" at: location]]]!

Item was changed:
+ ----- Method: TempVariableNode>>definingScope (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>definingScope (in category 'code generation (closures)') -----
  definingScope
  ^definingScope!

Item was changed:
+ ----- Method: TempVariableNode>>definingScope: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>definingScope: (in category 'code generation (closures)') -----
  definingScope: scopeBlock "<BlockNode>"
  definingScope = scopeBlock ifTrue: [^ self]. "No need to bail"
  definingScope ifNotNil:
  [self error: 'temp has more than one defining scope.  This is probably a parser error'].
  definingScope := scopeBlock!

Item was changed:
+ ----- Method: TempVariableNode>>index: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>index: (in category 'code generation (closures)') -----
  index: anInteger
  "For renumbering temps in the closure compiler."
  index := anInteger.
  code := self code: index type: LdTempType!

Item was changed:
+ ----- Method: TempVariableNode>>isDefinedWithinBlockExtent: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>isDefinedWithinBlockExtent: (in category 'code generation (closures)') -----
  isDefinedWithinBlockExtent: anInterval
  ^anInterval rangeIncludes: definingScope actualScope blockExtent first!

Item was changed:
+ ----- Method: TempVariableNode>>isIndirectTempVector (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>isIndirectTempVector (in category 'code generation (closures)') -----
  isIndirectTempVector
  ^false!

Item was changed:
+ ----- Method: TempVariableNode>>isReferencedWithinBlockExtent: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>isReferencedWithinBlockExtent: (in category 'code generation (closures)') -----
  isReferencedWithinBlockExtent: anInterval
  readingScopes ~~ nil ifTrue:
  [readingScopes do:
  [:set "<Set of <Integer>>"|
  set do:
  [:location|
  (anInterval rangeIncludes: location) ifTrue:
  [^true]]]].
  writingScopes ~~ nil ifTrue:
  [writingScopes do:
  [:set "<Set of <Integer>>"|
  set do:
  [:location|
  (anInterval rangeIncludes: location) ifTrue:
  [^true]]]].
  ^false!

Item was changed:
+ ----- Method: TempVariableNode>>referenceScopesAndIndicesDo: (in category 'closure analysis') -----
- ----- Method: TempVariableNode>>referenceScopesAndIndicesDo: (in category 'code generation (closures)') -----
  referenceScopesAndIndicesDo: aBinaryBlock
  "Evaluate aBinaryBlock with all read or write scopes and locations.
  This is used to copy the reference information into RemoteTempVectorNodes"
  readingScopes ~~ nil ifTrue:
  [readingScopes keysAndValuesDo:
  [:scopeBlock "<BlockNode>" :set "<Set of <Integer>>"|
  set do: [:location| aBinaryBlock value: scopeBlock value: location]]].
  writingScopes ~~ nil ifTrue:
  [writingScopes keysAndValuesDo:
  [:scopeBlock "<BlockNode>" :set "<Set of <Integer>>"|
  set do: [:location| aBinaryBlock value: scopeBlock value: location]]]!

Item was changed:
+ ----- Method: VariableNode>>beingAssignedToAnalyseTempsWithin:rootNode:assignmentPools: (in category 'closure analysis') -----
- ----- Method: VariableNode>>beingAssignedToAnalyseTempsWithin:rootNode:assignmentPools: (in category 'code generation (closures)') -----
  beingAssignedToAnalyseTempsWithin: scopeBlock "<BlockNode>" rootNode: rootNode "<MethodNode>" assignmentPools: assignmentPools "<Dictionary>"
  "No-op overridden by TempVariableNode"!