[Vm-dev] VM Maker: VMMaker.oscog-eem.674.mcz

commits at source.squeak.org commits at source.squeak.org
Mon Apr 14 19:14:59 UTC 2014


Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.674.mcz

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

Name: VMMaker.oscog-eem.674
Author: eem
Time: 14 April 2014, 12:12:28.615 pm
UUID: eefd603d-9638-4ad8-99c0-4ee12e87d49d
Ancestors: VMMaker.oscog-eem.673

Deal with the pc ambiguity of conditional branches followed by
backward branches (conditional branches map to the following
pc, backward branches to themselves) by providing and using
mcPCForBackwardBranch:startBcpc:in: when converting interpreted
frames containing loops to machine code frames.  This eliminates
a bogus mustBeBoolean when converting interpreter loops in Sista.
Change mcPCFor[BackwardBranch]:startBcpc:in: to answer the
absolute mcpc (as was commented) rather than the relative mcpc.

This requires a 5-argument perform in mapFor:bcpc:performUntil:arg:.
Refactor to add an isBackwardBranch argument to the function
perfomed in all clients.

For performance, specify that mapFor:[bcpc:]performUntil:arg: are
inlined, eliminating the perform/indirect function call.  This adds of
the order of 3% to the size of a cogit.o's text seg so is acceptable.

Tweak Slang to accomplish the inlining.

Fix a bug in printing frame flags (order was the wrong way around).

=============== Diff against VMMaker.oscog-eem.673 ===============

Item was changed:
  ----- Method: CoInterpreter>>convertToMachineCodeFrame:bcpc: (in category 'frame access') -----
  convertToMachineCodeFrame: cogHomeMethod bcpc: bcpc
  	<var: #cogHomeMethod type: #'CogHomeMethod *'>
  	"Convert the current interpreter frame into a machine code frame
  	 and answer the machine code pc matching bcpc."
  	| startBcpc methodField closure cogMethod pc |
  	<var: #cogMethod type: #'CogBlockMethod *'>
  	<var: #p type: #'char *'>
  	self assert: (self isMachineCodeFrame: framePointer) not.
  	"Update the return pc, perhaps saving it in the caller's iframeSavedIP."
  	(self isBaseFrame: framePointer)
  		ifTrue:
  			[stackPages
  				longAt: framePointer + FoxCallerSavedIP
  				put: cogit ceBaseFrameReturnPC]
  		ifFalse:
  			[(self isMachineCodeFrame: (self frameCallerFP: framePointer)) ifFalse:
  				[self iframeSavedIP: (self frameCallerFP: framePointer)
  					put: (self frameCallerSavedIP: framePointer) asInteger.
  				 stackPages
  					longAt: framePointer + FoxCallerSavedIP
  					put: cogit ceReturnToInterpreterPC]].
  	"Set the cog method field"
  	(self iframeIsBlockActivation: framePointer)
  		ifTrue:
  			[closure := self pushedReceiverOrClosureOfFrame: framePointer.
  			 startBcpc := self startPCOfClosure: closure.
  			 cogMethod := cogit
  								findMethodForStartBcpc: startBcpc
  								inHomeMethod: cogHomeMethod.
  			 methodField := cogMethod asInteger + MFMethodFlagIsBlockFlag]
  		ifFalse:
  			[startBcpc := self startPCOfMethodHeader: cogHomeMethod methodHeader.
  			 cogMethod := self cCoerceSimple: cogHomeMethod to: #'CogBlockMethod *'.
  			 methodField := cogHomeMethod asInteger].
  	stackPages
  		longAt: framePointer + FoxMethod
  		put: methodField
  			+ ((self iframeHasContext: framePointer)
  				ifTrue: [MFMethodFlagHasContextFlag]
  				ifFalse: [0]).
  	framePointer + FoxIFReceiver to: stackPointer by: BytesPerWord negated do:
  		[:p|
  		stackPages longAt: p + FoxMFReceiver - FoxIFReceiver put: (stackPages longAt: p)].
  	stackPointer := stackPointer + FoxMFReceiver - FoxIFReceiver.
+ 	pc := cogit mcPCForBackwardBranch: bcpc startBcpc: startBcpc in: cogMethod.
+ 	self assert: pc > (cogMethod asInteger + cogit noCheckEntryOffset).
+ 	^pc!
- 	pc := cogit mcPCFor: bcpc startBcpc: startBcpc in: cogMethod.
- 	self assert: pc > cogit noCheckEntryOffset.
- 	^cogMethod asInteger + pc!

Item was changed:
  ----- Method: CoInterpreter>>printFrame:WithSP: (in category 'debug printing') -----
  printFrame: theFP WithSP: theSP
  	<api>
  	| theMethod theMethodEnd numArgs numTemps rcvrAddress topThing |
  	<inline: false>
  	<var: #theFP type: #'char *'>
  	<var: #theSP type: #'char *'>
  	<var: #addr type: #'char *'>
  	<var: #rcvrAddress type: #'char *'>
  	<var: #cogMethod type: #'CogBlockMethod *'>
  	<var: #homeMethod type: #'CogMethod *'>
  	self cCode: '' inSmalltalk: [self transcript ensureCr].
  	(self isMachineCodeFrame: theFP)
  		ifTrue:
  			[| cogMethod homeMethod |
  			 cogMethod := self mframeCogMethod: theFP.
  			 homeMethod := self mframeHomeMethod: theFP.
  			 theMethod := homeMethod asInteger.
  			 theMethodEnd := homeMethod asInteger + homeMethod blockSize.
  			 numArgs := cogMethod cmNumArgs.
  			 numTemps := self temporaryCountOfMethodHeader: homeMethod methodHeader]
  		ifFalse:
  			[theMethod := self frameMethodObject: theFP.
  			 theMethodEnd := theMethod + (objectMemory sizeBitsOfSafe: theMethod).
  			 numArgs := self iframeNumArgs: theFP.
  			 numTemps := self tempCountOf: theMethod].
  	(self frameIsBlockActivation: theFP) ifTrue:
  		[| rcvrOrClosure |
  		 "No BlockLocalTempCounter in the Cogit's C code, so quick hack is to use numCopied + numArgs"
  		 rcvrOrClosure := self pushedReceiverOrClosureOfFrame: theFP.
  		 ((objectMemory isNonImmediate: rcvrOrClosure)
  		 and: [(objectMemory addressCouldBeObj: rcvrOrClosure)
  		 and: [(objectMemory fetchClassOfNonImm: rcvrOrClosure) = (objectMemory splObj: ClassBlockClosure)]])
  			ifTrue: [numTemps := numArgs + (self stSizeOf: rcvrOrClosure)]
  			ifFalse: [numTemps := numArgs]].
  	self shortPrintFrame: theFP.
  	(self isBaseFrame: theFP) ifTrue:
  		[self printFrameOop: '(caller ctxt'
  			at: theFP + (self frameStackedReceiverOffset: theFP) + (2 * BytesPerWord).
  		 self printFrameOop: '(saved ctxt'
  			at: theFP + (self frameStackedReceiverOffset: theFP) + (1 * BytesPerWord)].
  	self printFrameOop: 'rcvr/clsr'
  		at: theFP + FoxCallerSavedIP + ((numArgs + 1) * BytesPerWord).
  	numArgs to: 1 by: -1 do:
  		[:i|
  		self printFrameOop: 'arg' index: numArgs - i at: theFP + FoxCallerSavedIP + (i * BytesPerWord)].
  	self printFrameThing: 'caller ip'
  		at: theFP + FoxCallerSavedIP
  		extraString: ((stackPages longAt: theFP + FoxCallerSavedIP) = cogit ceReturnToInterpreterPC ifTrue:
  						['ceReturnToInterptreter']).
  	self printFrameThing: 'saved fp' at: theFP + FoxSavedFP.
  	self printFrameMethodFor: theFP.
+ 	(self isMachineCodeFrame: theFP) ifTrue:
- 	(self isMachineCodeFrame: theFP) ifFalse:
  		[self printFrameFlagsForFP: theFP].
  	self printFrameOop: 'context' at: theFP + FoxThisContext.
+ 	(self isMachineCodeFrame: theFP) ifFalse:
- 	(self isMachineCodeFrame: theFP) ifTrue:
  		[self printFrameFlagsForFP: theFP].
  	(self isMachineCodeFrame: theFP)
  		ifTrue: [rcvrAddress := theFP + FoxMFReceiver]
  		ifFalse:
  			[self printFrameThing: 'saved ip'
  				at: theFP + FoxIFSavedIP
  				extra: ((self iframeSavedIP: theFP) = 0
  							ifTrue: [0]
  							ifFalse: [(self iframeSavedIP: theFP) - theMethod + 2 - BaseHeaderSize]).
  			 rcvrAddress := theFP + FoxIFReceiver].
  	self printFrameOop: 'receiver' at: rcvrAddress.
  	topThing := stackPages longAt: theSP.
  	(topThing between: theMethod and: theMethodEnd)
  		ifTrue:
  			[rcvrAddress - BytesPerWord to: theSP + BytesPerWord by: BytesPerWord negated do:
  				[:addr| | index |
  				index := rcvrAddress - addr / BytesPerWord + numArgs.
  				index <= numTemps
  					ifTrue: [self printFrameOop: 'temp' index: index - 1 at: addr]
  					ifFalse: [self printFrameOop: ((self frameIsBlockActivation: theFP)
  													ifTrue: ['temp/stck']
  													ifFalse: ['stck'])
  								at: addr]].
  			self printFrameThing: 'frame ip'
  				at: theSP
  				extra: ((self isMachineCodeFrame: theFP)
  						ifTrue: [topThing - theMethod]
  						ifFalse: [topThing - theMethod + 2 - BaseHeaderSize])]
  		ifFalse:
  			[rcvrAddress - BytesPerWord to: theSP by: BytesPerWord negated do:
  				[:addr| | index |
  				index := rcvrAddress - addr / BytesPerWord + numArgs.
  				index <= numTemps
  					ifTrue: [self printFrameOop: 'temp' index: index - 1 at: addr]
  					ifFalse: [self printFrameOop: ((self frameIsBlockActivation: theFP)
  													ifTrue: ['temp/stck']
  													ifFalse: ['stck'])
  								at: addr]]]!

Item was added:
+ ----- Method: CogVMSimulator>>convertToMachineCodeFrame:bcpc: (in category 'frame access') -----
+ convertToMachineCodeFrame: cogHomeMethod bcpc: bcpc
+ 	"(minBackwardJumpCountForCompile ~= MinBackwardJumpCountForCompile
+ 	 and: [(self stringOf: (self penultimateLiteralOf: cogHomeMethod methodObject)) = #repeat]) ifTrue:
+ 		[self printExternalHeadFrame.
+ 		self print: bcpc; cr.
+ 		self halt]."
+ 	^super convertToMachineCodeFrame: cogHomeMethod bcpc: bcpc!

Item was added:
+ ----- Method: CogVMSimulator>>minBackwardJumpCountForCompile (in category 'simulation only') -----
+ minBackwardJumpCountForCompile
+ 	<doNotGenerate>
+ 	^minBackwardJumpCountForCompile!

Item was added:
+ ----- Method: CogVMSimulator>>minBackwardJumpCountForCompile: (in category 'simulation only') -----
+ minBackwardJumpCountForCompile: n
+ 	<doNotGenerate>
+ 	minBackwardJumpCountForCompile := n!

Item was changed:
  ----- Method: Cogit>>bytecodePCFor:startBcpc:in: (in category 'method map') -----
  bytecodePCFor: mcpc startBcpc: startbcpc in: cogMethod
  	"Answer the zero-relative bytecode pc matching the machine code pc argument in
  	 cogMethod, given the start of the bytecodes for cogMethod's block or method object."
  	<api>
  	<var: #cogMethod type: #'CogBlockMethod *'>
  	^self
  		mapFor: cogMethod
  		bcpc: startbcpc
+ 		performUntil: #find:IsBackwardBranch:Mcpc:Bcpc:MatchingMcpc:
- 		performUntil: #findMcpc:Bcpc:MatchingMcpc:
  		arg: mcpc asVoidPointer!

Item was added:
+ ----- Method: Cogit>>find:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc: (in category 'method map') -----
+ find: descriptor IsBackwardBranch: isBackwardBranch Mcpc: mcpc Bcpc: bcpc MatchingBcpc: targetBcpc
+ 	<var: #descriptor type: #'BytecodeDescriptor *'>
+ 	<var: #mcpc type: #'char *'>
+ 	<var: #targetBcpc type: #'void *'>
+ 	<inline: true>
+ 	^targetBcpc asInteger = ((descriptor isNil or: [isBackwardBranch]) ifTrue: [bcpc] ifFalse: [bcpc + descriptor numBytes])
+ 		ifTrue: [mcpc asInteger]
+ 		ifFalse: [0]!

Item was added:
+ ----- Method: Cogit>>find:IsBackwardBranch:Mcpc:Bcpc:MatchingMcpc: (in category 'method map') -----
+ find: descriptor IsBackwardBranch: isBackwardBranch Mcpc: mcpc Bcpc: bcpc MatchingMcpc: targetMcpc
+ 	<var: #descriptor type: #'BytecodeDescriptor *'>
+ 	<var: #mcpc type: #'char *'>
+ 	<var: #targetMcpc type: #'void *'>
+ 	"Machine code addresses map to the following bytecode for all bytecodes
+ 	 except backward branches, where they map to the backward branch itself.
+ 	 This is so that loops continue, rather than terminate prematurely."
+ 	^targetMcpc = mcpc
+ 		ifTrue: [(descriptor isNil or: [isBackwardBranch])
+ 					ifTrue: [bcpc]
+ 					ifFalse: [bcpc + descriptor numBytes]]
+ 		ifFalse: [0]!

Item was added:
+ ----- Method: Cogit>>findBackwardBranch:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc: (in category 'method map') -----
+ findBackwardBranch: descriptor IsBackwardBranch: isBackwardBranch Mcpc: mcpc Bcpc: bcpc MatchingBcpc: targetBcpc
+ 	<var: #descriptor type: #'BytecodeDescriptor *'>
+ 	<var: #mcpc type: #'char *'>
+ 	<var: #targetBcpc type: #'void *'>
+ 	<inline: true>
+ 	^(isBackwardBranch and: [targetBcpc asInteger = bcpc])
+ 		ifTrue: [mcpc asInteger]
+ 		ifFalse: [0]!

Item was removed:
- ----- Method: Cogit>>findMcpc:Bcpc:MatchingBcpc: (in category 'method map') -----
- findMcpc: mcpc Bcpc: bcpc MatchingBcpc: targetBcpc
- 	<var: #mcpc type: #'char *'>
- 	<var: #targetBcpc type: #'void *'>
- 	^targetBcpc asInteger = bcpc ifTrue: [mcpc asInteger] ifFalse: [0]!

Item was removed:
- ----- Method: Cogit>>findMcpc:Bcpc:MatchingMcpc: (in category 'method map') -----
- findMcpc: mcpc Bcpc: bcpc MatchingMcpc: targetMcpc
- 	<var: #mcpc type: #'char *'>
- 	<var: #targetMcpc type: #'void *'>
- 	^targetMcpc = mcpc ifTrue: [bcpc] ifFalse: [0]!

Item was changed:
  ----- Method: Cogit>>mapFor:bcpc:performUntil:arg: (in category 'method map') -----
  mapFor: cogMethod bcpc: startbcpc performUntil: functionSymbol arg: arg
  	"Machine-code <-> bytecode pc mapping support.  Evaluate functionSymbol
  	 for each mcpc, bcpc pair in the map until the function returns non-zero,
  	 answering that result, or 0 if it fails to.  This works only for frameful methods."
  	<var: #cogMethod type: #'CogBlockMethod *'>
+ 	<var: #functionSymbol declareC: 'sqInt (*functionSymbol)(BytecodeDescriptor *desc, sqInt isBackwardBranch, char *mcpc, sqInt bcpc, void *arg)'>
- 	<var: #functionSymbol declareC: 'sqInt (*functionSymbol)(char *mcpc, sqInt bcpc, void *arg)'>
  	<var: #arg type: #'void *'>
+ 	<inline: true>
  	| isInBlock mcpc bcpc endbcpc map mapByte homeMethod aMethodObj result
  	  latestContinuation byte descriptor bsOffset nExts |
  	<var: #descriptor type: #'BytecodeDescriptor *'>
  	<var: #homeMethod type: #'CogMethod *'>
  	self assert: cogMethod stackCheckOffset > 0.
  	"In both CMMethod and CMBlock cases find the start of the map and
  	 skip forward to the bytecode pc map entry for the stack check."
  	cogMethod cmType = CMMethod
  		ifTrue:
  			[isInBlock := false.
  			 homeMethod := self cCoerceSimple: cogMethod to: #'CogMethod *'.
  			 self assert: startbcpc = (coInterpreter startPCOfMethodHeader: homeMethod methodHeader).
  			 map := self mapStartFor: homeMethod.
  			 self assert: ((objectMemory byteAt: map) >> AnnotationShift = IsAbsPCReference
  						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsObjectReference
  						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsRelativeCall
  						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsDisplacementX2N]]]).
  			 latestContinuation := startbcpc.
  			 aMethodObj := homeMethod methodObject.
  			 endbcpc := (objectMemory byteLengthOf: aMethodObj) - 1.
  			 bsOffset := self bytecodeSetOffsetForHeader: homeMethod methodHeader]
  		ifFalse:
  			[isInBlock := true.
  			 homeMethod := cogMethod cmHomeMethod.
  			 map := self findMapLocationForMcpc: cogMethod asUnsignedInteger + (self sizeof: CogBlockMethod)
  						inMethod: homeMethod.
  			 self assert: map ~= 0.
  			 self assert: ((objectMemory byteAt: map) >> AnnotationShift = HasBytecodePC "fiducial"
  						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsDisplacementX2N]).
  			 [(objectMemory byteAt: map) >> AnnotationShift ~= HasBytecodePC] whileTrue:
  				[map := map - 1].
  			 map := map - 1. "skip fiducial; i.e. the map entry for the pc immediately following the method header."
  			 aMethodObj := homeMethod methodObject.
  			 bcpc := startbcpc - (self blockCreationBytecodeSizeForHeader: homeMethod methodHeader).
  			 bsOffset := self bytecodeSetOffsetForHeader: homeMethod methodHeader.
  			 byte := (objectMemory fetchByte: bcpc ofObject: aMethodObj) + bsOffset.
  			 descriptor := self generatorAt: byte.
  			 endbcpc := self nextBytecodePCFor: descriptor at: bcpc exts: -1 in: aMethodObj].
  	bcpc := startbcpc.
  	mcpc := cogMethod asUnsignedInteger + cogMethod stackCheckOffset.
  	nExts := 0.
  	"The stack check maps to the start of the first bytecode,
  	 the first bytecode being effectively after frame build."
  	result := self perform: functionSymbol
+ 					with: nil
+ 					with: false
  					with: (self cCoerceSimple: mcpc to: #'char *')
  					with: startbcpc
  					with: arg.
  	result ~= 0 ifTrue:
  		[^result].
  	"Now skip up through the bytecode pc map entry for the stack check." 
  	[(objectMemory byteAt: map) >> AnnotationShift ~= HasBytecodePC] whileTrue:
  		[map := map - 1].
  	map := map - 1.
  	[(mapByte := objectMemory byteAt: map) ~= MapEnd] whileTrue: "defensive; we exit on bcpc"
  		[mapByte >= FirstAnnotation
  			ifTrue:
+ 				[| annotation nextBcpc isBackwardBranch |
- 				[| annotation nextBcpc |
  				annotation := mapByte >> AnnotationShift.
  				mcpc := mcpc + (mapByte bitAnd: DisplacementMask).
  				(self isPCMappedAnnotation: annotation alternateInstructionSet: bsOffset > 0) ifTrue:
  					[[byte := (objectMemory fetchByte: bcpc ofObject: aMethodObj) + bsOffset.
  					  descriptor := self generatorAt: byte.
  					  isInBlock
  						ifTrue: [bcpc >= endbcpc ifTrue: [^0]]
  						ifFalse:
  							[(descriptor isReturn and: [bcpc >= latestContinuation]) ifTrue: [^0].
  							 (descriptor isBranch or: [descriptor isBlockCreation]) ifTrue:
  								[| targetPC |
  								 targetPC := self latestContinuationPCFor: descriptor at: bcpc exts: nExts in: aMethodObj.
  								 latestContinuation := latestContinuation max: targetPC]].
  					  nextBcpc := self nextBytecodePCFor: descriptor at: bcpc exts: nExts in: aMethodObj.
  					  descriptor isMapped
  					  or: [isInBlock and: [descriptor isMappedInBlock]]] whileFalse:
  						[bcpc := nextBcpc.
  						 nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]].
+ 					isBackwardBranch := descriptor isBranch
+ 										   and: [self isBackwardBranch: descriptor at: bcpc exts: nExts in: aMethodObj].
- 					"All subsequent bytecodes except backward branches map to the
- 					 following bytecode. Backward branches map to themselves other-
- 					 wise mapping could cause premature breaking out of loops." 
  					result := self perform: functionSymbol
+ 									with: descriptor
+ 									with: isBackwardBranch
  									with: (self cCoerceSimple: mcpc to: #'char *')
+ 									with: bcpc
- 									with: ((descriptor isBranch
- 										   and: [self isBackwardBranch: descriptor at: bcpc exts: nExts in: aMethodObj])
- 											ifTrue: [bcpc]
- 											ifFalse: [bcpc + descriptor numBytes])
  									with: arg.
  					 result ~= 0 ifTrue:
  						[^result].
  					 bcpc := nextBcpc.
+ 					 nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]].
+ 				self maybeRememberPrevMap: annotation absPCMcpc: mcpc]
- 					 nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]]]
  			ifFalse:
  				[mcpc := mcpc + (mapByte >= DisplacementX2N
  									ifTrue: [mapByte - DisplacementX2N << AnnotationShift]
  									ifFalse: [mapByte])].
  		 map := map - 1].
  	^0!

Item was changed:
  ----- Method: Cogit>>mapFor:performUntil:arg: (in category 'method map') -----
  mapFor: cogMethod performUntil: functionSymbol arg: arg
+ 	"Unlinking/GC/Disassembly support"
- 	"Disassembly/GC support"
  	<var: #cogMethod type: #'CogMethod *'>
  	<var: #functionSymbol declareC: 'int (*functionSymbol)(sqInt annotation, char *mcpc, sqInt arg)'>
+ 	<inline: true>
  	| mcpc map mapByte result |
  	mcpc := cogMethod asInteger + cmNoCheckEntryOffset.
  	map := self mapStartFor: cogMethod.
  	[(mapByte := coInterpreter byteAt: map) ~= MapEnd] whileTrue:
  		[mapByte >= FirstAnnotation
  			ifTrue:
  				[mcpc := mcpc + (mapByte bitAnd: DisplacementMask).
  				 result := self perform: functionSymbol
  							   with: mapByte >> AnnotationShift
  							   with: (self cCoerceSimple: mcpc to: #'char *')
  							   with: arg.
  				 result ~= 0 ifTrue:
  					[^result]]
  			ifFalse:
  				[mcpc := mcpc + (mapByte >= DisplacementX2N
  									ifTrue: [mapByte - DisplacementX2N << AnnotationShift]
  									ifFalse: [mapByte])].
  		 map := map - 1].
  	^0!

Item was added:
+ ----- Method: Cogit>>maybeRememberPrevMap:absPCMcpc: (in category 'method map') -----
+ maybeRememberPrevMap: annotation absPCMcpc: mcpc
+ 	"This is a nop in all except the SistaStackToRegisterMappingCogit."
+ 	<inline: true>!

Item was changed:
  ----- Method: Cogit>>mcPCFor:startBcpc:in: (in category 'method map') -----
  mcPCFor: bcpc startBcpc: startbcpc in: cogMethod
  	"Answer the absolute machine code pc matching the zero-relative bytecode pc argument
  	 in cogMethod, given the start of the bytecodes for cogMethod's block or method object."
- 	<api>
  	<var: #cogMethod type: #'CogBlockMethod *'>
+ 	<returnTypeC: #usqInt>
+ 	^self
+ 		mapFor: cogMethod
+ 		bcpc: startbcpc
+ 		performUntil: #find:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc:
+ 		arg: bcpc asVoidPointer!
- 	| absPC |
- 	absPC := self
- 				mapFor: cogMethod
- 				bcpc: startbcpc
- 				performUntil: #findMcpc:Bcpc:MatchingBcpc:
- 				arg: bcpc asVoidPointer.
- 	^absPC ~= 0
- 		ifTrue: [absPC asUnsignedInteger - cogMethod asUnsignedInteger]
- 		ifFalse: [absPC]!

Item was added:
+ ----- Method: Cogit>>mcPCForBackwardBranch:startBcpc:in: (in category 'method map') -----
+ mcPCForBackwardBranch: bcpc startBcpc: startbcpc in: cogMethod
+ 	"Answer the absolute machine code pc matching the zero-relative
+ 	 bytecode pc of a backward branch in cogMethod, given the start
+ 	 of the bytecodes for cogMethod's block or method object."
+ 	<api>
+ 	<var: #cogMethod type: #'CogBlockMethod *'>
+ 	<returnTypeC: #usqInt>
+ 	^self
+ 		mapFor: cogMethod
+ 		bcpc: startbcpc
+ 		performUntil: #findBackwardBranch:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc:
+ 		arg: bcpc asVoidPointer!

Item was added:
+ ----- Method: Cogit>>print:IsBackwardBranch:Mcpc:Bcpc:on: (in category 'method map') -----
+ print: descriptor IsBackwardBranch: isBackwardBranch Mcpc: mcpc Bcpc: bcpc on: aStream
+ 	<doNotGenerate>
+ 	aStream ensureCr.
+ 	mcpc printOn: aStream base: 16.
+ 	aStream
+ 		space; tab;
+ 		print: (isBackwardBranch ifTrue: [bcpc] ifFalse: [bcpc + descriptor numBytes]);
+ 		cr; flush.
+ 	^0!

Item was changed:
  ----- Method: Cogit>>printPCMapPairsFor:on: (in category 'method map') -----
  printPCMapPairsFor: cogMethod on: aStream
  	<doNotGenerate>
  	(self subMethodsAsRangesFor: cogMethod)
  		do: [:sm|
+ 			self mapFor: sm cogMethod bcpc: sm startpc performUntil: #print:IsBackwardBranch:Mcpc:Bcpc:on: arg: aStream]
- 			self mapFor: sm cogMethod bcpc: sm startpc performUntil: #printMcpc:Bcpc:on: arg: aStream]
  		separatedBy: [aStream tab; next: 2 put: $=; cr]!

Item was changed:
  ----- Method: Cogit>>testBcToMcPcMappingForMethod: (in category 'tests-method map') -----
  testBcToMcPcMappingForMethod: cogMethod
  	<doNotGenerate>
  	"self disassembleMethod: cogMethod"
  	"self printPCMapPairsFor: cogMethod on: Transcript"
  	| aMethodObj currentSubMethod subMethods |
  	aMethodObj := cogMethod methodObject.
  	subMethods := self subMethodsAsRangesFor: cogMethod.
  	currentSubMethod := subMethods first.
  	currentSubMethod endPC: (self endPCOf: aMethodObj).
  	self bcpcsAndDescriptorsFor: aMethodObj do:
  		[:bcpc :byte :desc| | subMethod |
  		(desc notNil and: [desc isBlockCreation]) ifTrue:
  			[subMethod := subMethods detect: [:sm| sm startpc = (bcpc + desc numBytes)].
  			 subMethod endPC: bcpc + desc numBytes + (self spanFor: desc at: bcpc exts: -1 in: aMethodObj) - 1]].
  	subMethods allButFirst do:
  		[:blockSubMethod| | cogBlockMethod |
  		cogBlockMethod := self
  								findMethodForStartBcpc: blockSubMethod startpc
  								inHomeMethod: cogMethod.
  		self assert: cogBlockMethod address = (blockSubMethod first - (self sizeof: CogBlockMethod))].
  	self bcpcsAndDescriptorsFor: aMethodObj do:
+ 		[:bcpc :byte :desc| | absMcpc mappedBcpc |
- 		[:bcpc :byte :desc| | relMcpc absMcpc mappedBcpc |
  		currentSubMethod := self innermostSubMethodFor: bcpc in: subMethods startingAt: 1.
  		(currentSubMethod cogMethod stackCheckOffset > 0
  		 and: [desc isNil or: [desc isMapped]]) ifTrue:
  			["The first bytecode and backward branch bytecodes are mapped to their pc.
  			  Other bytecodes map to their following pc."
  			mappedBcpc := (desc isNil
  							   or: [desc isBranch
  								   and: [(self isBackwardBranch: desc at: bcpc exts: (self nExtensionsFor: bcpc in: aMethodObj) in: aMethodObj)]])
  								ifTrue: [bcpc]
  								ifFalse: [bcpc + desc numBytes].
+ 			 absMcpc := self
- 			 relMcpc := self
  							mcPCFor: mappedBcpc
  							startBcpc: currentSubMethod startpc
  							in: currentSubMethod cogMethod.
+ 			 self assert: absMcpc > (currentSubMethod cogMethod asInteger + self noCheckEntryOffset).
- 			 self assert: relMcpc ~= 0.
- 			 absMcpc := relMcpc + currentSubMethod cogMethod address.
  			 self assert: (self
  							bytecodePCFor: absMcpc
  							startBcpc: currentSubMethod startpc
  							in: currentSubMethod cogMethod) = mappedBcpc]]!

Item was changed:
  ----- Method: Cogit>>testMcToBcPcMappingForMethod: (in category 'tests-method map') -----
  testMcToBcPcMappingForMethod: cogMethod
  	<doNotGenerate>
  	| bcMethod subMethods prevMcpc isAltInstSet |
  	"self disassembleMethod: cogMethod"
  	"coInterpreter symbolicMethod: cogMethod methodObject"
  	"coInterpreter printOop: cogMethod methodObject"
  	"self printPCMapPairsFor: cogMethod on: Transcript"
  	cogMethod stackCheckOffset = 0 ifTrue: "frameless"
  		[^self].
  	bcMethod := coInterpreter isCurrentImageFacade
  					ifTrue: [coInterpreter objectForOop: cogMethod methodObject]
  					ifFalse: [VMCompiledMethodProxy new
  								for: cogMethod methodObject
  								coInterpreter: coInterpreter
  								objectMemory: objectMemory].
  	subMethods := self subMethodsAsRangesFor: cogMethod.
  	isAltInstSet := coInterpreter headerIndicatesAlternateBytecodeSet: cogMethod methodHeader.
  	self mapFor: cogMethod do:
  		[:annotation :mcpc| | subMethod bcpc mappedpc |
  		(self isPCMappedAnnotation: annotation alternateInstructionSet: isAltInstSet) ifTrue:
  			[subMethod := subMethods
  								detect: [:range| range includes: mcpc]
  								ifNone: ["a trailing call ceNonLocalReturnTrampoline's following
  										 pc is the start of a following block or the end of the map"
  										subMethods detect: [:range| range includes: mcpc - 1]].
  			mcpc > subMethod first ifTrue:
  				[bcpc := self
  							bytecodePCFor: mcpc
  							startBcpc: subMethod startpc
  							in: subMethod cogMethod.
  				self assert: bcpc ~= 0.
  				mappedpc := self mcPCFor: bcpc startBcpc: subMethod startpc in: subMethod cogMethod.
+ 				self assert: mappedpc > (subMethod cogMethod address + self noCheckEntryOffset).
- 				self assert: mappedpc ~= 0.
- 				mappedpc := mappedpc + subMethod cogMethod address.
  				"mcpc = mappedpc is obviously what we want and expect.  PrevMcpc = mappedpc hacks
+ 				 around frame building accessors where the first bytecode is mapped twice, once for the
- 				 around frame building accessors where the frst bytecode is mapped twice, once for the
  				 stack check and once for the context inst var access.  The bytecode pc can only map
  				 back to a single mcpc, the first, so the second map entry will fail without this hack."
  				self assert: (mcpc = mappedpc or: [prevMcpc = mappedpc]).
  				"IsNSSendCall is used only for pushImplicitReceiver:.  This isn't a send bytecode.
  				 So filter-out these annotations."
  				((self isSendAnnotation: annotation) and: [annotation ~= IsNSSendCall]) ifTrue:
  					[| mcSelector bcSelector |
  					mcSelector := self selectorForSendAt: mcpc annotation: annotation.
  					"sends map to the following pc.  need to find the selector for the previous pc"
  					bcSelector := self selectorForSendBefore: bcpc in: bcMethod.
  					self assert: mcSelector = bcSelector]].
  			 prevMcpc := mcpc].
  		 false "keep scanning"]!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>bytecodePCFor:startBcpc:in: (in category 'method map') -----
- bytecodePCFor: mcpc startBcpc: startbcpc in: cogMethod
- 	"Answer the zero-relative bytecode pc matching the machine code pc argument in
- 	 cogMethod, given the start of the bytecodes for cogMethod's block or method object."
- 	<api>
- 	<var: #cogMethod type: #'CogBlockMethod *'>
- 	^self
- 		mapFor: cogMethod
- 		bcpc: startbcpc
- 		performUntil: #find:Mcpc:Bcpc:MatchingMcpc:
- 		arg: mcpc asVoidPointer!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>find:Mcpc:Bcpc:MatchingBcpc: (in category 'method map') -----
- find: descriptor Mcpc: mcpc Bcpc: bcpc MatchingBcpc: targetBcpc
- 	<var: #descriptor type: #'BytecodeDescriptor *'>
- 	<var: #mcpc type: #'char *'>
- 	<var: #targetBcpc type: #'void *'>
- 	^targetBcpc asInteger = bcpc ifTrue: [mcpc asInteger] ifFalse: [0]!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>find:Mcpc:Bcpc:MatchingMcpc: (in category 'method map') -----
- find: descriptor Mcpc: mcpc Bcpc: bcpc MatchingMcpc: targetMcpc
- 	<var: #descriptor type: #'BytecodeDescriptor *'>
- 	<var: #mcpc type: #'char *'>
- 	<var: #targetMcpc type: #'void *'>
- 	^targetMcpc = mcpc ifTrue: [bcpc] ifFalse: [0]!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>findMcpc:Bcpc:MatchingBcpc: (in category 'method map') -----
- findMcpc: mcpc Bcpc: bcpc MatchingBcpc: targetBcpc
- 	<doNotGenerate>
- 	self shouldNotImplement!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>findMcpc:Bcpc:MatchingMcpc: (in category 'method map') -----
- findMcpc: mcpc Bcpc: bcpc MatchingMcpc: targetMcpc
- 	<doNotGenerate>
- 	self shouldNotImplement!

Item was changed:
  ----- Method: SistaStackToRegisterMappingCogit>>mapFor:bcpc:performUntil:arg: (in category 'method map') -----
  mapFor: cogMethod bcpc: startbcpc performUntil: functionSymbol arg: arg
- 	"Machine-code <-> bytecode pc mapping support.  Evaluate functionSymbol
- 	 for each mcpc, bcpc pair in the map until the function returns non-zero,
- 	 answering that result, or 0 if it fails to.  This works only for frameful methods."
- 	<var: #cogMethod type: #'CogBlockMethod *'>
- 	<var: #functionSymbol declareC: 'sqInt (*functionSymbol)(BytecodeDescriptor * desc, char *mcpc, sqInt bcpc, void *arg)'>
- 	<var: #arg type: #'void *'>
- 	| isInBlock mcpc bcpc endbcpc map mapByte homeMethod aMethodObj result
- 	  latestContinuation byte descriptor bsOffset nExts |
- 	<var: #descriptor type: #'BytecodeDescriptor *'>
- 	<var: #homeMethod type: #'CogMethod *'>
- 	self assert: cogMethod stackCheckOffset > 0.
- 	"In both CMMethod and CMBlock cases find the start of the map and
- 	 skip forward to the bytecode pc map entry for the stack check."
- 	cogMethod cmType = CMMethod
- 		ifTrue:
- 			[isInBlock := false.
- 			 homeMethod := self cCoerceSimple: cogMethod to: #'CogMethod *'.
- 			 self assert: startbcpc = (coInterpreter startPCOfMethodHeader: homeMethod methodHeader).
- 			 map := self mapStartFor: homeMethod.
- 			 self assert: ((objectMemory byteAt: map) >> AnnotationShift = IsAbsPCReference
- 						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsObjectReference
- 						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsRelativeCall
- 						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsDisplacementX2N]]]).
- 			 latestContinuation := startbcpc.
- 			 aMethodObj := homeMethod methodObject.
- 			 endbcpc := (objectMemory byteLengthOf: aMethodObj) - 1.
- 			 bsOffset := self bytecodeSetOffsetForHeader: homeMethod methodHeader]
- 		ifFalse:
- 			[isInBlock := true.
- 			 homeMethod := cogMethod cmHomeMethod.
- 			 map := self findMapLocationForMcpc: cogMethod asUnsignedInteger + (self sizeof: CogBlockMethod)
- 						inMethod: homeMethod.
- 			 self assert: map ~= 0.
- 			 self assert: ((objectMemory byteAt: map) >> AnnotationShift = HasBytecodePC "fiducial"
- 						 or: [(objectMemory byteAt: map) >> AnnotationShift = IsDisplacementX2N]).
- 			 [(objectMemory byteAt: map) >> AnnotationShift ~= HasBytecodePC] whileTrue:
- 				[map := map - 1].
- 			 map := map - 1. "skip fiducial; i.e. the map entry for the pc immediately following the method header."
- 			 aMethodObj := homeMethod methodObject.
- 			 bcpc := startbcpc - (self blockCreationBytecodeSizeForHeader: homeMethod methodHeader).
- 			 bsOffset := self bytecodeSetOffsetForHeader: homeMethod methodHeader.
- 			 byte := (objectMemory fetchByte: bcpc ofObject: aMethodObj) + bsOffset.
- 			 descriptor := self generatorAt: byte.
- 			 endbcpc := self nextBytecodePCFor: descriptor at: bcpc exts: -1 in: aMethodObj].
- 	bcpc := startbcpc.
- 	mcpc := cogMethod asUnsignedInteger + cogMethod stackCheckOffset.
- 	nExts := 0.
  	"as a hack for collecting counters, remember the prev mcpc in a static variable."
  	prevMapAbsPCMcpc := 0.
+ 	^super mapFor: cogMethod bcpc: startbcpc performUntil: functionSymbol arg: arg!
- 	"The stack check maps to the start of the first bytecode,
- 	 the first bytecode being effectively after frame build."
- 	result := self perform: functionSymbol
- 					with: nil
- 					with: (self cCoerceSimple: mcpc to: #'char *')
- 					with: startbcpc
- 					with: arg.
- 	result ~= 0 ifTrue:
- 		[^result].
- 	"Now skip up through the bytecode pc map entry for the stack check." 
- 	[(objectMemory byteAt: map) >> AnnotationShift ~= HasBytecodePC] whileTrue:
- 		[map := map - 1].
- 	map := map - 1.
- 	[(mapByte := objectMemory byteAt: map) ~= MapEnd] whileTrue: "defensive; we exit on bcpc"
- 		[mapByte >= FirstAnnotation
- 			ifTrue:
- 				[| annotation nextBcpc |
- 				annotation := mapByte >> AnnotationShift.
- 				mcpc := mcpc + (mapByte bitAnd: DisplacementMask).
- 				(self isPCMappedAnnotation: annotation alternateInstructionSet: bsOffset > 0) ifTrue:
- 					[[byte := (objectMemory fetchByte: bcpc ofObject: aMethodObj) + bsOffset.
- 					  descriptor := self generatorAt: byte.
- 					  isInBlock
- 						ifTrue: [bcpc >= endbcpc ifTrue: [^0]]
- 						ifFalse:
- 							[(descriptor isReturn and: [bcpc >= latestContinuation]) ifTrue: [^0].
- 							 (descriptor isBranch or: [descriptor isBlockCreation]) ifTrue:
- 								[| targetPC |
- 								 targetPC := self latestContinuationPCFor: descriptor at: bcpc exts: nExts in: aMethodObj.
- 								 latestContinuation := latestContinuation max: targetPC]].
- 					  nextBcpc := self nextBytecodePCFor: descriptor at: bcpc exts: nExts in: aMethodObj.
- 					  descriptor isMapped
- 					  or: [isInBlock and: [descriptor isMappedInBlock]]] whileFalse:
- 						[bcpc := nextBcpc.
- 						 nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]].
- 					"All subsequent bytecodes except backward branches map to the
- 					 following bytecode. Backward branches map to themselves other-
- 					 wise mapping could cause premature breaking out of loops." 
- 					result := self perform: functionSymbol
- 									with: descriptor
- 									with: (self cCoerceSimple: mcpc to: #'char *')
- 									with: ((descriptor isBranch
- 										   and: [self isBackwardBranch: descriptor at: bcpc exts: nExts in: aMethodObj])
- 											ifTrue: [bcpc]
- 											ifFalse: [bcpc + descriptor numBytes])
- 									with: arg.
- 					 result ~= 0 ifTrue:
- 						[^result].
- 					 bcpc := nextBcpc.
- 					 nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]].
- 				annotation = IsAbsPCReference ifTrue:
- 					[self assert: mcpc ~= 0.
- 					 prevMapAbsPCMcpc := mcpc]]
- 			ifFalse:
- 				[mcpc := mcpc + (mapByte >= DisplacementX2N
- 									ifTrue: [mapByte - DisplacementX2N << AnnotationShift]
- 									ifFalse: [mapByte])].
- 		 map := map - 1].
- 	^0!

Item was added:
+ ----- Method: SistaStackToRegisterMappingCogit>>maybeRememberPrevMap:absPCMcpc: (in category 'method map') -----
+ maybeRememberPrevMap: annotation absPCMcpc: mcpc
+ 	"Remember the previous IsAbsPCReference's mcpc for collecting send and branch data."
+ 	<inline: true>
+ 	annotation = IsAbsPCReference ifTrue:
+ 		[self assert: mcpc ~= 0.
+ 		 prevMapAbsPCMcpc := mcpc]!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>mcPCFor:startBcpc:in: (in category 'method map') -----
- mcPCFor: bcpc startBcpc: startbcpc in: cogMethod
- 	"Answer the absolute machine code pc matching the zero-relative bytecode pc argument
- 	 in cogMethod, given the start of the bytecodes for cogMethod's block or method object."
- 	<api>
- 	<var: #cogMethod type: #'CogBlockMethod *'>
- 	| absPC |
- 	absPC := self
- 				mapFor: cogMethod
- 				bcpc: startbcpc
- 				performUntil: #find:Mcpc:Bcpc:MatchingBcpc:
- 				arg: bcpc asVoidPointer.
- 	^absPC ~= 0
- 		ifTrue: [absPC asUnsignedInteger - cogMethod asUnsignedInteger]
- 		ifFalse: [absPC]!

Item was added:
+ ----- Method: SistaStackToRegisterMappingCogit>>picDataFor:IsBackwardBranch:Mcpc:Bcpc:Method: (in category 'method introspection') -----
+ picDataFor: descriptor IsBackwardBranch: IsBackwardBranch Mcpc: mcpc Bcpc: bcpc Method: cogMethodArg
+ 	<var: #descriptor type: #'BytecodeDescriptor *'>
+ 	<var: #mcpc type: #'char *'>
+ 	<var: #cogMethodArg type: #'void *'>
+ 	| entryPoint tuple |
+ 	descriptor isNil ifTrue:
+ 		[^0].
+ 	descriptor isBranch ifTrue:
+ 		["it's a branch; conditional?"
+ 		 (descriptor isBranchTrue or: [descriptor isBranchFalse]) ifTrue:
+ 			[tuple := self picDataForConditionalBranch: prevMapAbsPCMcpc at: bcpc + descriptor numBytes.
+ 			 tuple = 0 ifTrue: [^PrimErrNoMemory].
+ 			 objectMemory storePointer: picDataIndex ofObject: picData withValue: tuple.
+ 			 picDataIndex := picDataIndex + 1].
+ 		 ^0].
+ 	"infer it's a send; alas we can't just test the descriptor because of the bloody
+ 	 doubleExtendedDoAnythingBytecode which does sends as well as other things."
+ 	(backEnd isCallPreceedingReturnPC: mcpc asUnsignedInteger) ifFalse:
+ 		[^0].
+ 	entryPoint := backEnd callTargetFromReturnAddress: mcpc asUnsignedInteger.
+ 	entryPoint <= methodZoneBase ifTrue: "send is not linked, or is not a send"
+ 		[^0].
+ 	self targetMethodAndSendTableFor: entryPoint into: "It's a linked send; find which kind."
+ 		[:targetMethod :sendTable|
+ 		 tuple := self picDataForSendTo: targetMethod
+ 					methodClassIfSuper: (sendTable = superSendTrampolines ifTrue:
+ 												[coInterpreter methodClassOf:
+ 													(self cCoerceSimple: cogMethodArg to: #'CogMethod *') methodObject])
+ 					at: mcpc
+ 					bcpc: bcpc + 1].
+ 	tuple = 0 ifTrue: [^PrimErrNoMemory].
+ 	objectMemory storePointer: picDataIndex ofObject: picData withValue: tuple.
+ 	picDataIndex := picDataIndex + 1.
+ 	^0!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>picDataFor:Mcpc:Bcpc:Method: (in category 'method introspection') -----
- picDataFor: descriptor Mcpc: mcpc Bcpc: bcpc Method: cogMethodArg
- 	<var: #descriptor type: #'BytecodeDescriptor *'>
- 	<var: #mcpc type: #'char *'>
- 	<var: #cogMethodArg type: #'void *'>
- 	| entryPoint tuple |
- 	descriptor isNil ifTrue:
- 		[^0].
- 	descriptor isBranch ifTrue:
- 		["it's a branch; conditional?"
- 		 (descriptor isBranchTrue or: [descriptor isBranchFalse]) ifTrue:
- 			[tuple := self picDataForConditionalBranch: prevMapAbsPCMcpc at: bcpc.
- 			 tuple = 0 ifTrue: [^PrimErrNoMemory].
- 			 objectMemory storePointer: picDataIndex ofObject: picData withValue: tuple.
- 			 picDataIndex := picDataIndex + 1].
- 		 ^0].
- 	"infer it's a send; alas we can't just test the descriptor because of the bloody
- 	 doubleExtendedDoAnythingBytecode which does sends as well as other things."
- 	(backEnd isCallPreceedingReturnPC: mcpc asUnsignedInteger) ifFalse:
- 		[^0].
- 	entryPoint := backEnd callTargetFromReturnAddress: mcpc asUnsignedInteger.
- 	entryPoint <= methodZoneBase ifTrue: "send is not linked, or is not a send"
- 		[^0].
- 	self targetMethodAndSendTableFor: entryPoint into: "It's a linked send; find which kind."
- 		[:targetMethod :sendTable|
- 		 tuple := self picDataForSendTo: targetMethod
- 					methodClassIfSuper: (sendTable = superSendTrampolines ifTrue:
- 												[coInterpreter methodClassOf:
- 													(self cCoerceSimple: cogMethodArg to: #'CogMethod *') methodObject])
- 					at: mcpc
- 					bcpc: bcpc + 1 - descriptor numBytes].
- 	tuple = 0 ifTrue: [^PrimErrNoMemory].
- 	objectMemory storePointer: picDataIndex ofObject: picData withValue: tuple.
- 	picDataIndex := picDataIndex + 1.
- 	^0!

Item was changed:
  ----- Method: SistaStackToRegisterMappingCogit>>picDataFor:into: (in category 'method introspection') -----
  picDataFor: cogMethod into: arrayObj
  	"Collect the branch and send data for cogMethod, storing it into arrayObj."
  	<api>
  	<var: #cogMethod type: #'CogMethod *'>
  	| errCode |
  	cogMethod stackCheckOffset = 0 ifTrue:
  		[^0].
  	picDataIndex := 0.
  	picData := arrayObj.
  	errCode := self
  					mapFor: (self cCoerceSimple: cogMethod to: #'CogBlockMethod *')
  					bcpc: (coInterpreter startPCOfMethod: cogMethod methodObject)
+ 					performUntil: #picDataFor:IsBackwardBranch:Mcpc:Bcpc:Method:
- 					performUntil: #picDataFor:Mcpc:Bcpc:Method:
  					arg: cogMethod asVoidPointer.
  	errCode ~= 0 ifTrue:
  		[self assert: errCode = PrimErrNoMemory.
  		 ^-1].
  	cogMethod blockEntryOffset ~= 0 ifTrue:
  		[errCode := self blockDispatchTargetsFor: cogMethod
  						perform: #picDataForBlockEntry:Method:
+ 						arg: cogMethod asInteger.
- 						arg: cogMethod asVoidPointer.
  		 errCode ~= 0 ifTrue:
  			[self assert: errCode = PrimErrNoMemory.
  			 ^-1]].
  	^picDataIndex!

Item was changed:
  ----- Method: SistaStackToRegisterMappingCogit>>picDataForBlockEntry:Method: (in category 'method introspection') -----
  picDataForBlockEntry: blockEntryMcpc Method: cogMethod
  	"Collect the branch and send data for the block method starting at blockEntryMcpc, storing it into picData."
+ 	<returnTypeC: #usqInt>
  	| cogBlockMethod |
  	<var: #cogBlockMethod type: #'CogBlockMethod *'>
  	cogBlockMethod := self cCoerceSimple: blockEntryMcpc - (self sizeof: CogBlockMethod)
  							  to: #'CogBlockMethod *'.
  	cogBlockMethod stackCheckOffset = 0 ifTrue:
  		[^0].
  	^self
  		mapFor: cogBlockMethod
  		bcpc: cogBlockMethod startpc
+ 		performUntil: #picDataFor:IsBackwardBranch:Mcpc:Bcpc:Method:
+ 		arg: cogMethod asVoidPointer!
- 		performUntil: #picDataFor:Mcpc:Bcpc:Method:
- 		arg: cogMethod!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>print:Mcpc:Bcpc:on: (in category 'method map') -----
- print: desc Mcpc: mcpc Bcpc: bcpc on: aStream
- 	<doNotGenerate>
- 	aStream ensureCr.
- 	mcpc printOn: aStream base: 16.
- 	aStream space; tab; print: bcpc; cr; flush.
- 	^0!

Item was removed:
- ----- Method: SistaStackToRegisterMappingCogit>>printPCMapPairsFor:on: (in category 'method map') -----
- printPCMapPairsFor: cogMethod on: aStream
- 	<doNotGenerate>
- 	(self subMethodsAsRangesFor: cogMethod)
- 		do: [:sm|
- 			self mapFor: sm cogMethod bcpc: sm startpc performUntil: #print:Mcpc:Bcpc:on: arg: aStream]
- 		separatedBy: [aStream tab; next: 2 put: $=; cr]!

Item was changed:
  ----- Method: TMethod>>inlineFunctionCall:in: (in category 'inlining') -----
  inlineFunctionCall: aSendNode in: aCodeGen
  	"Answer the body of the called function, substituting the actual
  	 parameters for the formal argument variables in the method body.
  	 Assume caller has established that:
  		1. the method arguments are all substitutable nodes, and
  		2. the method to be inlined contains no additional embedded returns."
  
  	| sel meth doNotRename argsForInlining substitutionDict |
  	sel := aSendNode selector.
  	meth := (aCodeGen methodNamed: sel) copy.
+ 	(meth isNil and: [sel beginsWith: 'perform:']) ifTrue:
+ 		[^self inlineFunctionCall: aSendNode asTransformedConstantPerform in: aCodeGen].
  	doNotRename := Set withAll: args.
  	argsForInlining := aSendNode argumentsForInliningCodeGenerator: aCodeGen.
  	meth args with: argsForInlining do:
  		[ :argName :exprNode |
  		exprNode isLeaf ifTrue:
  			[doNotRename add: argName]].
  	(meth statements size = 2
  	and: [meth statements first isSend
  	and: [meth statements first selector == #flag:]]) ifTrue:
  		[meth statements removeFirst].
  	meth renameVarsForInliningInto: self except: doNotRename in: aCodeGen.
  	meth renameLabelsForInliningInto: self.
  	self addVarsDeclarationsAndLabelsOf: meth except: doNotRename.
  	substitutionDict := Dictionary new: meth args size * 2.
  	meth args with: argsForInlining do:
  		[ :argName :exprNode |
  		substitutionDict at: argName put: exprNode.
  		(doNotRename includes: argName) ifFalse:
  			[locals remove: argName]].
  	meth parseTree bindVariablesIn: substitutionDict.
  	^meth statements first expression!

Item was changed:
  ----- Method: TMethod>>inlineableFunctionCall:in: (in category 'inlining') -----
  inlineableFunctionCall: aNode in: aCodeGen
  	"Answer true if the given send node is a call to a 'functional' method--a method whose body is a single return statement of some expression and whose actual parameters can all be directly substituted."
  
- 	| m |
  	self maybeBreakFor: aNode in: aCodeGen.
  	^aNode isSend
+ 	  and: [(aCodeGen methodNamed: aNode selector)
+ 			ifNil:
+ 				[aNode asTransformedConstantPerform
+ 					ifNil: [false]
+ 					ifNotNil: [:n| self inlineableFunctionCall: n in: aCodeGen]]
+ 			ifNotNil:
+ 				[:m|
+ 				 m ~~ self
+ 				 and: [m isFunctional
+ 				 and: [(aCodeGen mayInline: m selector)
+ 				 and: [aNode args allSatisfy: [ :a | self isSubstitutableNode: a intoMethod: m in: aCodeGen]]]]]]!
- 	and: [(m := aCodeGen methodNamed: aNode selector) notNil  "nil if builtin or external function"
- 	and: [m ~~ self
- 	and: [m isFunctional
- 	and: [(aCodeGen mayInline: m selector)
- 	and: [aNode args allSatisfy: [ :a | self isSubstitutableNode: a intoMethod: m in: aCodeGen]]]]]]!

Item was changed:
  ----- Method: TMethod>>isSubstitutableNode:intoMethod:in: (in category 'inlining') -----
  isSubstitutableNode: aNode intoMethod: targetMeth in: aCodeGen
  	"Answer true if the given parameter node is either a constant, a local variable, or a formal parameter of the receiver. Such parameter nodes may be substituted directly into the body of the method during inlining. Note that global variables cannot be subsituted into methods with possible side effects (i.e., methods that may assign to global variables) because the inlined method might depend on having the value of the global variable captured when it is passed in as an argument."
  
  	| var |
  	aNode isConstant ifTrue: [ ^ true ].
  
  	aNode isVariable ifTrue: [
  		var := aNode name.
  		((locals includes: var) or: [args includes: var]) ifTrue: [ ^ true ].
  		(#(self true false nil) includes: var) ifTrue: [ ^ true ].
  		(targetMeth maySubstituteGlobal: var in: aCodeGen) ifTrue: [ ^ true ].
  	].
  
  	"For now allow literal blocks to be substituted.  They better be accessed only
  	 with value[:value:*] messages though!!"
  	aNode isStmtList ifTrue: [^true].
  
  	(aNode isSend
  	 and: [aNode numArgs = 0
  	 and: [aNode isStructSendIn: aCodeGen]]) ifTrue:
  		[^true].
  
  	"scan expression tree; must contain only constants, builtin ops, and inlineable vars"
  	aNode nodesDo: [ :node |
  		node isSend ifTrue: [
+ 			(node isBuiltinOperator
+ 			 or: [node numArgs = 0
+ 				 and: [node isStructSendIn: aCodeGen]]) ifFalse: [ ^false ].
- 			node isBuiltinOperator ifFalse: [ ^false ].
  		].
  		node isVariable ifTrue: [
  			var := node name.
  			((locals includes: var) or:
  			 [(args includes: var) or:
  			 [(#(self true false nil) includes: var) or:
  			 [targetMeth maySubstituteGlobal: var in: aCodeGen]]]) ifFalse: [ ^ false ].
  		].
  		(node isConstant or: [node isVariable or: [node isSend]]) ifFalse: [ ^false ].
  	].
  
  	^ true!

Item was added:
+ ----- Method: TSendNode>>asTransformedConstantPerform (in category 'inlining support') -----
+ asTransformedConstantPerform
+ 	"If the receiver is a send of perform: with a constant selector,
+ 	 answer a send node that elides the perform:, otherwise answer nil."
+ 	^(selector isSymbol
+ 	  and: [(selector beginsWith: #perform:)
+ 	  and: [(selector keywords allSatisfy: [:kw| #('perform:' 'with:') includes: kw])
+ 	  and: [arguments first isConstant
+ 	  and: [arguments first value isSymbol
+ 	  and: [arguments first value numArgs + 1 = arguments size]]]]]) ifTrue:
+ 		[TSendNode new
+ 			setSelector: arguments first value
+ 			receiver: receiver
+ 			arguments: arguments allButFirst]!



More information about the Vm-dev mailing list