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

commits at source.squeak.org commits at source.squeak.org
Sat Dec 6 03:04:09 UTC 2014


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

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

Name: VMMaker.oscog-eem.969
Author: eem
Time: 5 December 2014, 7:01:27.391 pm
UUID: cdd723c1-bec0-42d5-970f-354702984955
Ancestors: VMMaker.oscog-eem.968

Two more cases where the mnu compilation break
point needs to be checked.

Use numBytesOf: for length of selector.

Rename checkValidInlineCacheTag: to
validInlineCacheTag:.  Implement it in the right place
in the obj rep hierarchy for Spur.

Add a debug lookup routine that only looks up (and follows forwarding pointers).

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

Item was added:
+ ----- Method: CogObjectRepresentation>>validInlineCacheTag: (in category 'debug support') -----
+ validInlineCacheTag: classIndexOrTagPattern
+ 	self subclassResponsibility!

Item was added:
+ ----- Method: CogObjectRepresentationFor32BitSpur>>validInlineCacheTag: (in category 'debug support') -----
+ validInlineCacheTag: classIndexOrTagPattern
+ 	"The two valid tag patterns are 0 (Character) and 1 (SmallInteger)"
+ 	<var: 'classIndexOrTagPattern' type: #usqInt>
+ 	^classIndexOrTagPattern <= 1
+ 	  or: [(objectMemory classAtIndex: classIndexOrTagPattern) notNil]!

Item was added:
+ ----- Method: CogObjectRepresentationFor64BitSpur>>validInlineCacheTag: (in category 'debug support') -----
+ validInlineCacheTag: classIndexOrTagPattern
+ 	"The three valid tag patterns are 1 (SmallInteger), 2 (Character) and 3 (SmallFloat64)."
+ 	^(classIndexOrTagPattern >= 1
+ 	  and: [classIndexOrTagPattern <= 3])
+ 	  or: [(objectMemory classAtIndex: classIndexOrTagPattern) notNil]!

Item was removed:
- ----- Method: CogObjectRepresentationForSpur>>checkValidInlineCacheTag: (in category 'debug support') -----
- checkValidInlineCacheTag: classIndexOrTagPattern
- 	^classIndexOrTagPattern <= objectMemory tagMask
- 	  or: [(objectMemory classAtIndex: classIndexOrTagPattern) notNil]!

Item was removed:
- ----- Method: CogObjectRepresentationForSqueakV3>>checkValidInlineCacheTag: (in category 'garbage collection') -----
- checkValidInlineCacheTag: cacheTag
- 	^cacheTag = ConstZero
- 	  or: [((cacheTag bitAnd: 1 << objectMemory shiftForWord - 1) = 0
- 		   and: [cacheTag
- 				between: 1 << objectMemory compactClassFieldLSB
- 				and: (objectMemory compactClassIndexOfHeader: -1) << objectMemory compactClassFieldLSB])
- 		 or: [self checkValidObjectReference: cacheTag]]!

Item was added:
+ ----- Method: CogObjectRepresentationForSqueakV3>>validInlineCacheTag: (in category 'garbage collection') -----
+ validInlineCacheTag: cacheTag
+ 	^cacheTag = ConstZero
+ 	  or: [((cacheTag bitAnd: 1 << objectMemory shiftForWord - 1) = 0
+ 		   and: [cacheTag
+ 				between: 1 << objectMemory compactClassFieldLSB
+ 				and: (objectMemory compactClassIndexOfHeader: -1) << objectMemory compactClassFieldLSB])
+ 		 or: [self checkValidObjectReference: cacheTag]]!

Item was changed:
  ----- Method: Cogit>>checkIfValidOopRef:pc:cogMethod: (in category 'garbage collection') -----
  checkIfValidOopRef: annotation pc: mcpc cogMethod: cogMethod
  	<var: #mcpc type: #'char *'>
  	annotation = IsObjectReference ifTrue:
  		[| literal |
  		 literal := backEnd literalBeforeFollowingAddress: mcpc asInteger.
  		 (objectRepresentation checkValidOopReference: literal) ifFalse:
  			[coInterpreter print: 'object ref leak in CM '; printHex: cogMethod asInteger; print: ' @ '; printHex: mcpc asInteger; cr.
  			^1]].
  	(self isSendAnnotation: annotation) ifTrue:
  		[| entryPoint selectorOrCacheTag offset |
  		 entryPoint := backEnd callTargetFromReturnAddress: mcpc asInteger.
  		 entryPoint <= methodZoneBase
  			ifTrue:
  				[offset := entryPoint]
  			ifFalse:
  				[self
  					offsetAndSendTableFor: entryPoint
  					annotation: annotation
  					into: [:off :table| offset := off]].
  		 selectorOrCacheTag := backEnd inlineCacheTagAt: mcpc asInteger.
  		 (entryPoint > methodZoneBase
  		  and: [offset ~= cmNoCheckEntryOffset
  		  and: [(self cCoerceSimple: entryPoint - offset to: #'CogMethod *') cmType ~= CMOpenPIC]])
  			ifTrue: "linked non-super send, cacheTag is a cacheTag"
+ 				[(objectRepresentation validInlineCacheTag: selectorOrCacheTag) ifFalse:
- 				[(objectRepresentation checkValidInlineCacheTag: selectorOrCacheTag) ifFalse:
  					[coInterpreter print: 'cache tag leak in CM '; printHex: cogMethod asInteger; print: ' @ '; printHex: mcpc asInteger; cr.
  					^1]]
  			ifFalse: "unlinked send or super send; cacheTag is a selector"
  				[(objectRepresentation checkValidOopReference: selectorOrCacheTag) ifFalse:
  					[coInterpreter print: 'selector leak in CM '; printHex: cogMethod asInteger; print: ' @ '; printHex: mcpc asInteger; cr.
  					^1]]].
  	^0 "keep scanning"!

Item was changed:
  ----- Method: Cogit>>checkIfValidOopRefAndTarget:pc:cogMethod: (in category 'garbage collection') -----
  checkIfValidOopRefAndTarget: annotation pc: mcpc cogMethod: cogMethod
  	<var: #mcpc type: #'char *'>
  	| literal entryPoint |
  	annotation = IsObjectReference ifTrue:
  		[literal := backEnd literalBeforeFollowingAddress: mcpc asInteger.
  		 (self asserta: (objectRepresentation checkValidOopReference: literal)) ifFalse:
  			[^1].
  		((objectRepresentation couldBeObject: literal)
  		 and: [objectMemory isReallyYoungObject: literal]) ifTrue:
  			[(self asserta: (self cCoerceSimple: cogMethod to: #'CogMethod *') cmRefersToYoung) ifFalse:
  				[^2]]].
  	(self isSendAnnotation: annotation) ifTrue:
  		[(self asserta: (self cCoerceSimple: cogMethod to: #'CogMethod *') cmType = CMMethod) ifFalse:
  			[^3].
  		 self offsetCacheTagAndCouldBeObjectAt: mcpc annotation: annotation into:
  			[:offset :cacheTag :tagCouldBeObject|
  			tagCouldBeObject
  				ifTrue:
  					[(objectRepresentation couldBeObject: cacheTag)
  						ifTrue:
  							[(self asserta: (objectRepresentation checkValidOopReference: cacheTag)) ifFalse:
  								[^4]]
  						ifFalse:
+ 							[(self asserta: (objectRepresentation validInlineCacheTag: cacheTag)) ifFalse:
- 							[(self asserta: (objectRepresentation checkValidInlineCacheTag: cacheTag)) ifFalse:
  								[^5]].
  					((objectRepresentation couldBeObject: cacheTag)
  					 and: [objectMemory isReallyYoungObject: cacheTag]) ifTrue:
  						[(self asserta: (self cCoerceSimple: cogMethod to: #'CogMethod *') cmRefersToYoung) ifFalse:
  							[^6]]]
  				ifFalse:
+ 					[(self asserta: (objectRepresentation validInlineCacheTag: cacheTag)) ifFalse:
- 					[(self asserta: (objectRepresentation checkValidInlineCacheTag: cacheTag)) ifFalse:
  						[^7]]].
  		entryPoint := backEnd callTargetFromReturnAddress: mcpc asInteger.
  		entryPoint > methodZoneBase ifTrue:
  			["It's a linked send; find which kind."
  			 self targetMethodAndSendTableFor: entryPoint into:
  					[:targetMethod :sendTable|
  					 (self asserta: (targetMethod cmType = CMMethod
  								   or: [targetMethod cmType = CMClosedPIC
  								   or: [targetMethod cmType = CMOpenPIC]])) ifFalse:
  						[^8]]]].
  	^0 "keep scanning"!

Item was changed:
  ----- Method: Cogit>>cogExtendPIC:CaseNMethod:tag:isMNUCase: (in category 'in-line cacheing') -----
  cogExtendPIC: cPIC CaseNMethod: caseNMethod tag: caseNTag isMNUCase: isMNUCase
  	"Extend the cPIC with the supplied case.  If caseNMethod is cogged dispatch direct to
  	 its unchecked entry-point.  If caseNMethod is not cogged, jump to the fast interpreter
  	 dispatch, and if isMNUCase then dispatch to fast MNU invocation and mark the cPIC as
  	 having the MNU case for cache flushing."
   	<var: #cPIC type: #'CogMethod *'>
  	| operand target address size end |
  	"stack allocate the various collections so that they
  	 are effectively garbage collected on return."
  	coInterpreter
  		compilationBreak: cPIC selector
+ 		point: (objectMemory numBytesOf: cPIC selector)
- 		point: (objectMemory lengthOf: cPIC selector)
  		isMNUCase: isMNUCase.
  	self allocateOpcodes: 5 bytecodes: 0.
  	self assert: (objectRepresentation inlineCacheTagIsYoung: caseNTag) not.
  	"Caller patches to open pic if caseNMethod is young."
  	self assert: (caseNMethod notNil and: [(objectMemory isYoung: caseNMethod) not]).
  	(isMNUCase not
  	 and: [coInterpreter methodHasCogMethod: caseNMethod])
  		ifTrue:
  			[operand := 0.
+ 			 target := (coInterpreter cogMethodOf: caseNMethod) asInteger + cmNoCheckEntryOffset]
- 			 target :=  (coInterpreter cogMethodOf: caseNMethod) asInteger + cmNoCheckEntryOffset]
  		ifFalse:
  			[isMNUCase ifTrue:
  				[cPIC cpicHasMNUCase: true].
  			 operand := caseNMethod.
  			 target := cPIC asInteger
  					+ (isMNUCase
  						ifTrue: [self sizeof: CogMethod]
  						ifFalse: [self interpretOffset - backEnd callInstructionByteSize])].
  	self CmpCw: caseNTag R: TempReg.
  	self MoveCw: operand R: SendNumArgsReg.
  	self JumpLongZero: target.
  	self MoveCw: cPIC asInteger R: ClassReg.
  	self JumpLong: (self cPICMissTrampolineFor: cPIC cmNumArgs).
  
  	self computeMaximumSizes.
  	address := self addressOfEndOfCase: cPIC cPICNumCases - 1 inCPIC: cPIC.
  	size := self generateInstructionsAt: address.
  	end := self outputInstructionsAt: address.
  	processor flushICacheFrom: address to: cPIC asInteger + closedPICSize.
  	cPIC cPICNumCases: cPIC cPICNumCases + 1.
  	^0!

Item was changed:
  ----- Method: Cogit>>cogMNUPICSelector:methodOperand:numArgs: (in category 'in-line cacheing') -----
  cogMNUPICSelector: selector methodOperand: methodOperand numArgs: numArgs
  	<api>
  	"Attempt to create a one-case PIC for an MNU.
  	 The tag for the case is at the send site and so doesn't need to be generated."
  	<returnTypeC: #'CogMethod *'>
  	| startAddress headerSize size end |
  	(objectMemory isYoung: selector) ifTrue:
  		[^0].
  	coInterpreter
  		compilationBreak: selector
+ 		point: (objectMemory numBytesOf: selector)
- 		point: (objectMemory lengthOf: selector)
  		isMNUCase: true.
  	self assert: endCPICCase0 notNil.
  	startAddress := methodZone allocate: closedPICSize.
  	startAddress = 0 ifTrue:
  		[coInterpreter callForCogCompiledCodeCompaction.
  		 ^0].
  	methodLabel
  		address: startAddress;
  		dependent: nil.
  	"stack allocate the various collections so that they
  	 are effectively garbage collected on return."
  	self allocateOpcodes: numPICCases * 7 bytecodes: 0.
  	self compileMNUCPIC: (self cCoerceSimple: startAddress to: #'CogMethod *')
  		methodOperand: methodOperand
  		numArgs: numArgs.
  	self computeMaximumSizes.
  	headerSize := self sizeof: CogMethod.
  	size := self generateInstructionsAt: startAddress + headerSize.
  	end := self outputInstructionsAt: startAddress + headerSize.
  	"The missOffset is the same as the interpretOffset."
  	self assert: missOffset = (interpretCall address + interpretCall machineCodeSize - startAddress).
  	self assert: startAddress + cmEntryOffset = entry address.
  	^self
  		fillInCPICHeader: (self cCoerceSimple: startAddress to: #'CogMethod *')
  		size: closedPICSize
  		numArgs: numArgs
  		numCases: 1
  		hasMNUCase: true
  		selector: selector !

Item was changed:
  ----- Method: Cogit>>cogOpenPICSelector:numArgs: (in category 'in-line cacheing') -----
  cogOpenPICSelector: selector numArgs: numArgs
  	"Create an Open PIC.  Temporarily create a direct call of ceSendFromOpenPIC:.
  	 Should become a probe of the first-level method lookup cache followed by a
  	 call of ceSendFromOpenPIC: if the probe fails."
  	<returnTypeC: #'CogMethod *'>
  	| startAddress headerSize codeSize mapSize end |
  	coInterpreter
  		compilationBreak: selector
+ 		point: (objectMemory numBytesOf: selector)
- 		point: (objectMemory lengthOf: selector)
  		isMNUCase: false.
  	startAddress := methodZone allocate: openPICSize.
  	startAddress = 0 ifTrue:
  		[^self cCoerceSimple: InsufficientCodeSpace to: #'CogMethod *'].
  	methodLabel
  		address: startAddress;
  		dependent: nil.
  	"stack allocate the various collections so that they
  	 are effectively garbage collected on return."
  	self allocateOpcodes: 100 bytecodes: 0.
  	self compileOpenPIC: selector numArgs: numArgs.
  	self computeMaximumSizes.
  	methodLabel concretizeAt: startAddress.
  	headerSize := self sizeof: CogMethod.
  	codeSize := self generateInstructionsAt: startAddress + headerSize.
  	mapSize := self generateMapAt: startAddress + openPICSize - 1 start: startAddress + cmNoCheckEntryOffset.
  	self assert: entry address - startAddress = cmEntryOffset.
  	self assert: headerSize + codeSize + mapSize <= openPICSize.
  	end := self outputInstructionsAt: startAddress + headerSize.
  	^self
  		fillInOPICHeader: (self cCoerceSimple: startAddress to: #'CogMethod *')
  		size: openPICSize
  		numArgs: numArgs
  		selector: selector !

Item was changed:
  ----- Method: Cogit>>cogPICSelector:numArgs:Case0Method:Case1Method:tag:isMNUCase: (in category 'in-line cacheing') -----
  cogPICSelector: selector numArgs: numArgs Case0Method: case0CogMethod Case1Method: case1MethodOrNil tag: case1Tag isMNUCase: isMNUCase
  	"Attempt to create a two-case PIC for case0CogMethod and  case1Method,case1Tag.
  	 The tag for case0CogMethod is at the send site and so doesn't need to be generated.
  	 case1Method may be any of
  		- a Cog method; link to its unchecked entry-point
  		- a CompiledMethod; link to ceInterpretMethodFromPIC:
  		- a CompiledMethod; link to ceMNUFromPICMNUMethod:receiver:"
  	<var: #case0CogMethod type: #'CogMethod *'>
  	<returnTypeC: #'CogMethod *'>
  	| startAddress headerSize size end |
  	(objectMemory isYoung: selector) ifTrue:
  		[^self cCoerceSimple: YoungSelectorInPIC to: #'CogMethod *'].
  	coInterpreter
  		compilationBreak: selector
+ 		point: (objectMemory numBytesOf: selector)
- 		point: (objectMemory lengthOf: selector)
  		isMNUCase: isMNUCase.
  	startAddress := methodZone allocate: closedPICSize.
  	startAddress = 0 ifTrue:
  		[^self cCoerceSimple: InsufficientCodeSpace to: #'CogMethod *'].
  	methodLabel
  		address: startAddress;
  		dependent: nil.
  	"stack allocate the various collections so that they
  	 are effectively garbage collected on return."
  	self allocateOpcodes: numPICCases * 7 bytecodes: 0.
  	self compileCPIC: (self cCoerceSimple: startAddress to: #'CogMethod *')
  		Case0: case0CogMethod
  		Case1Method: case1MethodOrNil
  		tag: case1Tag
  		isMNUCase: isMNUCase
  		numArgs: numArgs.
  	self computeMaximumSizes.
  	headerSize := self sizeof: CogMethod.
  	size := self generateInstructionsAt: startAddress + headerSize.
  	end := self outputInstructionsAt: startAddress + headerSize.
  	"The missOffset is th same as the interpretOffset."
  	self assert: missOffset = (interpretCall address + interpretCall machineCodeSize - startAddress).
  	self assert: startAddress + cmEntryOffset = entry address.
  	self assert: endCPICCase0 address = (startAddress + firstCPICCaseOffset).
  	self assert: endCPICCase1 address = (startAddress + firstCPICCaseOffset + cPICCaseSize).
  	^self
  		fillInCPICHeader: (self cCoerceSimple: startAddress to: #'CogMethod *')
  		size: closedPICSize
  		numArgs: numArgs
  		numCases: 2
  		hasMNUCase: isMNUCase
  		selector: selector !

Item was changed:
  ----- Method: Cogit>>compileCPIC:Case0:Case1Method:tag:isMNUCase:numArgs: (in category 'in-line cacheing') -----
  compileCPIC: cPIC Case0: case0CogMethod Case1Method: case1Method tag: case1Tag isMNUCase: isMNUCase numArgs: numArgs
  	"Compile the code for a two-case PIC for case0CogMethod and  case1Method,case1Tag.
  	 The tag for case0CogMethod is at the send site and so doesn't need to be generated.
  	 case1Method may be any of
  		- a Cog method; jump to its unchecked entry-point
  		- a CompiledMethod; jump to the ceInterpretFromPIC trampoline
  		- nil; call ceMNUFromPIC"
  	<var: #cPIC type: #'CogMethod *'>
  	| operand targetEntry jumpNext |
  	<var: #case0CogMethod type: #'CogMethod *'>
  	<var: #targetEntry type: #'void *'>
  	<var: #jumpNext type: #'AbstractInstruction *'>
  	self assert: case1Method notNil.
+ 	coInterpreter
+ 		compilationBreak: cPIC selector
+ 		point: (objectMemory numBytesOf: cPIC selector)
+ 		isMNUCase: isMNUCase.
  	self compilePICProlog: numArgs.
  	self assert: (objectRepresentation inlineCacheTagIsYoung: case1Tag) not.
  	(isMNUCase not
  	 and: [coInterpreter methodHasCogMethod: case1Method])
  		ifTrue:
  			[operand := 0.
  			 targetEntry := ((coInterpreter cogMethodOf: case1Method) asInteger + cmNoCheckEntryOffset) asVoidPointer]
  		ifFalse:
  			[self assert: (case1Method isNil or: [(objectMemory isYoungObject: case1Method) not]).
  			 operand := case1Method.
  			 targetEntry := case1Method isNil ifTrue: [mnuCall] ifFalse: [interpretLabel]].
  
  	jumpNext := self compileCPICEntry.
  	self MoveCw: 0 R: SendNumArgsReg.
  	self JumpLong: case0CogMethod asInteger + cmNoCheckEntryOffset.
  	endCPICCase0 := self CmpCw: case1Tag R: TempReg.
  	jumpNext jmpTarget: endCPICCase0.
  	self MoveCw: operand R: SendNumArgsReg.
  	self JumpLongZero: (isMNUCase ifTrue: [mnuCall] ifFalse: [targetEntry]) asInteger.
  	endCPICCase1 := self MoveCw: cPIC asInteger R: ClassReg.
  	self JumpLong: (self cPICMissTrampolineFor: numArgs).
  	^0
  !

Item was changed:
  ----- Method: Cogit>>compileMNUCPIC:methodOperand:numArgs: (in category 'in-line cacheing') -----
  compileMNUCPIC: cPIC methodOperand: methodOperand numArgs: numArgs
  	"Compile the code for a one-case MNU PIC that calls ceMNUFromPIC for case0Tag
  	 The tag for case0 is at the send site and so doesn't need to be generated."
  	<var: #cPIC type: #'CogMethod *'>
  	| jumpNext |
  	<var: #jumpNext type: #'AbstractInstruction *'>
+ 	coInterpreter
+ 		compilationBreak: cPIC selector
+ 		point: (objectMemory numBytesOf: cPIC selector)
+ 		isMNUCase: true.
  	self compilePICProlog: numArgs.
  	jumpNext := self compileCPICEntry.
  	self MoveCw: methodOperand R: SendNumArgsReg.
  	self JumpLong: mnuCall asInteger.
  	jumpNext jmpTarget: (self MoveCw: cPIC asInteger R: ClassReg).
  	self JumpLong: (self cPICMissTrampolineFor: numArgs).
  	^0
  !

Item was changed:
  ----- Method: Cogit>>setSelectorOf:to: (in category 'jit - api') -----
  setSelectorOf: cogMethod to: aSelectorOop
  	<api>
  	"If a method is compiled to machine code via a block entry it won't have a selector.
  	 A subsequent send can find the method and hence fill in the selector."
  	<var: #cogMethod type: #'CogMethod *'>
  	"self disassembleMethod: cogMethod"
  	coInterpreter
  		compilationBreak: aSelectorOop
+ 		point: (objectMemory numBytesOf: aSelectorOop)
- 		point: (objectMemory lengthOf: aSelectorOop)
  		isMNUCase: false.
  	self assert: cogMethod cmType = CMMethod.
  	cogMethod selector: aSelectorOop.
  	(objectMemory isYoung: aSelectorOop) ifTrue:
  		[methodZone ensureInYoungReferrers: cogMethod]!

Item was added:
+ ----- Method: StackInterpreter>>lookupSelector:inClass: (in category 'debug support') -----
+ lookupSelector: selector inClass: class
+ 	"Lookup selector in class.  Answer the method or nil.  This is a debugging routine."
+ 	| currentClass dictionary |
+ 	<api>
+ 
+ 	currentClass := class.
+ 	[currentClass ~= objectMemory nilObject] whileTrue:
+ 		[dictionary := objectMemory followObjField: MethodDictionaryIndex ofObject: currentClass.
+ 		 dictionary = objectMemory nilObject ifTrue:
+ 			[^nil].
+ 		 (self lookupMethodFor: selector InDictionary: dictionary) ifNotNil:
+ 			[:meth| ^meth].
+ 		currentClass := self superclassOf: currentClass].
+ 	^nil!

Item was changed:
+ ----- Method: StackInterpreter>>mnuBreak:point:receiver: (in category 'debug support') -----
- ----- Method: StackInterpreter>>mnuBreak:point:receiver: (in category 'debugging traps') -----
  mnuBreak: selectorString point: selectorLength receiver: receiverOrNil
  	<doNotGenerate> "C version is in platforms/Cross/vm/dispdbg.h"
  	"self shortPrintFrameAndCallers: localFP"
  	| i |
  	breakSelectorLength negated = selectorLength ifTrue:
  		[i := breakSelectorLength negated.
  		 [i > 0] whileTrue:
  			[(objectMemory byteAt: selectorString + i - 1) = (breakSelector at: i) asInteger
  				ifTrue: [(i := i - 1) = 0 ifTrue:
  							[self changed: #byteCountText.
  							 self halt: 'MNU of '
  									, breakSelector,
  									(receiverOrNil
  										ifNotNil: [' to ', (self shortPrint: receiverOrNil)]
  										ifNil: [''])]]
  				ifFalse: [i := 0]]]!

Item was changed:
+ ----- Method: StackInterpreter>>sendBreak:point:receiver: (in category 'debug support') -----
- ----- Method: StackInterpreter>>sendBreak:point:receiver: (in category 'debugging traps') -----
  sendBreak: selectorString point: selectorLength receiver: receiverOrNil
  	<doNotGenerate> "C version is in platforms/Cross/vm/dispdbg.h"
  	"self shortPrintFrameAndCallers: localFP"
  	| i |
  	breakSelectorLength = selectorLength ifTrue:
  		[i := breakSelectorLength.
  		 [i > 0] whileTrue:
  			[(objectMemory byteAt: selectorString + i - 1) = (breakSelector at: i) asInteger
  				ifTrue: [(i := i - 1) = 0 ifTrue:
  							[self changed: #byteCountText.
  							 self halt: 'Send of '
  									, breakSelector,
  									(receiverOrNil
  										ifNotNil: [' to ', (self shortPrint: receiverOrNil)]
  										ifNil: [''])]]
  				ifFalse: [i := 0]]]!



More information about the Vm-dev mailing list