The Inbox: Compiler-nice.186.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Compiler-nice.186.mcz

commits-2
A new version of Compiler was added to project The Inbox:
http://source.squeak.org/inbox/Compiler-nice.186.mcz

==================== Summary ====================

Name: Compiler-nice.186
Author: nice
Time: 3 February 2011, 11:26:04.738 pm
UUID: 9bdb53d0-56a1-4d0b-9835-f60a135dd7f6
Ancestors: Compiler-nice.184

Add both Compiler and Decompiler support for inlined #repeat.

Implementation notes:

For compilation, the repeat is implemented with a simple backward jump if ever the receiver is a block.

For decompilation, things are a bit more tedious because conditonnal loops (whileTrue/False) must be differentiated from unconditional loops (repeat).
The signature of conditional loops is that they all have their backward jump hoping over their conditional jump.
If a backward jump does not cross any conditional jump on its way back, then it must be a repeat.
The idea is thus to register the program counter of the test instruction before the conditional jump (lastJumpIfPc), and verify if the bacward jump branch after or before this instruction.
Things are a bit more complex because there can be a conditional instruction inside the repeat body.
That's why those conditional jumps must be stacked (on lastJumpIfPcStack), and unstacked once decompiled.

=============== Diff against Compiler-nice.184 ===============

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'
- instanceVariableNames: 'constructor method instVars tempVars constTable stack statements lastPc exit caseExits lastJumpPc lastReturnPc limit hasValue blockStackBase numLocalTemps blockStartsToTempVars tempVarCount'
  classVariableNames: 'ArgumentFlag CascadeFlag CaseFlag IfNilFlag'
  poolDictionaries: ''
  category: 'Compiler-Kernel'!
 
+ !Decompiler commentStamp: 'nice 2/3/2011 22:54' prior: 0!
- !Decompiler commentStamp: 'nice 3/1/2010 19:56' 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!
- tempVarCount <Integer> number of temp vars used by the method!

Item was changed:
  ----- Method: Decompiler>>decompile:in:method:using: (in category 'public access') -----
  decompile: aSelector in: aClass method: aMethod using: aConstructor
 
  | block node |
  constructor := aConstructor.
  method := aMethod.
  self initSymbols: aClass.  "create symbol tables"
  method isQuick
  ifTrue: [block := self quickMethod]
  ifFalse:
  [stack := OrderedCollection new: method frameSize.
+ lastJumpIfPcStack := OrderedCollection new.
  caseExits := OrderedCollection new.
  statements := OrderedCollection new: 20.
  numLocalTemps := 0.
  super method: method pc: method initialPC.
  "skip primitive error code store if necessary"
  (method primitive ~= 0 and: [self willStore]) ifTrue:
  [pc := pc + 2.
  tempVars := tempVars asOrderedCollection].
  block := self blockTo: method endPC + 1.
  stack isEmpty ifFalse: [self error: 'stack not empty']].
  node := constructor
  codeMethod: aSelector
  block: block
  tempVars: tempVars
  primitive: method primitive
  class: aClass.
  method primitive > 0 ifTrue:
  [node removeAndRenameLastTempIfErrorCode].
  ^node preen!

Item was changed:
  ----- Method: Decompiler>>jump: (in category 'instruction decoding') -----
  jump: dist
+ | blockBody destPc nextPC |
+ destPc := pc + dist.
+ (lastJumpIfPcStack isEmpty or: [dist < 0 and: [destPc > lastJumpIfPcStack last]])
+ ifTrue:
+ ["Rule: aBackward jump not crossing a Bfp/Btp must be a repeat"
+ nextPC := pc.
+ pc := destPc.
+ blockBody := self statementsTo: lastPc.
+ blockBody size timesRepeat: [statements removeLast].
+ pc := nextPC.
+ statements addLast:
+ (constructor
+ codeMessage: (constructor codeBlock: blockBody returns: false)
+ selector: (constructor
+ codeSelector: #repeat
+ code: #macro)
+ arguments: #()).
+ ]
+ ifFalse:
+ [exit := destPc.
+ lastJumpPc := lastPc]!
-
- exit := pc + dist.
- lastJumpPc := lastPc!

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 blockBody |
+ lastJumpIfPcStack addLast: lastPc.
+ stack last == CascadeFlag ifTrue: [^ [self case: dist] ensure: [lastJumpIfPcStack removeLast]].
- stack last == CascadeFlag ifTrue: [^ self case: dist].
  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:
  ["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 re-decompiling."
  stack := saveStack.
  pc := thenJump.
  blockBody := self statementsTo: elsePc.
  "discard unwanted statements from block"
  blockBody size - 1 timesRepeat: [statements removeLast].
  statements addLast:
  (constructor
  codeMessage: (constructor codeBlock: blockBody returns: false)
  selector: (constructor
  codeSelector: (sign
  ifTrue: [#whileFalse:]
  ifFalse: [#whileTrue:])
  code: #macro)
  arguments: { thenBlock }).
  pc := elseStart.
  self convertToDoLoop]
  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:
  [constructor
  codeMessage: ifExpr
  selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)
  arguments: (sign
  ifTrue: [{elseBlock. thenBlock}]
  ifFalse: [{thenBlock. elseBlock}])].
  stack := saveStack.
  condHasValue
  ifTrue: [stack addLast: cond]
+ ifFalse: [statements addLast: cond]].
+ lastJumpIfPcStack removeLast.!
- ifFalse: [statements addLast: cond]]!

Item was changed:
  ----- Method: MessageNode class>>initialize (in category 'class initialization') -----
+ initialize
+ "MessageNode initialize"
- initialize "MessageNode initialize"
  MacroSelectors :=
  #( ifTrue: ifFalse: ifTrue:ifFalse: ifFalse:ifTrue:
  and: or:
  whileFalse: whileTrue: whileFalse whileTrue
  to:do: to:by:do:
  caseOf: caseOf:otherwise:
+ ifNil: ifNotNil:  ifNil:ifNotNil: ifNotNil:ifNil:
+ repeat ).
- ifNil: ifNotNil:  ifNil:ifNotNil: ifNotNil:ifNil:).
  MacroTransformers :=
  #( transformIfTrue: transformIfFalse: transformIfTrueIfFalse: transformIfFalseIfTrue:
  transformAnd: transformOr:
  transformWhile: transformWhile: transformWhile: transformWhile:
  transformToDo: transformToDo:
  transformCase: transformCase:
+ transformIfNil: transformIfNil:  transformIfNilIfNotNil: transformIfNotNilIfNil:
+ transformRepeat: ).
- transformIfNil: transformIfNil:  transformIfNilIfNotNil: transformIfNotNilIfNil:).
  MacroEmitters :=
  #( emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
  emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
  emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
  emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value:
  emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value:
  emitCodeForToDo:encoder:value: emitCodeForToDo:encoder:value:
  emitCodeForCase:encoder:value: emitCodeForCase:encoder:value:
  emitCodeForIfNil:encoder:value: emitCodeForIfNil:encoder:value:
+ emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
+ emitCodeForRepeat:encoder:value:).
- emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:).
  MacroSizers :=
  #( sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value:
  sizeCodeForIf:value: sizeCodeForIf:value:
  sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForWhile:value:
  sizeCodeForToDo:value: sizeCodeForToDo:value:
  sizeCodeForCase:value: sizeCodeForCase:value:
+ sizeCodeForIfNil:value: sizeCodeForIfNil:value: sizeCodeForIf:value: sizeCodeForIf:value:
+ sizeCodeForRepeat:value:).
- sizeCodeForIfNil:value: sizeCodeForIfNil:value: sizeCodeForIf:value: sizeCodeForIf:value:).
  MacroPrinters :=
  #( printIfOn:indent: printIfOn:indent: printIfOn:indent: printIfOn:indent:
  printIfOn:indent: printIfOn:indent:
  printWhileOn:indent: printWhileOn:indent: printWhileOn:indent: printWhileOn:indent:
  printToDoOn:indent: printToDoOn:indent:
  printCaseOn:indent: printCaseOn:indent:
+ printIfNil:indent: printIfNil:indent: printIfNilNotNil:indent: printIfNilNotNil:indent:
+ printRepeatOn:indent:)!
- printIfNil:indent: printIfNil:indent: printIfNilNotNil:indent: printIfNilNotNil:indent:)!

Item was added:
+ ----- Method: MessageNode>>emitCodeForRepeat:encoder:value: (in category 'code generation') -----
+ emitCodeForRepeat: stack encoder: encoder value: forValue
+ " L1: ... Jmp(L1)"
+ | loopSize |
+ loopSize := sizes at: 1.
+ receiver emitCodeForEvaluatedEffect: stack encoder: encoder.
+ self emitCodeForJump: 0 - loopSize encoder: encoder.
+ forValue ifTrue: [encoder genPushSpecialLiteral: nil. stack push: 1]!

Item was added:
+ ----- Method: MessageNode>>printRepeatOn:indent: (in category 'printing') -----
+ printRepeatOn: aStream indent: level
+
+ self printReceiver: receiver on: aStream indent: level.
+
+ ^self printKeywords: selector key
+ arguments: (Array new)
+ on: aStream indent: level!

Item was added:
+ ----- Method: MessageNode>>sizeCodeForRepeat:value: (in category 'code generation') -----
+ sizeCodeForRepeat: encoder value: forValue
+ "L1: ... Jmp(L1) nil (nil for value only);"
+ | loopSize |
+ loopSize := (receiver sizeCodeForEvaluatedEffect: encoder) + (encoder sizeJumpLong: 1).
+ sizes := Array with: loopSize.
+ ^loopSize + (forValue ifTrue: [encoder sizePushSpecialLiteral: nil] ifFalse: [0])!

Item was added:
+ ----- Method: MessageNode>>transformRepeat: (in category 'macro transformations') -----
+ transformRepeat: encoder
+ "answer true if this #repeat message can be optimized"
+
+ ^(self checkBlock: receiver as: 'receiver' from: encoder)
+   and: [receiver noteOptimizedIn: self.
+ true]!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Compiler-nice.186.mcz

Nicolas Cellier
Oh, my class comment is poor. It should have better been

lastJumpIfPcStack <OrderedCollection of: Integer> the value of program
counter just before the conditional jumps not yet decoded so far

This goes into the inbox for 2 reasons
- I don't want to interfere with final 4.2
- 4 eyes better than 2 on complex code

Nicolas

2011/2/3  <[hidden email]>:

> A new version of Compiler was added to project The Inbox:
> http://source.squeak.org/inbox/Compiler-nice.186.mcz
>
> ==================== Summary ====================
>
> Name: Compiler-nice.186
> Author: nice
> Time: 3 February 2011, 11:26:04.738 pm
> UUID: 9bdb53d0-56a1-4d0b-9835-f60a135dd7f6
> Ancestors: Compiler-nice.184
>
> Add both Compiler and Decompiler support for inlined #repeat.
>
> Implementation notes:
>
> For compilation, the repeat is implemented with a simple backward jump if ever the receiver is a block.
>
> For decompilation, things are a bit more tedious because conditonnal loops (whileTrue/False) must be differentiated from unconditional loops (repeat).
> The signature of conditional loops is that they all have their backward jump hoping over their conditional jump.
> If a backward jump does not cross any conditional jump on its way back, then it must be a repeat.
> The idea is thus to register the program counter of the test instruction before the conditional jump (lastJumpIfPc), and verify if the bacward jump branch after or before this instruction.
> Things are a bit more complex because there can be a conditional instruction inside the repeat body.
> That's why those conditional jumps must be stacked (on lastJumpIfPcStack), and unstacked once decompiled.
>
> =============== Diff against Compiler-nice.184 ===============
>
> 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'
> -       instanceVariableNames: 'constructor method instVars tempVars constTable stack statements lastPc exit caseExits lastJumpPc lastReturnPc limit hasValue blockStackBase numLocalTemps blockStartsToTempVars tempVarCount'
>        classVariableNames: 'ArgumentFlag CascadeFlag CaseFlag IfNilFlag'
>        poolDictionaries: ''
>        category: 'Compiler-Kernel'!
>
> + !Decompiler commentStamp: 'nice 2/3/2011 22:54' prior: 0!
> - !Decompiler commentStamp: 'nice 3/1/2010 19:56' 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!
> -       tempVarCount <Integer> number of temp vars used by the method!
>
> Item was changed:
>  ----- Method: Decompiler>>decompile:in:method:using: (in category 'public access') -----
>  decompile: aSelector in: aClass method: aMethod using: aConstructor
>
>        | block node |
>        constructor := aConstructor.
>        method := aMethod.
>        self initSymbols: aClass.  "create symbol tables"
>        method isQuick
>                ifTrue: [block := self quickMethod]
>                ifFalse:
>                        [stack := OrderedCollection new: method frameSize.
> +                       lastJumpIfPcStack := OrderedCollection new.
>                        caseExits := OrderedCollection new.
>                        statements := OrderedCollection new: 20.
>                        numLocalTemps := 0.
>                        super method: method pc: method initialPC.
>                        "skip primitive error code store if necessary"
>                        (method primitive ~= 0 and: [self willStore]) ifTrue:
>                                [pc := pc + 2.
>                                 tempVars := tempVars asOrderedCollection].
>                        block := self blockTo: method endPC + 1.
>                        stack isEmpty ifFalse: [self error: 'stack not empty']].
>        node := constructor
>                                codeMethod: aSelector
>                                block: block
>                                tempVars: tempVars
>                                primitive: method primitive
>                                class: aClass.
>        method primitive > 0 ifTrue:
>                [node removeAndRenameLastTempIfErrorCode].
>        ^node preen!
>
> Item was changed:
>  ----- Method: Decompiler>>jump: (in category 'instruction decoding') -----
>  jump: dist
> +       | blockBody destPc nextPC |
> +       destPc := pc + dist.
> +       (lastJumpIfPcStack isEmpty or: [dist < 0 and: [destPc > lastJumpIfPcStack last]])
> +               ifTrue:
> +                       ["Rule: aBackward jump not crossing a Bfp/Btp must be a repeat"
> +                       nextPC := pc.
> +                       pc := destPc.
> +                       blockBody := self statementsTo: lastPc.
> +                       blockBody size timesRepeat: [statements removeLast].
> +                       pc := nextPC.
> +                       statements addLast:
> +                               (constructor
> +                                       codeMessage: (constructor codeBlock: blockBody returns: false)
> +                                       selector: (constructor
> +                                                               codeSelector: #repeat
> +                                                               code: #macro)
> +                                       arguments: #()).
> +                       ]
> +               ifFalse:
> +                       [exit := destPc.
> +                       lastJumpPc := lastPc]!
> -
> -       exit := pc + dist.
> -       lastJumpPc := lastPc!
>
> 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 blockBody |
> +       lastJumpIfPcStack addLast: lastPc.
> +       stack last == CascadeFlag ifTrue: [^ [self case: dist] ensure: [lastJumpIfPcStack removeLast]].
> -       stack last == CascadeFlag ifTrue: [^ self case: dist].
>        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:
>                        ["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 re-decompiling."
>                        stack := saveStack.
>                        pc := thenJump.
>                        blockBody := self statementsTo: elsePc.
>                        "discard unwanted statements from block"
>                        blockBody size - 1 timesRepeat: [statements removeLast].
>                        statements addLast:
>                                (constructor
>                                        codeMessage: (constructor codeBlock: blockBody returns: false)
>                                        selector: (constructor
>                                                                codeSelector: (sign
>                                                                                                ifTrue: [#whileFalse:]
>                                                                                                ifFalse: [#whileTrue:])
>                                                                code: #macro)
>                                        arguments: { thenBlock }).
>                        pc := elseStart.
>                        self convertToDoLoop]
>                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:
>                                                        [constructor
>                                                                codeMessage: ifExpr
>                                                                selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)
>                                                                arguments:      (sign
>                                                                                                ifTrue: [{elseBlock. thenBlock}]
>                                                                                                ifFalse: [{thenBlock. elseBlock}])].
>                        stack := saveStack.
>                        condHasValue
>                                ifTrue: [stack addLast: cond]
> +                               ifFalse: [statements addLast: cond]].
> +       lastJumpIfPcStack removeLast.!
> -                               ifFalse: [statements addLast: cond]]!
>
> Item was changed:
>  ----- Method: MessageNode class>>initialize (in category 'class initialization') -----
> + initialize
> +       "MessageNode initialize"
> - initialize            "MessageNode initialize"
>        MacroSelectors :=
>                #(      ifTrue: ifFalse: ifTrue:ifFalse: ifFalse:ifTrue:
>                        and: or:
>                        whileFalse: whileTrue: whileFalse whileTrue
>                        to:do: to:by:do:
>                        caseOf: caseOf:otherwise:
> +                       ifNil: ifNotNil:  ifNil:ifNotNil: ifNotNil:ifNil:
> +                       repeat ).
> -                       ifNil: ifNotNil:  ifNil:ifNotNil: ifNotNil:ifNil:).
>        MacroTransformers :=
>                #(      transformIfTrue: transformIfFalse: transformIfTrueIfFalse: transformIfFalseIfTrue:
>                        transformAnd: transformOr:
>                        transformWhile: transformWhile: transformWhile: transformWhile:
>                        transformToDo: transformToDo:
>                        transformCase: transformCase:
> +                       transformIfNil: transformIfNil:  transformIfNilIfNotNil: transformIfNotNilIfNil:
> +                       transformRepeat: ).
> -                       transformIfNil: transformIfNil:  transformIfNilIfNotNil: transformIfNotNilIfNil:).
>        MacroEmitters :=
>                #(      emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
>                        emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
>                        emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
>                        emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value:
>                        emitCodeForWhile:encoder:value: emitCodeForWhile:encoder:value:
>                        emitCodeForToDo:encoder:value: emitCodeForToDo:encoder:value:
>                        emitCodeForCase:encoder:value: emitCodeForCase:encoder:value:
>                        emitCodeForIfNil:encoder:value: emitCodeForIfNil:encoder:value:
> +                       emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:
> +                       emitCodeForRepeat:encoder:value:).
> -                       emitCodeForIf:encoder:value: emitCodeForIf:encoder:value:).
>        MacroSizers :=
>                #(      sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value: sizeCodeForIf:value:
>                        sizeCodeForIf:value: sizeCodeForIf:value:
>                        sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForWhile:value: sizeCodeForWhile:value:
>                        sizeCodeForToDo:value: sizeCodeForToDo:value:
>                        sizeCodeForCase:value: sizeCodeForCase:value:
> +                       sizeCodeForIfNil:value: sizeCodeForIfNil:value: sizeCodeForIf:value: sizeCodeForIf:value:
> +                       sizeCodeForRepeat:value:).
> -                       sizeCodeForIfNil:value: sizeCodeForIfNil:value: sizeCodeForIf:value: sizeCodeForIf:value:).
>        MacroPrinters :=
>                #(      printIfOn:indent: printIfOn:indent: printIfOn:indent: printIfOn:indent:
>                        printIfOn:indent: printIfOn:indent:
>                        printWhileOn:indent: printWhileOn:indent: printWhileOn:indent: printWhileOn:indent:
>                        printToDoOn:indent: printToDoOn:indent:
>                        printCaseOn:indent: printCaseOn:indent:
> +                       printIfNil:indent: printIfNil:indent: printIfNilNotNil:indent: printIfNilNotNil:indent:
> +                       printRepeatOn:indent:)!
> -                       printIfNil:indent: printIfNil:indent: printIfNilNotNil:indent: printIfNilNotNil:indent:)!
>
> Item was added:
> + ----- Method: MessageNode>>emitCodeForRepeat:encoder:value: (in category 'code generation') -----
> + emitCodeForRepeat: stack encoder: encoder value: forValue
> +       " L1: ... Jmp(L1)"
> +       | loopSize |
> +       loopSize := sizes at: 1.
> +       receiver emitCodeForEvaluatedEffect: stack encoder: encoder.
> +       self emitCodeForJump: 0 - loopSize encoder: encoder.
> +       forValue ifTrue: [encoder genPushSpecialLiteral: nil. stack push: 1]!
>
> Item was added:
> + ----- Method: MessageNode>>printRepeatOn:indent: (in category 'printing') -----
> + printRepeatOn: aStream indent: level
> +
> +       self printReceiver: receiver on: aStream indent: level.
> +
> +       ^self printKeywords: selector key
> +               arguments: (Array new)
> +               on: aStream indent: level!
>
> Item was added:
> + ----- Method: MessageNode>>sizeCodeForRepeat:value: (in category 'code generation') -----
> + sizeCodeForRepeat: encoder value: forValue
> +       "L1: ... Jmp(L1) nil (nil for value only);"
> +       | loopSize |
> +       loopSize := (receiver sizeCodeForEvaluatedEffect: encoder) + (encoder sizeJumpLong: 1).
> +       sizes := Array with: loopSize.
> +       ^loopSize + (forValue ifTrue: [encoder sizePushSpecialLiteral: nil] ifFalse: [0])!
>
> Item was added:
> + ----- Method: MessageNode>>transformRepeat: (in category 'macro transformations') -----
> + transformRepeat: encoder
> +       "answer true if this #repeat message can be optimized"
> +
> +       ^(self checkBlock: receiver as: 'receiver' from: encoder)
> +          and: [receiver noteOptimizedIn: self.
> +                       true]!
>
>
>