Blocks seems to work. No support for nested block. No support for
compiling cheap blocks.
"{ Package: '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$'
! !