IRDecompiler.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Mon, 29 Oct 2012 10:41:23 +0000
changeset 40 c99f058e2276
parent 37 be8c2dd09dff
child 41 f3898a3b378d
permissions -rw-r--r--
- removed dependency on an obsolete libtool3

"{ 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::                                                                                                                        $'
! !