<body><div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000">
<img src="cid:10854e18-41de-40a6-a844-d8dbdb7aab7e" width="auto"></img><div class="mb_sig"></div><blockquote class="history_container" type="cite" style="border-left-style:solid;border-width:1px; margin-top:20px; margin-left:0px;padding-left:10px;">
<p style="color: #AAAAAA; margin-top: 10px;">Am 04.09.2019 17:04:03 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p><div style="font-family:Arial,Helvetica,sans-serif">A new version of Compiler was added to project The Inbox:<br>http://source.squeak.org/inbox/Compiler-mt.410.mcz<br><br>==================== Summary ====================<br><br>Name: Compiler-mt.410<br>Author: mt<br>Time: 4 September 2019, 5:03:52.834738 pm<br>UUID: 7ff9d1f8-5f7a-4077-b11b-ede80ada7d13<br>Ancestors: Compiler-TraitTest.409<br><br>Fixes ifNil:ifNotNil: decompilation. Please review.<br><br>- Only decompile ifNil:ifNotNil: if temps are not closured across nested blocks. This is the same behavior as #to:(by:)do:, which does not restore #to:(by:)do: if the 'var' or 'limit' are in an outer (outer?) scope. Only relevant if programmers type the optimized source code themselves.<br>- Note that I created a new method in DecompilerConstructor to pass 'tempReadCounts'. #to:(by:)do: is reconstructed in Decompiler, which already has access to 'tempReadCounts'. See Decompiler >> #jump:if: and #convertToDoLoop:.<br><br>=============== Diff against Compiler-TraitTest.409 ===============<br><br>Item was added:<br>+ ----- Method: AssignmentNode>>ifNilTemporary (in category 'private') -----<br>+ ifNilTemporary<br>+ "(temp := object) == nil ifTrue: [...] ifFalse: [...]"<br>+ <br>+ ^ self variable!<br><br>Item was added:<br>+ ----- Method: AssignmentNode>>ifNilValue (in category 'private') -----<br>+ ifNilValue<br>+ "(temp := object) == nil ifTrue: [...] ifFalse: [...]"<br>+ <br>+ ^ self value!<br><br>Item was changed:<br> ----- Method: BlockNode>>printTemporaries:on:doPrior: (in category 'printing') -----<br> printTemporaries: tempSequence on: aStream doPrior: aBlock<br> "Print any in-scope temporaries. If there are any evaluate aBlock<br> prior to printing. Answer whether any temporaries were printed."<br> | tempStream seen |<br> tempSequence ifNil:<br> [^false].<br> tempStream := (String new: 16) writeStream.<br> "This is for the decompiler which canmot work out which optimized block a particular temp is<br> local to and hence may produce diplicates as in<br> expr ifTrue: [| aTemp | ...] ifFalse: [| aTemp | ...]"<br> seen := Set new.<br> tempSequence do:<br> [:tempNode |<br> tempNode isIndirectTempVector<br> ifTrue:<br> [tempNode remoteTemps do:<br> [:tempVariableNode|<br> (tempVariableNode scope >= 0<br>+ and: [<br>+ "This is for the deocmpiler which may create a block arg when converting<br>+ a ifTrue:ifFalse: into a ifNil:ifNotNil: but won't remove it from temporaries"<br>+ tempVariableNode isBlockArg not<br>+ and: [(seen includes: tempNode key) not]]) ifTrue:<br>- and: [(seen includes: tempNode key) not]) ifTrue:<br> [tempStream space; nextPutAll: (seen add: tempVariableNode key)]]]<br> ifFalse:<br> [(tempNode scope >= -1<br> and: ["This is for the decompiler which may create a block arg when converting<br>+ a while into a to:do: but won't remove it from temporaries"<br>- a while into a to:do: but won't remove it form temporaries"<br> tempNode isBlockArg not<br> and: [(seen includes: tempNode key) not]]) ifTrue:<br> [tempStream space; nextPutAll: (seen add: tempNode key)]]].<br> tempStream position = 0 ifTrue:<br> [^false].<br> aBlock value.<br> aStream nextPut: $|; nextPutAll: tempStream contents; space; nextPut: $|.<br> ^true!<br><br>Item was changed:<br> ----- Method: Decompiler>>jump:if: (in category 'instruction decoding') -----<br> jump: dist if: condition<br> <br> | savePc sign elsePc elseStart end cond ifExpr thenBlock elseBlock<br> thenJump elseJump condHasValue isIfNil saveStack |<br> lastJumpIfPcStack addLast: lastPc.<br> stack last == CascadeFlag ifTrue: [^ [self case: dist] ensure: [lastJumpIfPcStack removeLast]].<br> elsePc := lastPc.<br> elseStart := pc + dist.<br> end := limit.<br> "Check for bfp-jmp to invert condition.<br> Don't be fooled by a loop with a null body."<br> sign := condition.<br> savePc := pc.<br> self interpretJump ifNotNil:<br> [:elseDist|<br> (elseDist >= 0 and: [elseStart = pc]) ifTrue:<br> [sign := sign not. elseStart := pc + elseDist]].<br> pc := savePc.<br> ifExpr := stack removeLast.<br> (isIfNil := stack size > 0 and: [stack last == IfNilFlag]) ifTrue:<br> [stack removeLast].<br> saveStack := stack.<br> stack := OrderedCollection new.<br> thenBlock := self blockTo: elseStart.<br> condHasValue := hasValue or: [isIfNil].<br> "ensure jump is within block (in case thenExpr returns)"<br> thenJump := exit <= end="" iftrue:="" [exit]="" iffalse:=""></=><br> "if jump goes back, then it's a loop"<br> thenJump <><br> ifTrue:<br> [| blockBody blockArgs savedReadCounts blockBodyReadCounts selector |<br> "Must be a while loop...<br> thenJump will jump to the beginning of the while expr. In the case of while's<br> with a block in the condition, the while expr should include more than just<br> the last expression: find all the statements needed by searching for the node<br> with the relevant pc."<br> stack := saveStack.<br> savedReadCounts := tempReadCounts copy.<br> pc := thenJump.<br> blockBody := self statementsTo: elsePc.<br> blockBodyReadCounts := tempReadCounts.<br> savedReadCounts keysAndValuesDo:<br> [:temp :count|<br> blockBodyReadCounts at: temp put: (blockBodyReadCounts at: temp) - count].<br> tempReadCounts := savedReadCounts.<br> "discard unwanted statements from block"<br> blockBody size - 1 timesRepeat: [statements removeLast].<br> blockArgs := thenBlock statements = constructor codeEmptyBlock statements<br> ifTrue: [#()]<br> ifFalse: [{ thenBlock }].<br> selector := blockArgs isEmpty<br> ifTrue: [sign ifTrue: [#whileFalse] ifFalse: [#whileTrue]]<br> ifFalse: [sign ifTrue: [#whileFalse:] ifFalse: [#whileTrue:]].<br> statements addLast:<br> (constructor<br> codeMessage: (constructor codeBlock: blockBody returns: false)<br> selector: (constructor codeSelector: selector code: #macro)<br> arguments: blockArgs).<br> pc := elseStart.<br> selector == #whileTrue: ifTrue:<br> [self convertToDoLoop: blockBodyReadCounts]]<br> ifFalse:<br> ["Must be a conditional..."<br> elseBlock := self blockTo: thenJump.<br> elseJump := exit.<br> "if elseJump is backwards, it is not part of the elseExpr"<br> elseJump < elsepc=""><br> [pc := lastPc].<br> cond := isIfNil<br> ifTrue:<br> [constructor<br> codeMessage: ifExpr ifNilReceiver<br> selector: (constructor<br> codeSelector: (sign ifTrue: [#ifNotNil:] ifFalse: [#ifNil:])<br> code: #macro)<br> arguments: (Array with: thenBlock)]<br> ifFalse:<br>+ [(sign ifTrue: [{elseBlock. thenBlock}] ifFalse: [{thenBlock. elseBlock}]) in: [:args |<br>+ (constructor<br>+ decodeIfNilWithReceiver: ifExpr<br>+ selector: #ifTrue:ifFalse:<br>+ arguments: args<br>+ tempReadCounts: tempReadCounts) ifNil: [<br>+ constructor<br>+ codeMessage: ifExpr<br>+ selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)<br>+ arguments: args]]].<br>- [constructor<br>- codeMessage: ifExpr<br>- selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)<br>- arguments: (sign<br>- ifTrue: [{elseBlock. thenBlock}]<br>- ifFalse: [{thenBlock. elseBlock}])].<br> stack := saveStack.<br> condHasValue<br> ifTrue: [stack addLast: cond]<br> ifFalse: [statements addLast: cond]].<br> lastJumpIfPcStack removeLast.!<br><br>Item was changed:<br> ----- Method: DecompilerConstructor>>codeMessage:selector:arguments: (in category 'constructor') -----<br> codeMessage: receiver selector: selector arguments: arguments<br> | symbol |<br> symbol := selector key.<br> (self<br> decodeLiteralVariableValueDereferenceWithReceiver: receiver<br> selector: symbol<br> arguments: arguments) ifNotNil: [:node| ^node].<br>+ <br>- (self decodeIfNilWithReceiver: receiver<br>- selector: symbol<br>- arguments: arguments) ifNotNil: [:node| ^node].<br> ^MessageNode new<br> receiver: receiver selector: selector<br> arguments: arguments<br> precedence: symbol precedence!<br><br>Item was removed:<br>- ----- Method: DecompilerConstructor>>decodeIfNilWithReceiver:selector:arguments: (in category 'constructor') -----<br>- decodeIfNilWithReceiver: receiver selector: selector arguments: arguments<br>- receiver ifNil: [ ^nil ]. "For instance, when cascading"<br>- selector == #ifTrue:ifFalse:<br>- ifFalse: [^ nil].<br>- (receiver isMessage: #==<br>- receiver: nil<br>- arguments: [:argNode | argNode == NodeNil])<br>- ifFalse: [^ nil].<br>- ^ (MessageNode new<br>- receiver: receiver<br>- selector: (SelectorNode new key: #ifTrue:ifFalse: code: #macro)<br>- arguments: arguments<br>- precedence: 3)<br>- noteSpecialSelector: #ifNil:ifNotNil:!<br><br>Item was added:<br>+ ----- Method: DecompilerConstructor>>decodeIfNilWithReceiver:selector:arguments:tempReadCounts: (in category 'constructor') -----<br>+ decodeIfNilWithReceiver: receiver selector: selector arguments: arguments tempReadCounts: tempReadCounts<br>+ <br>+ | node temp |<br>+ receiver ifNil: [ ^nil ]. "For instance, when cascading"<br>+ selector == #ifTrue:ifFalse:<br>+ ifFalse: [^ nil].<br>+ <br>+ (receiver isMessage: #==<br>+ receiver: nil<br>+ arguments: [:argNode | argNode == NodeNil])<br>+ ifFalse: [^ nil].<br>+ <br>+ "Like #to:(by:)do:, support only local temps."<br>+ (((temp := receiver ifNilTemporary) isNil or: [tempReadCounts includesKey: temp]) or: [<br>+ "What about 'object ifNotNil: [:o | ]', which as not read the blockArg? Just check that there is no remote vector pointing to it."<br>+ tempReadCounts keys noneSatisfy: [:otherTemp |<br>+ otherTemp isIndirectTempVector<br>+ ifTrue: [otherTemp remoteTemps anySatisfy: [:remoteTemp | remoteTemp name = temp name]]<br>+ ifFalse: [otherTemp name = temp name]]<br>+ ])<br>+ ifFalse: [^ nil].<br>+ <br>+ node := (MessageNode new<br>+ receiver: receiver<br>+ selector: (SelectorNode new key: #ifTrue:ifFalse: code: #macro)<br>+ arguments: arguments<br>+ precedence: 3).<br>+ <br>+ "Reconfigure the message node to #ifNil:ifNotNil:. Note that original* instance variables keep their optimized format. See MessageNode >> #printIfNilNotNil:indent:." <br>+ node<br>+ noteSpecialSelector: #ifNil:ifNotNil:;<br>+ selector: (SelectorNode new key: #ifNil:ifNotNil:).<br>+ <br>+ temp ifNil: [^ node].<br>+ temp isTemp ifFalse: [^ node].<br>+ <br>+ (arguments second isJust: NodeNil) not ifTrue: [<br>+ temp beBlockArg.<br>+ node arguments: {<br>+ arguments first.<br>+ arguments second copy arguments: { temp }; yourself } ].<br>+ <br>+ ^ node!<br><br>Item was added:<br>+ ----- Method: MessageNode>>ifNilTemporary (in category 'private') -----<br>+ ifNilTemporary<br>+ <br>+ ^ self ifNilReceiver ifNilTemporary!<br><br>Item was changed:<br> ----- Method: MessageNode>>printIfNilNotNil:indent: (in category 'printing') -----<br> printIfNilNotNil: aStream indent: level<br> <br>+ (arguments first isJust: NodeNil) ifTrue: [<br>+ self printReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>+ ^ self printKeywords: #ifNotNil:<br>- self printReceiver: receiver ifNilReceiver on: aStream indent: level.<br>- <br>- (arguments first isJust: NodeNil) ifTrue:<br>- [^ self printKeywords: #ifNotNil:<br> arguments: { arguments second }<br> on: aStream indent: level].<br>+ <br>+ (arguments second isJust: NodeNil) ifTrue: [<br>+ self printReceiver: receiver ifNilReceiver on: aStream indent: level.<br>+ ^ self printKeywords: #ifNil:<br>- (arguments second isJust: NodeNil) ifTrue:<br>- [^ self printKeywords: #ifNil:<br> arguments: { arguments first }<br> on: aStream indent: level].<br>+ <br>+ self printReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br> ^ self printKeywords: #ifNil:ifNotNil:<br> arguments: arguments<br> on: aStream indent: level!<br><br>Item was changed:<br> ----- Method: MessageNode>>printWithClosureAnalysisIfNilNotNil:indent: (in category 'printing') -----<br> printWithClosureAnalysisIfNilNotNil: aStream indent: level<br> <br>+ (arguments first isJust: NodeNil) ifTrue: [<br>+ self printWithClosureAnalysisReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>+ ^ self printWithClosureAnalysisKeywords: #ifNotNil:<br>- self printWithClosureAnalysisReceiver: receiver ifNilReceiver on: aStream indent: level.<br>- <br>- (arguments first isJust: NodeNil) ifTrue:<br>- [^self printWithClosureAnalysisKeywords: #ifNotNil:<br> arguments: { arguments second }<br> on: aStream indent: level].<br>+ <br>+ (arguments second isJust: NodeNil) ifTrue: [<br>+ self printWithClosureAnalysisReceiver: receiver ifNilReceiver on: aStream indent: level.<br>+ ^ self printWithClosureAnalysisKeywords: #ifNil:<br>- (arguments second isJust: NodeNil) ifTrue:<br>- [^self printWithClosureAnalysisKeywords: #ifNil:<br> arguments: { arguments first }<br> on: aStream indent: level].<br>+ <br>+ self printWithClosureAnalysisReceiver: receiver ifNilReceiver ifNilValue on: aStream indent: level.<br>+ ^ self printWithClosureAnalysisKeywords: #ifNil:ifNotNil:<br>- ^self printWithClosureAnalysisKeywords: #ifNil:ifNotNil:<br> arguments: arguments<br> on: aStream indent: level!<br><br>Item was added:<br>+ ----- Method: ParseNode>>ifNilTemporary (in category 'private') -----<br>+ ifNilTemporary<br>+ <br>+ ^ nil!<br><br>Item was added:<br>+ ----- Method: ParseNode>>ifNilValue (in category 'private') -----<br>+ ifNilValue<br>+ <br>+ ^self!<br><br>Item was changed:<br> ----- Method: Parser>>parseCue:noPattern:ifFail: (in category 'public access') -----<br> parseCue: aCue noPattern: noPattern ifFail: aBlock <br> "Answer a MethodNode for the argument, sourceStream, that is the root of<br> a parse tree. Parsing is done with respect to the CompilationCue to <br> resolve variables. Errors in parsing are reported to the cue's requestor; <br> otherwise aBlock is evaluated. The argument noPattern is a Boolean that is<br> true if the the sourceStream does not contain a method header (i.e., for DoIts)."<br> <br> | methNode repeatNeeded myStream s p subSelection |<br> myStream := aCue sourceStream.<br> [repeatNeeded := false.<br> p := myStream position.<br> s := myStream upToEnd.<br> myStream position: p.<br>+ <br>+ doitFlag := noPattern.<br>+ failBlock:= aBlock.<br> <br> self encoder init: aCue notifying: self.<br> self init: myStream cue: aCue failBlock: [^ aBlock value].<br> <br> subSelection := self interactive and: [cue requestor selectionInterval = (p + 1 to: p + s size)].<br> <br>- doitFlag := noPattern.<br>- failBlock:= aBlock.<br> [methNode := self method: noPattern context: cue context] <br> on: ReparseAfterSourceEditing <br> do: [ :ex |<br> repeatNeeded := true.<br> properties := nil. "Avoid accumulating pragmas and primitives Number"<br> myStream := ex newSource <br> ifNil: [subSelection<br> ifTrue:<br> [ReadStream<br> on: cue requestor text string<br> from: cue requestor selectionInterval first<br> to: cue requestor selectionInterval last]<br> ifFalse:<br> [ReadStream on: cue requestor text string]]<br> ifNotNil: [:src | myStream := src readStream]].<br> repeatNeeded] whileTrue:<br> [encoder := self encoder class new].<br> methNode sourceText: s.<br> ^methNode<br> !<br><br><br></div></blockquote>
</div></body>