[Vm-dev] VM Maker: VMMaker.oscog-cb.1875.mcz

Clément Bera bera.clement at gmail.com
Fri May 27 12:05:11 UTC 2016


Hi,

Final results:
                               | Immutability overhead before this commit |
Immutability overhead now
setter (setting 1 IV with arg) |                       18.3%              |
         4.8%
imm:nonImm: method (4 IV)      |                       18.7%              |
         2.6%

So I believe the immutability / write barrier overhead can be up to 5% in
some cases.
However, the Binarytree bench is intensive in instance variable stores and
there is no visible difference in performance.

If Eliot validates the commit, I consider now that immutability integration
in Cog is finished.

We still plan to change the implementation of uncommon trampolines
(mustBeBoolean and cannotAssign:withIndex:) to optimize register
allocation, which is indirectly related.


On Fri, May 27, 2016 at 11:43 AM, <commits at source.squeak.org> wrote:

>
> ClementBera uploaded a new version of VMMaker to project VM Maker:
> http://source.squeak.org/VMMaker/VMMaker.oscog-cb.1875.mcz
>
> ==================== Summary ====================
>
> Name: VMMaker.oscog-cb.1875
> Author: cb
> Time: 27 May 2016, 11:43:40.369719 am
> UUID: 4da4e9c0-8ef3-4400-b610-73c7959006ce
> Ancestors: VMMaker.oscog-eem.1874
>
> Anti-slavery society forms (NY)...
>
> When I benched Immutability/write barrier, games benchmarks had identical
> performance with and without it (the difference was within noise).
>
> However I built a microbench doing only inst var stores:
>
> Foo>>imm: imm nonImm: nonImm
>         iv1 := 1.
>         iv2 := #foo.
>         iv3 := imm.
>         iv4 := nonImm.
>
> | f |
> f := Foo new.
> [f imm: 2 nonImm: #bar ] bench
>
> And in this case there was 18.7% overhead with the write barrier.
> BinaryTree bench has a similar method, use it extensively and yet we can't
> see the overhead on the whole bench, so it's not that critical, but it's
> still something which could matter in specific cases.
>
> In this commit I changed the JIT so that (with Immutability/Write barrier
> ON) methods with only frameless instance variable stores are compiled with
> two paths. The code first checks if the receiver is immutable/read-only,
> and takes the right path accordingly. The first path is frameless and does
> not include immutability/write barrier checks. The second one is frameful
> and does all the immutability/write barrier checks.
>
> I didn't do it for blocks as I believe blocks with only instance variable
> store are not that common - I may be wrong, if you prove to me I am wrong
> on this with numbers, I will do it for blocks.
>
> In the micro-bench shown, #imm:nonImm:, the overhead decreased from 18.7%
> to 2.6%.
>
> I think this has an impact in general to setter methods.
>
> I made that in a Pharo sprint (or what it matters).
>
> =============== Diff against VMMaker.oscog-eem.1874 ===============
>
> Item was changed:
>   ----- Method: RegisterAllocatingCogit>>scanMethod (in category 'compile
> abstract instructions') -----
>   scanMethod
>         "Overrides to count the number of fixups."
>         "Scan the method (and all embedded blocks) to determine
>                 - what the last bytecode is; extra bytes at the end of a
> method are used to encode things like source pointers or temp names
>                 - if the method needs a frame or not
>                 - what are the targets of any backward branches.
>                 - how many blocks it creates
>          Answer the block count or on error a negative error code"
>         | latestContinuation nExts descriptor pc numBlocks distance
> targetPC framelessStackDelta |
>         <var: #descriptor type: #'BytecodeDescriptor *'>
>         needsFrame := false.
>         numFixups := 0.
>         prevBCDescriptor := nil.
> +       self
> +               cppIf: IMMUTABILITY
> +               ifTrue: [needsTwoPath := false].
>         NewspeakVM ifTrue:
>                 [numIRCs := 0].
>         (primitiveIndex > 0
>          and: [coInterpreter isQuickPrimitiveIndex: primitiveIndex])
> ifTrue:
>                 [^0].
>         pc := latestContinuation := initialPC.
>         numBlocks := framelessStackDelta := nExts := extA := extB := 0.
>         [pc <= endPC] whileTrue:
>                 [byte0 := (objectMemory fetchByte: pc ofObject: methodObj)
> + bytecodeSetOffset.
>                  descriptor := self generatorAt: byte0.
>                  descriptor isExtension ifTrue:
>                         [descriptor opcode = Nop ifTrue: "unknown bytecode
> tag; see Cogit class>>#generatorTableFrom:"
>                                 [^EncounteredUnknownBytecode].
>                          self loadSubsequentBytesForDescriptor: descriptor
> at: pc.
>                          self perform: descriptor generator].
>                  (descriptor isReturn
>                   and: [pc >= latestContinuation]) ifTrue:
>                         [endPC := pc].
> +                 self
> +                       cppIf: IMMUTABILITY
> +                       ifTrue: [(needsFrame and: [needsTwoPath not])
> +                                       ifFalse: [(descriptor
> needsFrameFunction isNil
> +                                                               or: [self
> perform: descriptor needsFrameFunction with: framelessStackDelta])
> +                                                       ifTrue:
> [needsFrame := true.
> +
>  needsTwoPath := descriptor generator ==
> #genStoreAndPopReceiverVariableBytecode]
> +                                                       ifFalse:
> [framelessStackDelta := framelessStackDelta + descriptor stackDelta]]]
> +                       ifFalse: [needsFrame
> +                                       ifFalse: [(descriptor
> needsFrameFunction isNil
> +                                                               or: [self
> perform: descriptor needsFrameFunction with: framelessStackDelta])
> +                                                       ifTrue:
> [needsFrame := true]
> +                                                       ifFalse:
> [framelessStackDelta := framelessStackDelta + descriptor stackDelta]]].
> -                needsFrame ifFalse:
> -                       [(descriptor needsFrameFunction isNil
> -                         or: [self perform: descriptor needsFrameFunction
> with: framelessStackDelta])
> -                               ifTrue: [needsFrame := true]
> -                               ifFalse: [framelessStackDelta :=
> framelessStackDelta + descriptor stackDelta]].
>                  descriptor isBranch ifTrue:
>                         [distance := self spanFor: descriptor at: pc exts:
> nExts in: methodObj.
>                          targetPC := pc + descriptor numBytes + distance.
>                          (self isBackwardBranch: descriptor at: pc exts:
> nExts in: methodObj)
>                                 ifTrue: [self initializeFixupAt: targetPC
> - initialPC]
>                                 ifFalse:
>                                         [latestContinuation :=
> latestContinuation max: targetPC.
>                                         numFixups := numFixups + 1]].
>                  descriptor isBlockCreation ifTrue:
>                         [numBlocks := numBlocks + 1.
>                          distance := self spanFor: descriptor at: pc exts:
> nExts in: methodObj.
>                          targetPC := pc + descriptor numBytes + distance.
>                          latestContinuation := latestContinuation max:
> targetPC.
>                          numFixups := numFixups + 1].
>                  NewspeakVM ifTrue:
>                         [descriptor hasIRC ifTrue:
>                                 [numIRCs := numIRCs + 1]].
>                  pc := pc + descriptor numBytes.
>                  descriptor isExtension
>                         ifTrue: [nExts := nExts + 1]
>                         ifFalse: [nExts := extA := extB := 0].
>                  prevBCDescriptor := descriptor].
>         ^numBlocks!
>
> Item was changed:
>   ----- Method: SistaCogit>>scanMethod (in category 'compile abstract
> instructions') -----
>   scanMethod
>         "Scan the method (and all embedded blocks) to determine
>                 - what the last bytecode is; extra bytes at the end of a
> method are used to encode things like source pointers or temp names
>                 - if the method needs a frame or not
>                 - what are the targets of any backward branches.
>                 - how many blocks it creates
>                 - how many counters it needs/conditional branches it
> contains
>          Answer the block count or on error a negative error code"
>         | latestContinuation nExts descriptor pc numBlocks distance
> targetPC framelessStackDelta numFixups |
>         <var: #descriptor type: #'BytecodeDescriptor *'>
>         self flag: 'numFixup should be reverted to inst var when moving
> back sistaCogit as subclass of RegisterAllocatingCogit'.
>         needsFrame := false.
>         numFixups := 0.
>         prevBCDescriptor := nil.
>         numCounters := 0.
> +       self
> +               cppIf: IMMUTABILITY
> +               ifTrue: [needsTwoPath := false].
>         NewspeakVM ifTrue:
>                 [numIRCs := 0].
>         (primitiveIndex > 0
>          and: [coInterpreter isQuickPrimitiveIndex: primitiveIndex])
> ifTrue:
>                 [^0].
>         pc := latestContinuation := initialPC.
>         numBlocks := framelessStackDelta := nExts := extA := extB := 0.
>         [pc <= endPC] whileTrue:
>                 [byte0 := (objectMemory fetchByte: pc ofObject: methodObj)
> + bytecodeSetOffset.
>                  descriptor := self generatorAt: byte0.
>                  descriptor isExtension ifTrue:
>                         [descriptor opcode = Nop ifTrue: "unknown bytecode
> tag; see Cogit class>>#generatorTableFrom:"
>                                 [^EncounteredUnknownBytecode].
>                          self loadSubsequentBytesForDescriptor: descriptor
> at: pc.
>                          self perform: descriptor generator].
>                  (descriptor isReturn
>                   and: [pc >= latestContinuation]) ifTrue:
>                         [endPC := pc].
> +                self
> +                       cppIf: IMMUTABILITY
> +                       ifTrue: [(needsFrame and: [needsTwoPath not])
> +                                       ifFalse: [(descriptor
> needsFrameFunction isNil
> +                                                               or: [self
> perform: descriptor needsFrameFunction with: framelessStackDelta])
> +                                                       ifTrue:
> [needsFrame := true.
> +
>  needsTwoPath := descriptor generator ==
> #genStoreAndPopReceiverVariableBytecode]
> +                                                       ifFalse:
> [framelessStackDelta := framelessStackDelta + descriptor stackDelta]]]
> +                       ifFalse: [needsFrame
> +                                       ifFalse: [(descriptor
> needsFrameFunction isNil
> +                                                               or: [self
> perform: descriptor needsFrameFunction with: framelessStackDelta])
> +                                                       ifTrue:
> [needsFrame := true]
> +                                                       ifFalse:
> [framelessStackDelta := framelessStackDelta + descriptor stackDelta]]].
> -                needsFrame ifFalse:
> -                       [(descriptor needsFrameFunction isNil
> -                         or: [self perform: descriptor needsFrameFunction
> with: framelessStackDelta])
> -                               ifTrue: [needsFrame := true]
> -                               ifFalse: [framelessStackDelta :=
> framelessStackDelta + descriptor stackDelta]].
>                  descriptor isBranch ifTrue:
>                         [distance := self spanFor: descriptor at: pc exts:
> nExts in: methodObj.
>                          targetPC := pc + descriptor numBytes + distance.
>                          (self isBackwardBranch: descriptor at: pc exts:
> nExts in: methodObj)
>                                 ifTrue: [self initializeFixupAt: targetPC
> - initialPC]
>                                 ifFalse:
>                                         [latestContinuation :=
> latestContinuation max: targetPC.
>                                         numFixups := numFixups + 1.
>                                          (descriptor isBranchTrue or:
> [descriptor isBranchFalse]) ifTrue:
>                                                 [numCounters :=
> numCounters + 1]]].
>                  descriptor isBlockCreation ifTrue:
>                         [numBlocks := numBlocks + 1.
>                          distance := self spanFor: descriptor at: pc exts:
> nExts in: methodObj.
>                          targetPC := pc + descriptor numBytes + distance.
>                          latestContinuation := latestContinuation max:
> targetPC.
>                          numFixups := numFixups + 1].
>                  NewspeakVM ifTrue:
>                         [descriptor hasIRC ifTrue:
>                                 [numIRCs := numIRCs + 1]].
>                  pc := pc + descriptor numBytes.
>                  descriptor isExtension
>                         ifTrue: [nExts := nExts + 1]
>                         ifFalse: [nExts := extA := extB := 0].
>                  prevBCDescriptor := descriptor].
>         ^numBlocks!
>
> Item was changed:
>   SimpleStackBasedCogit subclass: #StackToRegisterMappingCogit
> +       instanceVariableNames: 'prevBCDescriptor numPushNilsFunction
> pushNilSizeFunction methodOrBlockNumTemps regArgsHaveBeenPushed simSelf
> simStack simStackPtr simSpillBase optStatus
> ceCallCogCodePopReceiverArg0Regs ceCallCogCodePopReceiverArg1Arg0Regs
> methodAbortTrampolines picAbortTrampolines picMissTrampolines
> ceCall0ArgsPIC ceCall1ArgsPIC ceCall2ArgsPIC debugStackPointers
> debugFixupBreaks realCECallCogCodePopReceiverArg0Regs
> realCECallCogCodePopReceiverArg1Arg0Regs deadCode needsTwoPath'
> -       instanceVariableNames: 'prevBCDescriptor numPushNilsFunction
> pushNilSizeFunction methodOrBlockNumTemps regArgsHaveBeenPushed simSelf
> simStack simStackPtr simSpillBase optStatus
> ceCallCogCodePopReceiverArg0Regs ceCallCogCodePopReceiverArg1Arg0Regs
> methodAbortTrampolines picAbortTrampolines picMissTrampolines
> ceCall0ArgsPIC ceCall1ArgsPIC ceCall2ArgsPIC debugStackPointers
> debugFixupBreaks realCECallCogCodePopReceiverArg0Regs
> realCECallCogCodePopReceiverArg1Arg0Regs deadCode'
>         classVariableNames: 'NeedsMergeFixupFlag NeedsNonMergeFixupFlag'
>         poolDictionaries: 'CogCompilationConstants VMMethodCacheConstants
> VMObjectIndices VMStackFrameOffsets'
>         category: 'VMMaker-JIT'!
>   StackToRegisterMappingCogit class
>         instanceVariableNames: 'numPushNilsFunction pushNilSizeFunction'!
>
>   !StackToRegisterMappingCogit commentStamp: 'eem 12/19/2010 18:12' prior:
> 0!
>   StackToRegisterMappingCogit is an optimizing code generator that
> eliminates a lot of stack operations and inlines some special selector
> arithmetic.  It does so by a simple stack-to-register mapping scheme based
> on deferring the generation of code to produce operands until
> operand-consuming operations.  The operations that consume operands are
> sends, stores and returns.
>
>   See methods in the class-side documentation protocol for more detail.
>
>   Instance Variables
>         callerSavedRegMask:
>      <Integer>
>         ceEnter0ArgsPIC:
>               <Integer>
>         ceEnter1ArgsPIC:
>               <Integer>
>         ceEnter2ArgsPIC:
>               <Integer>
>         ceEnterCogCodePopReceiverArg0Regs:              <Integer>
>         ceEnterCogCodePopReceiverArg1Arg0Regs:  <Integer>
>         debugBytecodePointers:
> <Set of Integer>
>         debugFixupBreaks:
>              <Set of Integer>
>         debugStackPointers:
>      <CArrayAccessor of (Integer|nil)>
>         methodAbortTrampolines:
>  <CArrayAccessor of Integer>
>         methodOrBlockNumTemps:
> <Integer>
>         optStatus:
>                       <Integer>
>         picAbortTrampolines:
>       <CArrayAccessor of Integer>
>         picMissTrampolines:
>      <CArrayAccessor of Integer>
>         realCEEnterCogCodePopReceiverArg0Regs:          <Integer>
>         realCEEnterCogCodePopReceiverArg1Arg0Regs:      <Integer>
>         regArgsHaveBeenPushed:
> <Boolean>
>         simSelf:
>                               <CogSimStackEntry>
>         simSpillBase:
>              <Integer>
>         simStack:
>                      <CArrayAccessor of CogSimStackEntry>
>         simStackPtr:
>               <Integer>
>         traceSimStack:
>               <Integer>
>
>   callerSavedRegMask
>         - the bitmask of the ABI's caller-saved registers
>
>   ceEnter0ArgsPIC ceEnter1ArgsPIC ceEnter2ArgsPIC
>         - the trampoline for entering an N-arg PIC
>
>   ceEnterCogCodePopReceiverArg0Regs ceEnterCogCodePopReceiverArg1Arg0Regs
>         - teh trampoline for entering a method with N register args
>
>   debugBytecodePointers
>         - a Set of bytecode pcs for setting breakpoints (simulation only)
>
>   debugFixupBreaks
>         - a Set of fixup indices for setting breakpoints (simulation only)
>
>   debugStackPointers
>         - an Array of stack depths for each bytecode for code verification
>
>   methodAbortTrampolines
>         - a CArrayAccessor of abort trampolines for 0, 1, 2 and N args
>
>   methodOrBlockNumTemps
>         - the number of method or block temps (including args) in the
> current compilation unit (method or block)
>
>   optStatus
>         - the variable used to track the status of ReceiverResultReg for
> avoiding reloading that register with self between adjacent inst var
> accesses
>
>   picAbortTrampolines
>         - a CArrayAccessor of abort trampolines for 0, 1, 2 and N args
>
>   picMissTrampolines
>         - a CArrayAccessor of abort trampolines for 0, 1, 2 and N args
>
>   realCEEnterCogCodePopReceiverArg0Regs
> realCEEnterCogCodePopReceiverArg1Arg0Regs
>         - the real trampolines for ebtering machine code with N reg args
> when in the Debug regime
>
>   regArgsHaveBeenPushed
>         - whether the register args have been pushed before frame build
> (e.g. when an interpreter primitive is called)
>
>   simSelf
>         - the simulation stack entry representing self in the current
> compilation unit
>
>   simSpillBase
>         - the variable tracking how much of the simulation stack has been
> spilled to the real stack
>
>   simStack
>         - the simulation stack itself
>
>   simStackPtr
>         - the pointer to the top of the simulation stack
>   !
>   StackToRegisterMappingCogit class
>         instanceVariableNames: 'numPushNilsFunction pushNilSizeFunction'!
>
> Item was changed:
>   ----- Method: StackToRegisterMappingCogit>>compileFrameBuild (in
> category 'compile abstract instructions') -----
>   compileFrameBuild
>         "Build a frame for a CogMethod activation.  See CoInterpreter
> class>>initializeFrameIndices.
>          Override to push the register receiver and register arguments, if
> any."
> +       self cppIf: IMMUTABILITY ifTrue:
> +               [needsTwoPath ifTrue:
> +                       [self compileTwoPathFrameBuild.
> +                       ^self]].
>         needsFrame ifFalse:
>                 [self initSimStackForFramelessMethod: initialPC.
>                  ^self].
>         self genPushRegisterArgs.
>         super compileFrameBuild.
>         self initSimStackForFramefulMethod: initialPC!
>
> Item was added:
> + ----- Method: StackToRegisterMappingCogit>>compileTwoPathFrameBuild (in
> category 'compile abstract instructions') -----
> + compileTwoPathFrameBuild
> +       <option: #IMMUTABILITY>
> +       "We are in a method where the frame is needed *only* for instance
> variable store, typically a setter method.
> +       This case has 20% overhead with Immutability compared to setter
> without immutability because of the stack
> +       frame creation. We compile two path, one where the object is
> immutable, one where it isn't. At the beginning
> +       of the frame build, we take one path or the other depending on the
> receiver mutability.
> +
> +       Note: this specific case happens only where there are only
> instance variabel stores. We could do something
> +       similar for literal variable stores, but we don't as it's too
> uncommon."
> +       |jumpImmutable|
> +       self assert: needsFrame.
> +       self assert: IMMUTABILITY.
> +       self assert: needsTwoPath.
> +       self assert: blockCount = 0.
> +       jumpImmutable := objectRepresentation genJumpImmutable:
> ReceiverResultReg scratchReg: TempReg.
> +       "first path. The receiver is mutable"
> +       self initSimStackForFramelessMethod: initialPC.
> +       self compileMethodBody.
> +       "second path. The receiver is mutable"
> +       needsTwoPath := false. "reset because it impact inst var store
> compilation"
> +       jumpImmutable jmpTarget: self Label.
> +       self genPushRegisterArgs.
> +       super compileFrameBuild.
> +       self initSimStackForFramefulMethod: initialPC!
>
> Item was changed:
>   ----- Method:
> StackToRegisterMappingCogit>>genStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:
> (in category 'bytecode generator support') -----
>   genStorePop: popBoolean slotIndex: slotIndex destReg: destReg
> needsStoreCheck: needsStoreCheck needsRestoreRcvr: needsRestoreReceiver
>         <inline: true>
>         "This method expects destReg to hold the object to store into. In
> practice, it is almost always RcvrResultReg because it is mandatory for the
> various store checks. We could put any register there if no store check is
> needed"
>         self
>                 cppIf: IMMUTABILITY
>                 ifTrue:
> +                       [needsTwoPath
> +                               ifTrue:
> +                                       [self  "first path, receiver is
> mutable"
> +                                               genVanillaStorePop:
> popBoolean
> +                                               slotIndex: slotIndex
> +                                               destReg: destReg
> +                                               needsStoreCheck:
> needsStoreCheck]
> +                               ifFalse:
> +                                       [self
> +                                               genImmCheckStorePop:
> popBoolean
> +                                               slotIndex: slotIndex
> +                                               destReg: destReg
> +                                               needsStoreCheck:
> needsStoreCheck
> +                                               needsRestoreRcvr:
> needsRestoreReceiver]]
> -                       [ self
> -                               genImmCheckStorePop: popBoolean
> -                               slotIndex: slotIndex
> -                               destReg: destReg
> -                               needsStoreCheck: needsStoreCheck
> -                               needsRestoreRcvr: needsRestoreReceiver ]
>                 ifFalse:
> +                       [self
> -                       [ self
>                                 genVanillaStorePop: popBoolean
>                                 slotIndex: slotIndex
>                                 destReg: destReg
> +                               needsStoreCheck: needsStoreCheck].
> -                               needsStoreCheck: needsStoreCheck ].
>                 !
>
> Item was changed:
>   ----- Method: StackToRegisterMappingCogit>>genUpArrowReturn (in category
> 'bytecode generators') -----
>   genUpArrowReturn
>         "Generate a method return from within a method or a block.
>          Frameless method activation looks like
>          CISCs (x86):
>                                 receiver
>                                 args
>                 sp->    ret pc.
>          RISCs (ARM):
>                                 receiver
>                                 args
>                                 ret pc in LR.
>          A fully framed activation is described in CoInterpreter
> class>initializeFrameIndices.
>          Return pops receiver and arguments off the stack.  Callee pushes
> the result."
> +       | framelessReturn |
>         deadCode := true. "can't fall through"
>         inBlock ifTrue:
>                 [self assert: needsFrame.
>                  self CallRT: ceNonLocalReturnTrampoline.
>                  self annotateBytecode: self Label.
>                  ^0].
> +       self
> +               cppIf: IMMUTABILITY
> +               ifTrue: [framelessReturn := needsFrame and: [needsTwoPath
> not]]
> +               ifFalse: [framelessReturn := needsFrame].
> +       framelessReturn
> -       needsFrame
>                 ifTrue:
>                         [self MoveR: FPReg R: SPReg.
>                          self PopR: FPReg.
>                          backEnd hasLinkRegister ifTrue:
>                                 [self PopR: LinkReg].
>                          self RetN: methodOrBlockNumArgs + 1 *
> objectMemory wordSize]
>                 ifFalse:
>                         [self RetN: ((methodOrBlockNumArgs > self
> numRegArgs
>                                                 "A method with an
> interpreter prim will push its register args for the prim.  If the failure
>                                                  body is frameless the
> args must still be popped, see e.g. Behavior>>nextInstance."
>                                                 or:
> [regArgsHaveBeenPushed])
>                                                         ifTrue:
> [methodOrBlockNumArgs + 1 * objectMemory wordSize]
>                                                         ifFalse: [0])].
>         ^0!
>
> Item was changed:
>   ----- Method: StackToRegisterMappingCogit>>scanMethod (in category
> 'compile abstract instructions') -----
>   scanMethod
>         "Scan the method (and all embedded blocks) to determine
>                 - what the last bytecode is; extra bytes at the end of a
> method are used to encode things like source pointers or temp names
>                 - if the method needs a frame or not
>                 - what are the targets of any backward branches.
>                 - how many blocks it creates
>          Answer the block count or on error a negative error code"
>         | latestContinuation nExts descriptor pc numBlocks distance
> targetPC framelessStackDelta |
>         <var: #descriptor type: #'BytecodeDescriptor *'>
>         needsFrame := false.
>         prevBCDescriptor := nil.
> +       self cppIf: IMMUTABILITY ifTrue: [ needsTwoPath := false ].
>         NewspeakVM ifTrue:
>                 [numIRCs := 0].
>         (primitiveIndex > 0
>          and: [coInterpreter isQuickPrimitiveIndex: primitiveIndex])
> ifTrue:
>                 [^0].
>         pc := latestContinuation := initialPC.
>         numBlocks := framelessStackDelta := nExts := extA := extB := 0.
>         [pc <= endPC] whileTrue:
>                 [byte0 := (objectMemory fetchByte: pc ofObject: methodObj)
> + bytecodeSetOffset.
>                  descriptor := self generatorAt: byte0.
>                  descriptor isExtension ifTrue:
>                         [descriptor opcode = Nop ifTrue: "unknown bytecode
> tag; see Cogit class>>#generatorTableFrom:"
>                                 [^EncounteredUnknownBytecode].
>                          self loadSubsequentBytesForDescriptor: descriptor
> at: pc.
>                          self perform: descriptor generator].
>                  (descriptor isReturn
>                   and: [pc >= latestContinuation]) ifTrue:
>                         [endPC := pc].
> +               self cppIf: IMMUTABILITY
> +                       ifTrue:
> +                               [(needsFrame and: [needsTwoPath not])
> ifFalse:
> +                                       [(descriptor needsFrameFunction
> isNil
> +                                         or: [self perform: descriptor
> needsFrameFunction with: framelessStackDelta])
> +                                               ifTrue:
> +                                                       [needsFrame :=
> true.
> +                                                        needsTwoPath :=
> descriptor generator == #genStoreAndPopReceiverVariableBytecode ]
> +                                               ifFalse:
> [framelessStackDelta := framelessStackDelta + descriptor stackDelta]]]
> +                       ifFalse:
> +                               [needsFrame ifFalse:
> +                                       [(descriptor needsFrameFunction
> isNil
> +                                         or: [self perform: descriptor
> needsFrameFunction with: framelessStackDelta])
> +                                               ifTrue: [needsFrame :=
> true]
> +                                               ifFalse:
> [framelessStackDelta := framelessStackDelta + descriptor stackDelta]]].
> +
> -                needsFrame ifFalse:
> -                       [(descriptor needsFrameFunction isNil
> -                         or: [self perform: descriptor needsFrameFunction
> with: framelessStackDelta])
> -                               ifTrue: [needsFrame := true]
> -                               ifFalse: [framelessStackDelta :=
> framelessStackDelta + descriptor stackDelta]].
>                  descriptor isBranch ifTrue:
>                         [distance := self spanFor: descriptor at: pc exts:
> nExts in: methodObj.
>                          targetPC := pc + descriptor numBytes + distance.
>                          (self isBackwardBranch: descriptor at: pc exts:
> nExts in: methodObj)
>                                 ifTrue: [self initializeFixupAt: targetPC
> - initialPC]
>                                 ifFalse: [latestContinuation :=
> latestContinuation max: targetPC]].
>                  descriptor isBlockCreation ifTrue:
>                         [numBlocks := numBlocks + 1.
>                          distance := self spanFor: descriptor at: pc exts:
> nExts in: methodObj.
>                          targetPC := pc + descriptor numBytes + distance.
>                          latestContinuation := latestContinuation max:
> targetPC].
>                  NewspeakVM ifTrue:
>                         [descriptor hasIRC ifTrue:
>                                 [numIRCs := numIRCs + 1]].
>                  pc := pc + descriptor numBytes.
>                  descriptor isExtension
>                         ifTrue: [nExts := nExts + 1]
>                         ifFalse: [nExts := extA := extB := 0].
>                  prevBCDescriptor := descriptor].
>         ^numBlocks!
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20160527/96069831/attachment-0001.htm


More information about the Vm-dev mailing list