[squeak-dev] The Trunk: Compiler-nice.432.mcz
commits at source.squeak.org
commits at source.squeak.org
Sun May 10 10:50:40 UTC 2020
Nicolas Cellier uploaded a new version of Compiler to project The Trunk:
http://source.squeak.org/trunk/Compiler-nice.432.mcz
==================== Summary ====================
Name: Compiler-nice.432
Author: nice
Time: 10 May 2020, 12:50:37.677856 pm
UUID: f6faf998-9905-4fbd-9bc4-66a2e9f8bc93
Ancestors: Compiler-nice.431
Fix Decompiler after correction byteCodes generated by inlined #caseOf: and recompile all senders of caseOf: in postscript.
Note: I have changed the logic a little bit:
- the ancient CaseFlag is replaced by OtherwiseFlag (that's the purpose, we are trying to detect last case before otherwise:).
- CascadeFlag is replaced by CaseFlag as soon as we have detected a potential caseOf:.
I never put so many Halt in code before having it right. Good luck to the next one wanting to change the Decompiler...
=============== Diff against Compiler-nice.431 ===============
Item was changed:
InstructionStream subclass: #Decompiler
instanceVariableNames: 'constructor method instVars tempVars constTable stack statements lastPc exit caseExits lastJumpPc lastReturnPc limit hasValue blockStackBase numLocalTemps blockStartsToTempVars tempVarCount lastJumpIfPcStack tempReadCounts'
+ classVariableNames: 'ArgumentFlag CascadeFlag CaseFlag IfNilFlag OtherwiseFlag'
- classVariableNames: 'ArgumentFlag CascadeFlag CaseFlag IfNilFlag'
poolDictionaries: ''
category: 'Compiler-Kernel'!
!Decompiler commentStamp: 'nice 2/3/2011 22:54' prior: 0!
I decompile a method in three phases:
Reverser: postfix byte codes -> prefix symbolic codes (nodes and atoms)
Parser: prefix symbolic codes -> node tree (same as the compiler)
Printer: node tree -> text (done by the nodes)
instance vars:
constructor <DecompilerConstructor> an auxiliary knowing how to generate Abstract Syntax Tree (node tree)
method <CompiledMethod> the method being decompiled
instVars <Array of: String> the instance variables of the class implementing method
tempVars <String | (OrderedCollection of: String)> hold the names of temporary variables (if known)
NOTE: POLYMORPHISM WILL BE RESOLVED IN #initSymbols:
constTable <Collection of: ParseNode> parse node associated with byte encoded constants (nil true false 0 1 -1 etc...)
stack <OrderedCollection of: (ParseNode | String | Integer) > multipurpose...
statements <OrderedCollection of: ParseNode> the statements of the method being decompiled
lastPc <Integer>
exit <Integer>
caseExits <OrderedCollection of: Integer> - stack of exit addresses that have been seen in the branches of caseOf:'s
lastJumpPc <Integer>
lastReturnPc <Integer>
limit <Integer>
hasValue <Boolean>
blockStackBase <Integer>
numLocaltemps <Integer | Symbol> - number of temps local to a block; also a flag indicating decompiling a block
blockStartsToTempVars <Dictionary key: Integer value: (OrderedCollection of: String)>
tempVarCount <Integer> number of temp vars used by the method
lastJumpIfPcStack <OrderedCollection of: Integer> the value of program counter just before the last encountered conditional jumps!
Item was changed:
----- Method: Decompiler class>>initialize (in category 'class initialization') -----
initialize
CascadeFlag := 'cascade'. "A unique object"
CaseFlag := 'case'. "Ditto"
+ OtherwiseFlag := 'otherwise'. "Ditto"
ArgumentFlag := 'argument'. "Ditto"
IfNilFlag := 'ifNil'. "Ditto"
"Decompiler initialize"!
Item was changed:
----- Method: Decompiler>>case: (in category 'instruction decoding') -----
case: dist
"statements = keyStmts CascadeFlag keyValueBlock ... keyStmts"
| nextCase thenJump stmtStream elements b node cases otherBlock myExits |
nextCase := pc + dist.
+ "Now add CaseFlag & keyValueBlock to statements"
- "Now add CascadeFlag & keyValueBlock to statements"
statements addLast: stack removeLast.
+ "Trick: put a flag on the stack.
+ If it is the last case before otherwise: block, then
+ - there won't be a dup of caseOf: receiver before sending =
+ - there won't be a pop in the case handling block"
+ stack addLast: OtherwiseFlag. "set for next pop"
- stack addLast: CaseFlag. "set for next pop"
statements addLast: (self blockForCaseTo: nextCase).
+
+ stack last == OtherwiseFlag
-
- stack last == CaseFlag
ifTrue: "Last case"
["ensure jump is within block (in case thenExpr returns wierdly I guess)"
stack removeLast. "get rid of CaseFlag"
stmtStream := ReadStream on: (self popTo: stack removeLast).
elements := OrderedCollection new.
b := OrderedCollection new.
[stmtStream atEnd] whileFalse:
+ [(node := stmtStream next) == CaseFlag
- [(node := stmtStream next) == CascadeFlag
ifTrue:
[elements addLast: (constructor
codeMessage: (constructor codeBlock: b returns: false)
selector: (constructor codeSelector: #-> code: #macro)
arguments: (Array with: stmtStream next)).
b := OrderedCollection new]
ifFalse: [b addLast: node]].
b size > 0 ifTrue: [self error: 'Bad cases'].
cases := constructor codeBrace: elements.
"try find the end of the case"
myExits := caseExits removeLast: elements size.
myExits := myExits reject: [ :e | e isNil or: [ e < 0 or: [ e > method endPC ] ] ].
thenJump := myExits isEmpty
ifTrue: [ nextCase ]
ifFalse: [ myExits max ].
otherBlock := self blockTo: thenJump.
stack addLast:
(constructor
codeMessage: stack removeLast
selector: (constructor codeSelector: #caseOf:otherwise: code: #macro)
arguments: (Array with: cases with: otherBlock))].!
Item was changed:
----- Method: Decompiler>>doDup (in category 'instruction decoding') -----
doDup
+ stack last == CaseFlag
+ ifTrue:
+ ["We are in the process of decompiling a caseOf:"
+ stack addLast: CaseFlag.
+ ^self].
-
stack last == CascadeFlag
ifFalse:
["Save position and mark cascade"
stack addLast: statements size.
stack addLast: CascadeFlag].
stack addLast: CascadeFlag!
Item was changed:
----- Method: Decompiler>>doPop (in category 'instruction decoding') -----
doPop
stack isEmpty ifTrue:
["Ignore pop in first leg of ifNil for value"
^ self].
+ stack last == OtherwiseFlag
- stack last == CaseFlag
ifTrue: [stack removeLast]
ifFalse: [statements addLast: stack removeLast].!
Item was changed:
----- Method: Decompiler>>jump:if: (in category 'instruction decoding') -----
jump: dist if: condition
| savePc sign elsePc elseStart end cond ifExpr thenBlock elseBlock
thenJump elseJump condHasValue isIfNil saveStack |
lastJumpIfPcStack addLast: lastPc.
+ stack last == CaseFlag ifTrue: [^ [self case: dist] ensure: [lastJumpIfPcStack removeLast]].
- stack last == CascadeFlag ifTrue: [^ [self case: dist] ensure: [lastJumpIfPcStack removeLast]].
elsePc := lastPc.
elseStart := pc + dist.
end := limit.
"Check for bfp-jmp to invert condition.
Don't be fooled by a loop with a null body."
sign := condition.
savePc := pc.
self interpretJump ifNotNil:
[:elseDist|
(elseDist >= 0 and: [elseStart = pc]) ifTrue:
[sign := sign not. elseStart := pc + elseDist]].
pc := savePc.
ifExpr := stack removeLast.
(isIfNil := stack size > 0 and: [stack last == IfNilFlag]) ifTrue:
[stack removeLast].
saveStack := stack.
stack := OrderedCollection new.
thenBlock := self blockTo: elseStart.
condHasValue := hasValue or: [isIfNil].
"ensure jump is within block (in case thenExpr returns)"
thenJump := exit <= end ifTrue: [exit] ifFalse: [elseStart].
"if jump goes back, then it's a loop"
thenJump < elseStart
ifTrue:
[| blockBody blockArgs savedReadCounts blockBodyReadCounts selector |
"Must be a while loop...
thenJump will jump to the beginning of the while expr. In the case of while's
with a block in the condition, the while expr should include more than just
the last expression: find all the statements needed by searching for the node
with the relevant pc."
stack := saveStack.
savedReadCounts := tempReadCounts copy.
pc := thenJump.
blockBody := self statementsTo: elsePc.
blockBodyReadCounts := tempReadCounts.
savedReadCounts keysAndValuesDo:
[:temp :count|
blockBodyReadCounts at: temp put: (blockBodyReadCounts at: temp) - count].
tempReadCounts := savedReadCounts.
"discard unwanted statements from block"
blockBody size - 1 timesRepeat: [statements removeLast].
blockArgs := thenBlock statements = constructor codeEmptyBlock statements
ifTrue: [#()]
ifFalse: [{ thenBlock }].
selector := blockArgs isEmpty
ifTrue: [sign ifTrue: [#whileFalse] ifFalse: [#whileTrue]]
ifFalse: [sign ifTrue: [#whileFalse:] ifFalse: [#whileTrue:]].
statements addLast:
(constructor
codeMessage: (constructor codeBlock: blockBody returns: false)
selector: (constructor codeSelector: selector code: #macro)
arguments: blockArgs).
pc := elseStart.
selector == #whileTrue: ifTrue:
[self convertToDoLoop: blockBodyReadCounts]]
ifFalse:
["Must be a conditional..."
elseBlock := self blockTo: thenJump.
elseJump := exit.
"if elseJump is backwards, it is not part of the elseExpr"
elseJump < elsePc ifTrue:
[pc := lastPc].
cond := isIfNil
ifTrue:
[constructor
codeMessage: ifExpr ifNilReceiver
selector: (constructor
codeSelector: (sign ifTrue: [#ifNotNil:] ifFalse: [#ifNil:])
code: #macro)
arguments: (Array with: thenBlock)]
ifFalse:
[(sign
ifTrue: [{elseBlock. thenBlock}]
ifFalse: [{thenBlock. elseBlock}]) in:
[:args |
(constructor
decodeIfNilWithReceiver: ifExpr
selector: #ifTrue:ifFalse:
arguments: args
tempReadCounts: tempReadCounts) ifNil:
[constructor
codeMessage: ifExpr
selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)
arguments: args]]].
stack := saveStack.
condHasValue
ifTrue: [stack addLast: cond]
ifFalse: [statements addLast: cond]].
lastJumpIfPcStack removeLast.!
Item was changed:
----- Method: Decompiler>>send:super:numArgs: (in category 'instruction decoding') -----
send: selector super: superFlag numArgs: numArgs
| args rcvr selNode msgNode messages |
args := Array new: numArgs.
(numArgs to: 1 by: -1) do:
[:i | args at: i put: stack removeLast].
rcvr := stack removeLast.
superFlag ifTrue: [rcvr := constructor codeSuper].
selNode := constructor codeAnySelector: selector.
+ rcvr == CaseFlag
+ ifTrue:
+ [| cases stmtStream elements node b |
+ selector == #= ifTrue:
+ [" = signals a case statement..."
+ statements addLast: args first.
+ stack addLast: rcvr. "restore CaseFlag"
+ ^ self].
+ selector = #caseError ifFalse: [self error: 'unexpected message send while decompiling a caseOf:'].
+ stmtStream := ReadStream on: (self popTo: stack removeLast).
+
+ elements := OrderedCollection new.
+ b := OrderedCollection new.
+ [stmtStream atEnd] whileFalse:
+ [(node := stmtStream next) == CaseFlag
+ ifTrue:
+ [elements addLast: (constructor
+ codeMessage: (constructor codeBlock: b returns: false)
+ selector: (constructor codeSelector: #-> code: #macro)
+ arguments: (Array with: stmtStream next)).
+ b := OrderedCollection new]
+ ifFalse: [b addLast: node]].
+ b size > 0 ifTrue: [self error: 'Bad cases'].
+ cases := constructor codeBrace: elements.
+
+ stack addLast:
+ (constructor
+ codeMessage: stack removeLast
+ selector: (constructor codeSelector: #caseOf: code: #macro)
+ arguments: (Array with: cases)).
+ ^self].
rcvr == CascadeFlag
ifTrue:
["May actually be a cascade or an ifNil: for value."
self willJumpIfFalse
ifTrue: "= generated by a case macro"
[selector == #= ifTrue:
[" = signals a case statement..."
statements addLast: args first.
+ stack removeLast; addLast: CaseFlag; addLast: CaseFlag. "Properly mark the case statement"
- stack addLast: rcvr. "restore CascadeFlag"
^ self].
selector == #== ifTrue:
[" == signals an ifNil: for value..."
stack removeLast; removeLast.
rcvr := stack removeLast.
stack addLast: IfNilFlag;
addLast: (constructor
codeMessage: rcvr
selector: selNode
arguments: args).
^ self]]
ifFalse:
[(self willJumpIfTrue and: [selector == #==]) ifTrue:
[" == signals an ifNotNil: for value..."
stack removeLast; removeLast.
rcvr := stack removeLast.
stack addLast: IfNilFlag;
addLast: (constructor
codeMessage: rcvr
selector: selNode
arguments: args).
^ self]].
msgNode := constructor
codeCascadedMessage: selNode
arguments: args.
stack last == CascadeFlag ifFalse:
["Last message of a cascade"
statements addLast: msgNode.
messages := self popTo: stack removeLast. "Depth saved by first dup"
msgNode := constructor
codeCascade: stack removeLast
messages: messages]]
ifFalse:
[msgNode := constructor
codeMessage: rcvr
selector: selNode
arguments: args].
stack addLast: msgNode!
Item was changed:
----- Method: Decompiler>>statementsForCaseTo: (in category 'control') -----
statementsForCaseTo: end
"Decompile the method from pc up to end and return an array of
expressions. If at run time this block will leave a value on the stack,
set hasValue to true. If the block ends with a jump or return, set exit
to the destination of the jump, or the end of the method; otherwise, set
exit = end. Leave pc = end.
+ Note that stack initially contains a OtherwiseFlag which will be removed by
- Note that stack initially contains a CaseFlag which will be removed by
a subsequent Pop instruction, so adjust the StackPos accordingly."
| blockPos stackPos |
blockPos := statements size.
+ stackPos := stack size - 1. "Adjust for OtherwiseFlag"
- stackPos := stack size - 1. "Adjust for CaseFlag"
[pc < end]
whileTrue:
[lastPc := pc. limit := end. "for performs"
self interpretNextInstructionFor: self].
"If there is an additional item on the stack, it will be the value
of this block."
(hasValue := stack size > stackPos)
ifTrue:
+ [stack last == OtherwiseFlag
- [stack last == CaseFlag
ifFalse: [ statements addLast: stack removeLast] ].
lastJumpPc = lastPc ifFalse: [exit := pc].
caseExits add: exit.
^self popTo: blockPos!
Item was changed:
(PackageInfo named: 'Compiler') postscript: '"below, add code to be run after the loading of this package"
+ "Recompile senders of caseOf:"
+ self systemNavigation allSelectorsAndMethodsDo: [ :behavior :selector :method |
+ (method hasLiteral: #caseOf:)
+ ifTrue: [behavior recompile: selector]]'!
- "Make all relevant literals read-only, avoiding the recompile step, so as to avoid unbound methods"
- self systemNavigation allSelect:
- [:m| | b |
- b := #notNil.
- b := [:lit| lit isCollection ifTrue: [lit beReadOnlyObject. lit isArray ifTrue: [lit do: b "do: b do:"]]].
- m allLiteralsDo:
- [:l|
- (l isLiteral
- and: [(l isCollection or: [l isNumber and: [l isReadOnlyObject not]])
- and: [(l isArray and: [m primitive == 117 and: [l == (m literalAt: 1)]]) not]]) ifTrue:
- [b value: l]].
- false]'!
More information about the Squeak-dev
mailing list
|