IRDecompiler.st
changeset 37 be8c2dd09dff
child 41 f3898a3b378d
--- /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::                                                                                                                        $'
+! !