--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/IRDecompiler.st Thu Mar 29 18:03:58 2012 +0000
@@ -0,0 +1,1305 @@
+"{ Package: 'cvut:stx/goodies/newcompiler' }"
+
+IRInterpreter subclass:#IRDecompiler
+ instanceVariableNames:'stack sp scope currentInstr valueLabelMap mapEmptyStatement'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'NewCompiler-IR'
+!
+
+IRDecompiler comment:'I interpret IRMethod instructions and generate a Smalltalk abstract syntax tree rooted at a RBMethodNode.
+This is implemented like a shift-reduce parser. Each instruction either causes a node to be pushed on the stack (shift), or causes one or more nodes to be popped and combined into a single node which is push back on the stack (reduce). Most reduction is done at the "label: labelNum" instruction where it tries to reduce jump structures into control messages like #ifTrue:, whileFalse:, etc.
+Several pseudo nodes (RBPseudoNode and subclasses) are used to represent basic instructions that have not been reduced to real AST nodes yet.
+'
+!
+
+
+!IRDecompiler class methodsFor:'as yet unclassified'!
+
+dummySelector: numArgs
+ "Answer a dummy selector with number of args"
+
+ | sel |
+ sel _ 'unknown'.
+ 1 to: numArgs do: [:i |
+ sel _ sel, 'with:'].
+ ^ sel asSymbol
+! !
+
+!IRDecompiler methodsFor:'accessing'!
+
+scope
+
+ ^scope
+! !
+
+!IRDecompiler methodsFor:'init'!
+
+addTempToScope: ir
+
+ "Temp may be created only if they are not used in the method"
+ 0 to: ir numRargs - 1 do: [:i | (scope
+ rawVarAt: i
+ ifNone: [
+ scope capturedVars do: [:each |
+ each index = i ifTrue:[
+ scope tempVarAt: scope capturedVars size + scope tempVars size.
+ ^self]].
+ scope tempVarAt: i]) markArg]
+!
+
+decompileIR: ir
+ | sequenceNode temps args goto seq value method |
+ scope isBlockScope
+ ifTrue:[(scope addTemp: 'parent env') markArg]
+ ifFalse:[(scope addTemp: 'self') markArg].
+ ir tempKeys do: [:temp | scope tempVarAt: temp].
+ 0 to: ir numRargs - 1 do: [:i | (scope tempVarAt: i) markArg].
+ self interpret: ir.
+
+ self addTempToScope: ir.
+ self label: #return.
+ self Label: #return.
+ (self endCase: #lastReturn) ifFalse:[self Label: #return.].
+ goto := self Goto.
+ value := self ValueOrNone.
+ seq := self Sequence.
+ self removeClosureCreation: seq.
+ sp = 1 ifFalse: [stack explore. self error: 'error'].
+ value ifNotNil: [seq addNode: value].
+ sequenceNode := (self newBlock: seq return: goto) body.
+ temps := scope compactIndexTemps asArray.
+ ir tempKeys: temps.
+ args := (temps first: ir numRargs) allButFirst.
+ args := args collect: [:var | self newVar: var].
+ temps := temps allButFirst: ir numRargs.
+ sequenceNode temporaries: (temps collect: [:var | self newVar: var]),
+ ((scope capturedVars select:[:var | var name ~= 'self' and: [var sourceTemp == nil]])
+ collect:[:var | self newVar: var]).
+ method := (RBMethodNode new)
+ selectorParts: (self
+ newSelectorParts: (self class dummySelector: args size));
+ arguments: args;
+ body: sequenceNode;
+ primitiveNode: ir primitiveNode;
+ scope: scope.
+ sequenceNode parent: method.
+ Preferences compileBlocksAsClosures
+ ifFalse: [ASTFixDecompileBlockScope new visitNode: method].
+ ^ method
+!
+
+removeClosureCreation: seq
+ (Preferences compileBlocksAsClosures
+ and: [seq statements size > 0]
+ and: [seq statements first isClosureEnvironmentCreation]) ifTrue: [
+ seq statements removeFirst.
+ (seq statements size > 0
+ and: [seq statements first isClosureEnvironmentRegistration])
+ ifTrue: [seq statements removeFirst]].
+
+ [Preferences compileBlocksAsClosures
+ and: [seq statements size > 0]
+ and: [seq statements first isClosureRegistrationAndCreation
+ or: [seq statements first isSelfClosureRegistration]
+ or: [seq statements first isTempClosureRegistration]]]
+ whileTrue: [seq statements removeFirst]
+!
+
+scope: aLexicalScope
+
+ scope := aLexicalScope
+! !
+
+!IRDecompiler methodsFor:'instructions'!
+
+goto: seqNum
+
+ self stackPush: (RBPseudoGotoNode new destination: seqNum).
+!
+
+if: bool goto: seqNum1 otherwise: seqNum2
+
+ self stackPush: (RBPseudoIfNode new
+ boolean: bool;
+ destination: seqNum1;
+ otherwise: seqNum2)
+!
+
+label: seqNum
+
+ stack isEmpty ifTrue: [ "start"
+ ^ stack addLast: (RBPseudoLabelNode new destination: seqNum)].
+
+ self captureEmptyStatement.
+ "Reduce jump structures to one of the following if possible"
+ [ (self endBlock: seqNum) or: [
+ (self endAndOr: seqNum) or: [
+ (self endAndOr2: seqNum) or: [
+ (self endIfThen: seqNum) or: [
+ (self endIfThen2: seqNum) or:[
+ (self endIfThenElse: seqNum) or: [
+ (self endCase: seqNum) or: [
+ (self endToDo: seqNum) or: [
+ (self endWhile: seqNum) or: [
+ (self endWhile2: seqNum) or: [
+ (self endIfNil: seqNum)]]]]]]]]]]
+ ] whileTrue.
+
+ stack addLast: (RBPseudoLabelNode new destination: seqNum).
+!
+
+popTop
+
+ | value |
+ stack last ifNil: [^ stack removeLast]. "pop no-op from #simplifyTempAssign:"
+ [stack last isLabel
+ and: [(stack atLast:2) isGoto]
+ and: [stack last destination = (stack atLast: 2) destination]]
+ whileTrue: [
+ stack removeLast.
+ stack removeLast].
+ stack last isValue ifTrue: [
+ (stack atLast: 2) isSequence ifTrue: [
+ value := stack removeLast.
+ ^ stack last addNode: value.
+ ] ifFalse: [(stack atLast: 2) isPseudo ifTrue: [
+ value := stack removeLast.
+ ^ stack addLast: (RBSequenceNode statements: {value}).
+ ]].
+ ].
+ stack addLast: RBPseudoPopNode new
+!
+
+pushBlock: irMethod
+
+ self block: irMethod env: nil
+!
+
+pushBlockMethod: irMethod
+
+ "block will recognized when send: #createBlock:"
+ self pushLiteral: irMethod
+!
+
+pushDup
+
+ stack addLast: RBPseudoDupNode new
+!
+
+pushInstVar: index
+
+ self stackPush: (self newVar: (scope instanceScope instVar: index))
+!
+
+pushLiteral: object
+
+ self stackPush: (self newLiteral: object).
+!
+
+pushLiteralVariable: object
+
+ | var |
+ var := scope lookupVar: object key asString.
+ self stackPush: (self newVar: var)
+!
+
+pushTemp: tempIndex
+
+ | var |
+ var := scope basicTempVarAt: tempIndex.
+ var isTemp ifTrue: [var cantBeCapture].
+ self stackPush: (self newVar: var).
+!
+
+remoteReturn
+
+ stack removeLast. "pop home context free var"
+ self goto: #return.
+!
+
+returnTop
+
+ self goto: #return.
+!
+
+send: selector numArgs: numArgs
+
+ | args rcvr |
+ selector = #caseError ifTrue:[^self stackPush: (RBPseudoSendNode new selector: selector)].
+ args := OrderedCollection new.
+ [ selector numArgs timesRepeat: [args addFirst: self Value].
+ rcvr := self Value.
+ ] on: Abort do: [
+ [self stackPush: (RBPseudoSendNode new selector: selector).
+ ^self cascade] on: Abort do:[^false]
+ ].
+
+ Preferences compileBlocksAsClosures
+ ifTrue: [ (rcvr isLiteral and: [selector = #createBlock:]) ifTrue: [
+ ^ self block: rcvr value env: args first]]
+ ifFalse: [ (selector = #blockCopy:) ifTrue: [
+ ^ self stackPush: (RBPseudoSendNode new selector: selector; arguments: args)]].
+
+ self stackPush: (self simplify: (RBMessageNode new
+ receiver: rcvr
+ selectorParts: (self newSelectorParts: selector)
+ arguments: args)).
+
+ "Created: / 01-12-2008 / 19:40:52 / Jan Vrany <vranyj1@fel.cvut.cz>"
+!
+
+send: selector numArgs: numArgs toSuperOf: behavior
+
+ | args rcvr |
+ args := OrderedCollection new.
+ selector numArgs timesRepeat: [args addFirst: self Value].
+ rcvr := self Value.
+ (rcvr isVariable and: [rcvr name = 'self']) ifFalse: [self patternError].
+
+ rcvr identifierToken: (SqueakToken value: 'super' start: 0).
+ self stackPush: (RBMessageNode new
+ receiver: rcvr
+ selectorParts: (self newSelectorParts: selector)
+ arguments: args).
+
+ "Created: / 01-12-2008 / 19:45:52 / Jan Vrany <vranyj1@fel.cvut.cz>"
+!
+
+storeIntoLiteralVariable: association
+
+ | var |
+ var := scope lookupVar: association key asString.
+ self stackPush: (self simplifyTempAssign:
+ (RBAssignmentNode variable: (self newVar: (var markWrite)) value: self Value))
+!
+
+storeTemp: tempIndex
+
+ | var |
+ var := scope basicTempVarAt: tempIndex.
+ var isCaptured ifFalse: [var cantBeCapture].
+ var isTemp ifTrue:[
+ var isArg: false].
+ self stackPush: (self simplifyTempAssign:
+ (RBAssignmentNode variable: (self newVar: (var markWrite)) value: self Value)).
+! !
+
+!IRDecompiler methodsFor:'interpret'!
+
+interpretInstruction: irInstruction
+
+ currentInstr := irInstruction.
+ super interpretInstruction: irInstruction.
+!
+
+interpretSequence: instructionSequence
+
+ super interpretSequence: instructionSequence.
+ "currentInstr := nil."
+! !
+
+!IRDecompiler methodsFor:'old blocks'!
+
+blockReturnTop
+
+ self goto: #return.
+!
+
+endBlock: seqNum
+
+ | blockSeq block goto startBlock |
+ [
+ goto := self GotoOrReturn: seqNum.
+ (goto isRet
+ or:[goto mapInstr notNil
+ and: [goto mapInstr isBlockReturnTop]]) ifFalse: [self abort].
+ sp = 0 ifTrue: [self abort].
+ blockSeq := self Sequence2.
+ startBlock := self Label.
+ block := self Block.
+ (goto isRet not
+ and:[goto mapInstr notNil]
+ and: [goto mapInstr isBlockReturnTop]
+ and: [block successor ~= seqNum]) ifTrue:[
+ self stackPush: block.
+ self stackPush: startBlock.
+ self stackPush: blockSeq.
+ self stackPush: goto.
+ self abort].
+ self Send.
+ ] on: Abort do: [^ false].
+
+ self stackPush: (self newBlock: blockSeq return: goto).
+ stack last arguments: block arguments.
+ "No extra scope is need if we don't use any temporaries and arguments.
+ so we remove them"
+ (stack last arguments isEmpty and: [stack last body temporaries isEmpty])
+ ifTrue:[ASTReplaceVariableScope replace: stack last scope: scope outerScope ].
+ scope := scope outerScope.
+ currentInstr := nil.
+ self goto: block successor.
+ ^ true
+!
+
+jumpOverBlock: seqNum1 to: seqNum2
+ | numArgs args oldscope pseudoBlock |
+
+ oldscope := scope.
+ self scope: (scope newBlockScope).
+ oldscope tempVarAt: 0.
+ (scope addObjectTemp: (oldscope tempVarAt: 0)).
+ numArgs := stack last arguments first value.
+ self stackPush: (pseudoBlock := RBPseudoBlockNode new).
+
+ args := OrderedCollection new.
+ numArgs timesRepeat: [ | var instr |
+ instr := currentInstr blockSequence removeFirst.
+ var := oldscope tempVarAt: instr number.
+ args add: (self newVar: var).
+ var isUnused ifTrue: [oldscope removeTempFromOldBlock: var].
+ scope addObjectTemp: var.
+ currentInstr blockSequence first isPop
+ ifFalse: [
+ currentInstr blockSequence sequence addFirst: (IRInstruction pushTemp: var index)]
+ ifTrue:[currentInstr blockSequence removeFirst].
+
+ ].
+ args := args reverse.
+ pseudoBlock
+ block: seqNum1;
+ successor: seqNum2;
+ arguments: args
+
+!
+
+storeInstVar: number
+
+ | var |
+ var := scope instanceScope instVar: number.
+ self stackPush: (RBAssignmentNode variable: (self newVar: var) value: self Value)
+! !
+
+!IRDecompiler methodsFor:'priv instructions'!
+
+addReturn: statements from: goto
+
+ | ret |
+ statements last isReturn ifTrue:[^self].
+ ret := RBReturnNode value: statements last.
+ Preferences compileBlocksAsClosures ifTrue:[
+ scope isHome ifFalse: [ret homeBinding: scope outerEnvScope thisEnvVar]].
+ goto mapInstr sourceNode: ret.
+ statements atLast: 1 put: ret.
+!
+
+block: method env: envRefNode
+
+ self stackPush: (IRDecompiler new
+ scope: (scope newBlockScope "capturedVars: vars");
+ decompileIR: method ir)
+ asBlock
+!
+
+cascade
+
+ | messages selector args rcvr |
+ messages := OrderedCollection new.
+ "last message"
+ selector _ self Send selector.
+ args := OrderedCollection new.
+ selector numArgs timesRepeat: [args addFirst: self Value].
+ messages addFirst: selector -> args.
+
+ "rest of messages"
+ [(rcvr := self ValueOrNone) isNil] whileTrue: [
+ self Pop.
+ selector := self Send selector.
+ args := OrderedCollection new.
+ selector numArgs timesRepeat: [args addFirst: self Value].
+ self Dup.
+ messages addFirst: selector -> args.
+ ].
+
+ messages := messages collect: [:assoc |
+ RBMessageNode
+ receiver: rcvr
+ selector: assoc key
+ arguments: assoc value].
+ self stackPush: (RBCascadeNode messages: messages).
+!
+
+endAndOr2: seqNum
+
+ | goto seq p if2 test else o if1 seqValue elseTest otherwise |
+ [
+ goto _ self Goto.
+ seqValue _ self ValueOrNone.
+ seq _ self Sequence.
+ p _ self Label destination.
+ if2 _ self IfGoto: seqNum otherwise: p.
+ elseTest _ self Value.
+ else _ self SequenceBackTo: goto destination.
+ o _ self Label destination.
+ o = goto destination ifTrue: [self abort].
+ if1 _ self IfGoto: seqNum otherwise: o.
+ test _ self Value.
+ ] on: Abort do: [^ false].
+
+ if1 boolean = if2 boolean
+ ifFalse: [
+ otherwise := RBSequenceNode statements: #().
+ otherwise addNode: (self newLiteral: if2 boolean).
+ self stackPush: (RBMessageNode
+ receiver: test
+ selector: (if2 boolean ifTrue: [#ifTrue:ifFalse:] ifFalse: [#ifFalse:ifTrue:])
+ arguments: {self newBlock: (else addNode: elseTest).
+ self newBlock: otherwise}).]
+ ifTrue:[self stackPush: (RBMessageNode
+ receiver: test
+ selector: (if2 boolean ifTrue: [#or:] ifFalse: [#and:])
+ arguments: {self newBlock: (else addNode: elseTest)})].
+ stack addLast: if2.
+ self label: p.
+ stack addLast: seq.
+ seqValue ifNotNil: [stack addLast: seqValue].
+ stack addLast: goto.
+ ^ true
+!
+
+endAndOr: seqNum
+
+ | o test branches if body block sel1 sel2 if2 |
+ branches := OrderedCollection new.
+ [
+ (if2 := self If) otherwise = seqNum ifFalse: [self abort].
+ [ test := self Value.
+ body := self Sequence.
+ branches add: {body. test}.
+ o := self Label destination.
+ (if := self If) otherwise = o ifFalse: [self abort].
+ if destination = seqNum
+ ] whileFalse: [
+ if boolean = if2 boolean ifFalse: [self abort].
+ if destination = if2 destination ifFalse: [self abort].
+ ].
+ if boolean = if2 boolean ifTrue: [self abort].
+ test := self Value.
+ ] on: Abort do: [^ false].
+
+ if boolean
+ ifTrue: [sel1 := #or:. sel2 := #and:]
+ ifFalse: [sel1 := #and:. sel2 := #or:].
+ block := self newBlock: (branches first first addNode: branches first second).
+ branches allButFirstDo: [:pair |
+ block := self newBlock: (pair first addNode: (RBMessageNode
+ receiver: pair second
+ selector: sel2
+ arguments: {block})).
+ ].
+ self stackPush: (RBMessageNode
+ receiver: test
+ selector: sel1
+ arguments: {block}).
+ stack addLast: if2.
+ ^ true
+!
+
+endCase: seqNum
+
+ | otherwiseGoto goto node otherwiseValue otherwiseSeq n branchValue branchSeq f caseValue caseSeq rcvr branches message seqEnd afterOterwise seq afterOterwiseValue |
+ branches := OrderedCollection new.
+ [ "otherwise"
+ otherwiseGoto := self Goto.
+ node := self stackDown.
+ node isSequence ifTrue: [(node statements size = 1
+ and:[node statements first isSend]
+ and: [
+ node := node statements first.
+ node selector == #caseError]) ifFalse: [
+ otherwiseSeq := node] ].
+ (node isPop or: [node isSend and: [node selector == #caseError]]) ifTrue: [
+ node isPop ifTrue: [node := self Send].
+ node selector == #caseError ifFalse: [self abort].
+ ] ifFalse: [
+ sp := sp + 1. "stackUp"
+
+ seqNum == #lastReturn
+ ifFalse: [
+ otherwiseValue := self ValueOrNone.
+ otherwiseSeq := self Sequence]
+ ifTrue: [
+ afterOterwiseValue := self ValueOrNone.
+ otherwiseSeq := RBSequenceNode statements: #().
+ afterOterwise := self SequenceOtherwise].
+ ].
+ n := self Label destination.
+ "last case branch"
+ seqNum == #lastReturn
+ ifFalse: [goto := self GotoOrReturn: seqNum]
+ ifTrue: [
+ seqEnd := n.
+ goto := self GotoOrReturn: n.
+ otherwiseGoto := goto].
+ branchValue := self ValueOrNone.
+ branchSeq := self Sequence.
+ (stack at: sp) isPop ifTrue: [self stackDown].
+ f := self Label destination.
+
+ "last case"
+ self IfGoto: n otherwise: f.
+ self Send selector == #= ifFalse: [self abort].
+ caseValue := self Value.
+ caseSeq := self Sequence.
+ otherwiseSeq ifNil: [self Dup].
+ branches addFirst: ({caseSeq. caseValue} -> {branchSeq. branchValue. goto}).
+
+ [(rcvr := self ValueOrNone) isNil] whileTrue: [
+ "case branch"
+ n := self Label destination.
+ seqNum == #lastReturn
+ ifFalse: [goto := self GotoOrReturn: seqNum]
+ ifTrue: [goto := self GotoOrReturn: seqEnd].
+ branchValue := self ValueOrNone.
+ branchSeq := self Sequence.
+ self Pop.
+ f := self Label destination.
+ "case"
+ self IfGoto: n otherwise: f.
+ self Send selector == #= ifFalse: [self abort].
+ caseValue := self Value.
+ caseSeq := self Sequence.
+ self Dup.
+ branches addFirst: ({caseSeq. caseValue} -> {branchSeq. branchValue. goto}).
+ ].
+ ] on: Abort do: [^ false].
+
+ branches := branches collect: [:assoc |
+ assoc key second
+ ifNotNil: [assoc key first addNode: assoc key second].
+ assoc value second
+ ifNotNil: [assoc value first addNode: assoc value second].
+ RBMessageNode
+ receiver: (self newBlock: assoc key first return: nil)
+ selector: #->
+ arguments:
+ {self newBlock: assoc value first return: assoc value third}
+ ].
+ message := otherwiseSeq
+ ifNil: [
+ RBMessageNode
+ receiver: rcvr
+ selector: #caseOf:
+ arguments: {RBArrayNode statements: branches}]
+ ifNotNil: [
+ otherwiseValue
+ ifNotNil: [otherwiseSeq addNode: otherwiseValue].
+ RBMessageNode
+ receiver: rcvr
+ selector: #caseOf:otherwise:
+ arguments:
+ {RBArrayNode statements: branches.
+ self newBlock: otherwiseSeq return: otherwiseGoto}.
+ ].
+ self stackPush: message.
+ seqNum == #lastReturn ifTrue: [
+ self popTop.
+ seq := self Sequence.
+ afterOterwise ifNotNil:[seq statements addAllLast: afterOterwise statements].
+ self stackPush: seq.
+ afterOterwiseValue ifNotNil:[self stackPush: afterOterwiseValue].
+ branchValue := 1].
+ branchValue ifNil: [self popTop].
+ self stackPush: otherwiseGoto.
+ ^ true
+!
+
+endIfNil: seqNum
+
+ | goto branch o if rcvr value |
+ [
+ goto := self Goto.
+ value := self Value.
+ branch := self Sequence.
+ self Pop.
+ o := self Label destination.
+ if := self IfGoto: seqNum otherwise: o.
+ self Send selector == #== ifFalse: [self abort].
+ (self Value isLiteral: [:v | v isNil]) ifFalse: [self abort].
+ self Dup.
+ rcvr := self Value.
+ ] on: Abort do: [^ false].
+
+ branch addNode: value.
+ self stackPush: (RBMessageNode
+ receiver: rcvr
+ selector: (if boolean ifTrue: [#ifNotNil:] ifFalse: [#ifNil:])
+ arguments: {self newBlock: branch return: goto}).
+ self goto: seqNum.
+ ^ true
+!
+
+endIfThen2: seqNum
+
+ | goto branch o if test value gotoNum branch2 |
+ [
+ goto := self Goto.
+ (goto mapInstr ~= nil
+ and: [goto mapInstr isJump]
+ and: [goto mapInstr destination size = 1]
+ and: [goto mapInstr destination last isJump])
+ ifTrue: [gotoNum := goto
+ mapInstr destination last destination orderNumber]
+ ifFalse:[self abort].
+ (currentInstr ~= nil
+ and: [currentInstr isJump]
+ and: [currentInstr destination orderNumber = goto destination])
+ ifFalse: [self abort].
+ value := self Value.
+ branch := self Sequence.
+ o := self Label destination.
+ seqNum = gotoNum
+ ifFalse:[if := self IfGoto: gotoNum otherwise: o]
+ ifTrue:[self abort].
+ test := self Value.
+ ] on: Abort do: [^ false].
+
+ value ifNotNil: [branch addNode: value].
+ branch2 := RBSequenceNode statements: #().
+ branch2 addNode: (self newLiteral: if boolean).
+ self stackPush: (self simplify: (RBMessageNode
+ receiver: test
+ selector: (if boolean ifTrue: [#ifFalse:ifTrue:] ifFalse: [#ifTrue:ifFalse:])
+ arguments: {self newBlock: branch return: goto.
+ self newBlock: branch2})).
+ self goto: goto destination.
+ ^true
+!
+
+endIfThen3: seqNum
+
+ | goto branch o if test value |
+ [
+ goto := self Goto.
+ (goto destination == seqNum or: [self isExplicitReturn: goto])
+ ifFalse: [self abort].
+ goto isRet ifTrue: [value := self Value].
+ branch := self Sequence.
+ o := self Label destination.
+ if := self If.
+ ((if destination = seqNum
+ or: [if destination = (mapEmptyStatement at: seqNum ifAbsent:[seqNum])])
+ and: [if otherwise = o])
+ ifFalse:[self abort].
+ test := self Value.
+ ] on: Abort do: [^ false].
+
+
+ value ifNotNil: [branch addNode: value].
+ self stackPush: (self simplify: (RBMessageNode
+ receiver: test
+ selector: (if boolean ifTrue: [#ifFalse:] ifFalse: [#ifTrue:])
+ arguments: {self newBlock: branch return: goto})).
+ self popTop.
+ self goto: seqNum.
+ ^ true
+!
+
+endIfThen: seqNum
+
+ | goto branch o if test value |
+ [
+ goto := self Goto.
+ (goto destination == seqNum or: [self isExplicitReturn: goto])
+ ifFalse: [self abort].
+ goto isRet ifTrue: [value := self Value].
+ branch := self Sequence.
+ o := self Label destination.
+ if := self IfGoto: seqNum otherwise: o.
+ test := self Value.
+ ] on: Abort do: [^ false].
+
+
+ value ifNotNil: [branch addNode: value].
+ self stackPush: (self simplify: (RBMessageNode
+ receiver: test
+ selector: (if boolean ifTrue: [#ifFalse:] ifFalse: [#ifTrue:])
+ arguments: {self newBlock: branch return: goto})).
+ self popTop.
+ self goto: seqNum.
+ ^ true
+!
+
+endIfThenElse: seqNum
+
+ | goto2 else d goto1 then o if test value2 value1 |
+ [
+ goto2 := self Goto.
+ value2 := self ValueOrNone.
+ else := self Sequence.
+ d := self Label destination.
+ goto1 := self Goto.
+ ((self isExplicitReturn: goto2) or: [goto2 destination == goto1 destination]) ifFalse: [self abort].
+ value1 := self ValueOrNone.
+ then := self Sequence.
+ o := self Label destination.
+ if := self IfGoto: d otherwise: o.
+ test := self Value.
+ ] on: Abort do: [^ false].
+
+ value2 ifNotNil: [else addNode: value2].
+ value1 ifNotNil: [then addNode: value1].
+ (self isExplicitReturn: goto1) ifTrue:[self addReturn: then statements from: goto1].
+ (self isExplicitReturn: goto2) ifTrue:[self addReturn: else statements from: goto2].
+ self stackPush: (self simplify: (else isEmpty
+ ifTrue: [RBMessageNode
+ receiver: test
+ selector: (if boolean ifTrue: [#ifFalse:] ifFalse: [#ifTrue:])
+ arguments: {self newBlock: then return: goto1}]
+ ifFalse: [RBMessageNode
+ receiver: test
+ selector: (if boolean
+ ifTrue: [#ifFalse:ifTrue:]
+ ifFalse: [#ifTrue:ifFalse:])
+ arguments: {
+ self newBlock: then return: goto1.
+ self newBlock: else return: goto2}])).
+ value1 ifNil: [self popTop].
+ currentInstr := goto1 mapInstr.
+ self stackPush: goto1.
+ (else statements isEmpty and:
+ [stack anySatisfy: [:n | n isIf and: [n destination = d]]]
+ ) ifTrue: [
+ self label: d.
+ currentInstr := goto2 mapInstr.
+ self stackPush: goto2.
+ ].
+ ^ true
+!
+
+endToDo: seqNum
+
+ | start limit incr iter step loopBlock o if test limitExpr init |
+ [
+ start := self Goto destination.
+ limit := self Value.
+ incr := self Assignment.
+ iter := incr variable.
+ (incr value isMessage and:
+ [incr value selector == #+ and:
+ [incr value receiver isVariable and:
+ [incr value receiver binding == iter binding]]]
+ ) ifFalse: [self abort].
+ step := incr value arguments first.
+ loopBlock := self Sequence.
+ o := self Label destination.
+ if := self IfGoto: seqNum otherwise: o.
+ test := self Value.
+ (test isMessage and:
+ [(test selector == #<= or: [test selector == #>=]) and:
+ [(valueLabelMap at: test arguments first ifAbsent: [self abort]) destination = start]]
+ ) ifFalse: [self abort].
+ limitExpr := test arguments first.
+ limitExpr isAssignment ifTrue: [
+ (limitExpr variable binding index == limit binding index
+ and:[limitExpr variable binding scope == limit binding scope]) ifFalse: [self abort].
+ limitExpr := limitExpr value.
+ ].
+ init := test receiver.
+ (init isAssignment and: [init variable binding == iter binding])
+ ifFalse: [self abort].
+ ] on: Abort do: [^ false].
+ limit isVariable
+ ifTrue:[scope
+ removeTemp: limit binding
+ ifAbsent:[Preferences compileBlocksAsClosures
+ ifFalse:[scope removeTempFromOldBlock: limit]]].
+ loopBlock := self newBlock: loopBlock.
+ loopBlock arguments: {iter}.
+ self stackPush: ((step isLiteral: [:c | c = 1])
+ ifTrue: [RBMessageNode
+ receiver: init value
+ selector: #to:do:
+ arguments: {limitExpr. loopBlock}]
+ ifFalse: [RBMessageNode
+ receiver: init value
+ selector: #to:by:do:
+ arguments: {limitExpr. step. loopBlock}]).
+ self popTop.
+ self goto: seqNum.
+ ^ true
+!
+
+endWhile2: seqNum
+
+ | start loopBlock if test sequence o goto previousStack |
+ [
+ stack := (previousStack := stack) copy.
+ start := (goto := self Goto) destination.
+ self stackPush: goto.
+ [self endIfThen3: start] whileTrue.
+ start := self Goto destination.
+ loopBlock _ self Sequence.
+ o _ self Label destination.
+ if _ self IfGoto: seqNum otherwise: o.
+ test _ self Value.
+ sequence _ self SequenceBackTo: start.
+ self Label: start.
+ sp _ sp + 1. "stackUp"
+ ] on: Abort do: [stack := previousStack. ^ false].
+ loopBlock isEmpty
+ ifTrue:[self stackPush: (self simplify: (RBMessageNode
+ receiver: (self newBlock: (sequence addNode: test))
+ selector: (if boolean ifTrue: [#whileFalse] ifFalse: [#whileTrue])
+ arguments: #()))]
+ ifFalse:[self stackPush: (self simplify: (RBMessageNode
+ receiver: (self newBlock: (sequence addNode: test))
+ selector: (if boolean ifTrue: [#whileFalse:] ifFalse: [#whileTrue:])
+ arguments: {self newBlock: loopBlock}))].
+ self popTop.
+ self goto: seqNum.
+ ^ true
+!
+
+endWhile: seqNum
+
+ | start loopBlock if test sequence o |
+ [
+ start _ self Goto destination.
+ loopBlock _ self Sequence.
+ o _ self Label destination.
+ if _ self IfGoto: seqNum otherwise: o.
+ test _ self Value.
+ sequence _ self SequenceBackTo: start.
+ self Label: start.
+ sp _ sp + 1. "stackUp"
+ ] on: Abort do: [^ false].
+ loopBlock isEmpty
+ ifTrue:[self stackPush: (self simplify: (RBMessageNode
+ receiver: (self newBlock: (sequence addNode: test))
+ selector: (if boolean ifTrue: [#whileFalse] ifFalse: [#whileTrue])
+ arguments: #()))]
+ ifFalse:[self stackPush: (self simplify: (RBMessageNode
+ receiver: (self newBlock: (sequence addNode: test))
+ selector: (if boolean ifTrue: [#whileFalse:] ifFalse: [#whileTrue:])
+ arguments: {self newBlock: loopBlock}))].
+ self popTop.
+ self goto: seqNum.
+ ^ true
+! !
+
+!IRDecompiler methodsFor:'private'!
+
+captureEmptyStatement
+ | by replace node |
+
+ [by := self Goto destination.
+ replace := self Label destination.
+ replace = 0 ifTrue: [self abort]]
+ on: Abort
+ do: [^ false].
+ mapEmptyStatement at: by put: replace.
+ sp := nil.
+ ^ true
+!
+
+fixInnerFreeVar: aRcvrTemp
+
+ | scopeInnerFreeVar |
+ scopeInnerFreeVar := scope outerScope.
+ [aRcvrTemp scope = scopeInnerFreeVar] whileFalse:[
+ scopeInnerFreeVar hasInnerFreeVars: true.
+ scopeInnerFreeVar := scopeInnerFreeVar outerScope].
+ aRcvrTemp scope hasInnerFreeVars: true
+!
+
+initialize
+
+ stack := OrderedCollection new.
+ scope := nil parseScope newMethodScope. "in case never set"
+ valueLabelMap := IdentityDictionary new.
+ mapEmptyStatement := IdentityDictionary new
+!
+
+isExplicitReturn: goto
+
+ Preferences compileBlocksAsClosures
+ ifTrue:[^ goto isRet
+ and: [goto mapInstr notNil]
+ and: [goto mapInstr isRemote or: [scope isBlockScope not]]]
+ ifFalse: [^goto isRet and: [goto mapInstr isBlockReturnTop not]]
+!
+
+mapNode: node
+
+ currentInstr ifNil: [^ self].
+ node isPseudo
+ ifTrue: [node mapInstr: currentInstr]
+ ifFalse: [currentInstr sourceNode: node]
+!
+
+newBlock: sequence
+
+ ^ self newBlock: sequence return: nil
+!
+
+newBlock: sequence return: goto
+
+ | statements block |
+ statements := sequence statements.
+ (goto notNil and: [self isExplicitReturn: goto]) ifTrue: [
+ self addReturn: statements from: goto
+ ].
+ sequence statements: statements.
+ block := RBBlockNode body: sequence.
+ sequence parent: block.
+ Preferences compileBlocksAsClosures ifFalse: [block scope: scope].
+ ^block
+!
+
+newLiteral: literal
+
+ ^ RBLiteralNode value: literal
+!
+
+newSelectorParts: selector
+
+ ^ selector keywords collect: [:word |
+ RBLiteralToken value: word]
+!
+
+newVar: semVar
+
+ ^ RBVariableNode new
+ identifierToken: (RBIdentifierToken value: semVar name start: 0);
+ binding: semVar
+!
+
+simplify: mess
+ "mess is a messageNode. If it is a message created by the compiler convert it back to its normal form"
+
+ | rcvr var |
+" (mess selector == #value and: [mess receiver isLiteral]) ifTrue: [
+ ^ self newVar: (GlobalVar new assoc: mess receiver value; scope: scope)
+ ]."
+
+ (mess selector = #privSetInHolder: and: [mess arguments first isLiteral]) ifTrue: [
+ ^ RBAssignmentNode
+ variable: (self newVar: (GlobalVar new assoc: mess arguments first value; scope: scope) markWrite)
+ value: mess receiver
+ ].
+
+ (mess selector = #privGetInstVar: and:
+ [mess arguments first isLiteral and:
+ [mess receiver isVariable]]) ifTrue: [
+ rcvr := mess receiver binding.
+ rcvr == scope receiverVar ifTrue: [
+ ^ self newVar: (scope receiverVarAt: mess arguments first value)].
+ (rcvr isContextVar and: [mess arguments first value == 5]) ifTrue: [
+ var := scope tempVarAt: -1.
+ ^self newVar: var].
+ (rcvr isCaptured and:[rcvr sourceTemp = rcvr scope receiverVar])
+ ifTrue:[
+ self fixInnerFreeVar: rcvr.
+ ^self newVar: (rcvr scope receiverVarAt: mess arguments first value)].
+ rcvr isEnv ifTrue: [^self newVar: (rcvr scope captureVarAt: mess arguments first value)]].
+
+ (mess selector = #privStoreIn:instVar: and:
+ [mess arguments last isLiteral and:
+ [mess arguments first isVariable]]) ifTrue: [
+ rcvr := mess arguments first binding.
+ (mess receiver name = 'self' and: [rcvr isEnv])
+ ifTrue:[scope captureSelf: mess arguments last value.
+ ^mess].
+ rcvr == scope receiverVar ifTrue: [^ RBAssignmentNode
+ variable: (self newVar: (scope receiverVarForAssignmentAt: mess arguments last value) markWrite)
+ value: mess receiver].
+ (rcvr isCaptured and:[rcvr sourceTemp = rcvr scope receiverVar])
+ ifTrue:[
+ self fixInnerFreeVar: rcvr.
+ ^RBAssignmentNode
+ variable: (self newVar: (rcvr scope receiverVarForAssignmentAt: mess arguments last value) markWrite)
+ value: mess receiver].
+ mess isClosureEnvironmentRegistration
+ ifTrue: [
+ scope captureSelf: mess arguments last value.
+ ^mess].
+ rcvr isEnv ifTrue:[
+ mess receiver isTemp
+ ifTrue:[var := (scope
+ captureVarAt: mess arguments last value
+ sourceTemp: mess receiver binding) markWrite.]
+ ifFalse:[var := (scope
+ captureVarAt: mess arguments last value sourceTemp: ((TempVar new)
+ name: (scope captureVarName: mess arguments last value);
+ index: mess arguments last value;
+ scope: self;
+ cantBeCapture)) markWrite
+ ].
+ ^ RBAssignmentNode
+ variable: (self newVar: var)
+ value: mess receiver]].
+ ^mess
+!
+
+simplifyTempAssign: assignment
+ "If it is a assignment created by the compiler convert it back to its normal form"
+
+ | mess |
+ ((mess := assignment value) isMessage and:
+ [mess selector = #wrapInTempHolder and:
+ [mess receiver isLiteral: [:v | v isNil]]]
+ ) ifTrue: [
+ ^ nil "no-op"
+ ].
+
+ ^ assignment
+! !
+
+!IRDecompiler methodsFor:'stack'!
+
+Assignment
+
+ | node |
+ (node := self stackDown) isAssignment ifTrue: [^ node].
+ self abort
+!
+
+Block
+
+ | node |
+ (node := self stackDown) isBlock ifTrue: [^ node].
+ self abort
+!
+
+Dup
+
+ | node |
+ (node := self stackDown) isDup ifTrue: [^ node].
+ self abort
+!
+
+Goto
+
+ | node |
+ (node := self stackDown) isGoto ifTrue: [^ node].
+ self abort
+!
+
+Goto: seqNum
+
+ | goto |
+ (goto := self Goto) destination = seqNum ifTrue: [^ goto].
+ self abort
+!
+
+GotoOrReturn: seqNum
+
+ | goto |
+ goto := self Goto.
+ (goto destination = seqNum or: [goto isRet]) ifTrue: [^ goto].
+ self abort
+!
+
+If
+
+ | node |
+ (node := self stackDown) isIf ifTrue: [^ node].
+ self abort
+!
+
+IfGoto: seqNum otherwise: seqNum2
+
+ | if |
+ ((if := self If) destination = seqNum and: [if otherwise = seqNum2])
+ ifTrue: [^ if].
+ self abort
+!
+
+Label
+
+ | node |
+ (node := self stackDown) isLabel ifTrue: [^ node].
+ self abort
+!
+
+Label: seqNum
+
+ | label |
+ (label := self Label) destination = seqNum ifTrue: [^ label].
+ self abort
+!
+
+Pop
+
+ | node |
+ (node := self stackDown) isPop ifTrue: [^ node].
+ self abort
+!
+
+Send
+
+ | node |
+ (node := self stackDown) isPseudoSend ifTrue: [^ node].
+ self abort
+!
+
+Sequence
+ | node seq i goto |
+ seq := RBSequenceNode statements: #().
+ i := self spIndex.
+ [node := stack at: i.
+ node isSequence
+ ifTrue:
+ [seq addNodesFirst: node statements.
+ node := stack at: (i := i - 1)].
+ (node isLabel and: [i > 1])
+ ifFalse:
+ [sp := i.
+ ^ seq].
+ goto := stack at: (i := i - 1).
+ goto isGoto and: [goto destination = node destination]]
+ whileTrue: [i := i - 1].
+ sp := i + 1.
+ ^ seq
+!
+
+Sequence2
+ | node seq i block temps label |
+ seq := RBSequenceNode statements: #().
+ i := self spIndex.
+ node := stack at: i.
+ [(node isLabel and: [(stack at: i - 1) isGoto] and:[node destination = (stack at: i - 1) destination])
+ ifTrue:[
+ i := i - 2.
+ node := stack at: i].
+ (node isLabel not and: [i > 1])] whileTrue:
+ [
+ node isSequence
+ ifTrue: [seq addNodesFirst: node statements]
+ ifFalse: [seq addNodeFirst: node].
+ i := i - 1.
+ node := stack at: i].
+ sp := i.
+ label := self Label.
+ block := self Block.
+ self stackPush: block.
+ self stackPush: label.
+ "Add the temporaries find"
+ temps := scope tempVars asArray allButFirst.
+ temps := temps select: [:each | ((block arguments
+ collect: [:var | var binding]) includes: each) not].
+ seq temporaries: (temps collect: [:var | self newVar: var]).
+ ^ seq
+!
+
+SequenceBackTo: labelNum
+ | node seq i goto |
+ seq := RBSequenceNode statements: #().
+ i := self spIndex.
+ [node := stack at: i.
+ node isSequence
+ ifTrue:
+ [seq addNodesFirst: node statements.
+ node := stack at: (i := i - 1)].
+ (node isLabel and: [i > 1])
+ ifFalse:
+ [sp := i.
+ ^ seq].
+ node destination = labelNum
+ ifTrue:
+ [sp := i.
+ ^ seq].
+ goto := stack at: (i := i - 1).
+ goto isGoto and: [goto destination = node destination]]
+ whileTrue: [i := i - 1].
+ sp := i + 1.
+ ^ seq
+!
+
+SequenceOtherwise
+ | node seq i |
+ seq := RBSequenceNode statements: #().
+ i := self spIndex.
+ node := stack at: i.
+ node isSequence ifTrue: [
+ seq addNodesFirst: node statements.
+ self stackDown]
+ ifFalse:[node isLabel ifFalse:[self abort]].
+ ^ seq
+!
+
+Value
+
+ | node |
+ node := self ValueOrNone.
+ node ifNil: [self abort].
+ ^ node
+!
+
+ValueOrNone
+ | node i label |
+ i := self spIndex.
+ [node := stack at: i.
+ node isValue
+ ifTrue:
+ [label ifNotNil: [valueLabelMap at: node put: label].
+ sp := i - 1.
+ ^ node].
+ (node isLabel and: [i > 1]) ifFalse: [^ nil].
+ label := node.
+ node := stack at: (i := i - 1).
+ node isGoto and: [node destination = label destination]]
+ whileTrue: [i := i - 1].
+ ^ nil
+!
+
+abort
+
+ | spWas |
+ spWas := sp.
+ sp := nil.
+ Abort signal
+!
+
+fixStack
+
+ sp ifNotNil: [stack removeLast: (stack size - sp)].
+ sp := nil.
+!
+
+spIndex
+ ^ sp ifNil: [sp := stack size]
+!
+
+stackDown
+
+ | node |
+ sp ifNil: [sp _ stack size].
+ sp = 0 ifTrue: [self abort].
+ node _ stack at: sp.
+ sp _ sp - 1.
+ ^ node
+!
+
+stackPush: node
+
+ self fixStack.
+ stack addLast: node.
+ node ifNil: [^ self]. "no op"
+ self mapNode: node.
+! !
+
+!IRDecompiler class methodsFor:'documentation'!
+
+version
+ ^ '$Id$'
+!
+
+version_CVS
+ ^ '§Header: /cvs/stx/cvut/stx/goodies/newcompiler/IRDecompiler.st,v 1.3 2009/10/08 12:04:20 fm Exp §'
+!
+
+version_SVN
+ ^ '$Id:: $'
+! !