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

commits at source.squeak.org commits at source.squeak.org
Tue Feb 24 03:56:04 UTC 2015


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

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

Name: VMMaker.oscog-eem.1075
Author: eem
Time: 23 February 2015, 7:54:39.978 pm
UUID: 3ced243a-888b-4008-8036-42ec11da99d7
Ancestors: VMMaker.oscog-eem.1074

Slang:
More improvement to type inferrence/propagation.
Support ifTrue:ifFalse:.  Better separate the passes in
inferTypesForImplicitlyTypedVariablesAndMethods.
Fix slip in nodeToCast:to:.  Provide types for atan, et al.

Make the inline selector breakpoint machinery allow
multiple selectors, and use it to break for type inferrence
too.

Don't inline complex expressions that are bound to
variables used in asserts.

Make sure return types are symbols if they exist as symbols.

General:
Simplify primitiveMakePoint. Include primitiveMethodXray

Fix some type warnings in Spur stack VMs.

Fix privacy violation checking for super sends.

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

Item was changed:
  Object subclass: #CCodeGenerator
+ 	instanceVariableNames: 'vmClass structClasses translationDict asArgumentTranslationDict inlineList constants variables variableDeclarations scopeStack methods macros apiMethods kernelReturnTypes currentMethod headerFiles globalVariableUsage useSymbolicConstants generateDeadCode requiredSelectors logger suppressAsmLabels asmLabelCounts pools selectorTranslations optionsDictionary breakSrcInlineSelectors breakDestInlineSelectors breakOnInline vmMaker'
- 	instanceVariableNames: 'vmClass structClasses translationDict asArgumentTranslationDict inlineList constants variables variableDeclarations scopeStack methods macros apiMethods kernelReturnTypes currentMethod headerFiles globalVariableUsage useSymbolicConstants generateDeadCode requiredSelectors logger suppressAsmLabels asmLabelCounts pools selectorTranslations optionsDictionary breakSrcInlineSelector breakDestInlineSelector breakOnInline vmMaker'
  	classVariableNames: 'NoRegParmsInAssertVMs'
  	poolDictionaries: 'VMBasicConstants'
  	category: 'VMMaker-Translation to C'!
  
  !CCodeGenerator commentStamp: 'tpr 5/2/2003 14:30' prior: 0!
  This class oversees the translation of a subset of Smalltalk to C, allowing the comforts of Smalltalk during development and the efficiency and portability of C for the resulting interpreter.  
  See VMMaker for more useful info!

Item was changed:
  ----- Method: CCodeGenerator>>breakDestInlineSelector: (in category 'accessing') -----
  breakDestInlineSelector: aSelector
+ 	breakDestInlineSelectors add: aSelector!
- 	breakDestInlineSelector := aSelector!

Item was changed:
  ----- Method: CCodeGenerator>>breakSrcInlineSelector: (in category 'accessing') -----
  breakSrcInlineSelector: aSelector
+ 	breakSrcInlineSelectors add: aSelector!
- 	breakSrcInlineSelector := aSelector!

Item was changed:
  ----- Method: CCodeGenerator>>collectInlineList: (in category 'inlining') -----
  collectInlineList: inlineFlagOrSymbol
  	"Make a list of methods that should be inlined.  If inlineFlagOrSymbol == #asSpecified
  	 only inline methods marked with <inline: true>."
  	"Details: The method must not include any inline C, since the
  	 translator cannot currently map variable names in inlined C code.
  	 Methods to be inlined must be small or called from only one place."
  
  	| selectorsOfMethodsNotToInline callsOf |
  	self assert: (#(true false asSpecified) includes: inlineFlagOrSymbol).
  	selectorsOfMethodsNotToInline := Set new: methods size.
  	selectorsOfMethodsNotToInline addAll: macros keys.
  	apiMethods ifNotNil:
  		[selectorsOfMethodsNotToInline addAll: apiMethods keys].
  	methods do:
  		[:m|
  		m isStructAccessor ifTrue:
  			[selectorsOfMethodsNotToInline add: m selector]].
  
  	"build dictionary to record the number of calls to each method"
  	callsOf := Dictionary new: methods size * 2.
  	methods keysAndValuesDo:
  		[:s :m|
  		(m isRealMethod
  		 and: [self shouldGenerateMethod: m]) ifTrue:
  			[callsOf at: s put: 0]].
  
  	"For each method, scan its parse tree once or twice to:
  		1. determine if the method contains unrenamable C code or declarations or has a C builtin
  		2. determine how many nodes it has
  		3. increment the sender counts of the methods it calls"
  	inlineList := Set new: methods size * 2.
  	(methods reject: [:m| selectorsOfMethodsNotToInline includes: m selector]) do:
  		[:m| | inlineIt hasUnrenamableCCode nodeCount |
+ 		((breakSrcInlineSelectors includes: m selector)
- 		(breakSrcInlineSelector = m selector
  		 and: [breakOnInline isNil]) ifTrue:
  			[self halt].
  		inlineIt := #dontCare.
  		(translationDict includesKey: m selector)
  			ifTrue: [hasUnrenamableCCode := true]
  			ifFalse:
  				[hasUnrenamableCCode := m hasUnrenamableCCode.
  				 nodeCount := 0.
  				 m parseTree nodesDo:
  					[:node|
  					node isSend ifTrue:
  						[callsOf
  							at: node selector
  							ifPresent:
  								[:senderCount| callsOf at: node selector put: senderCount + 1]].
  					 nodeCount := nodeCount + 1].
  				inlineIt := m extractInlineDirective].  "may be true, false, or #dontCare"
  		(hasUnrenamableCCode or: [inlineIt == false])
  			ifTrue: "don't inline if method has C code or contains negative inline directive"
  				[inlineIt == true ifTrue:
  					[logger
  						ensureCr;
  						nextPutAll: 'failed to inline ';
  						nextPutAll: m selector;
  						nextPutAll: ' as it contains unrenamable C declarations or C code';
  						cr; flush].
  				selectorsOfMethodsNotToInline add: m selector]
  			ifFalse:
  				[(inlineFlagOrSymbol == #asSpecified
  					ifTrue: [inlineIt == true]
  					ifFalse: [nodeCount < 40 or: [inlineIt == true]]) ifTrue:
  				"inline if method has no C code and is either small or contains inline directive"
  					[inlineList add: m selector]]].
  
  	inlineFlagOrSymbol ~~ #asSpecified ifTrue:
  		[callsOf associationsDo:
  			[:assoc|
  			(assoc value = 1
  			 and: [(selectorsOfMethodsNotToInline includes: assoc key) not]) ifTrue:
  				[inlineList add: assoc key]]]!

Item was changed:
  ----- Method: CCodeGenerator>>inferTypesForImplicitlyTypedVariablesAndMethods (in category 'type inference') -----
  inferTypesForImplicitlyTypedVariablesAndMethods
  	"Infer the return tupe and the types of untyped variables.
  	 As far as variables go, for now we try only to infer variables
  	 assigned the result of #longLongAt:, but much more could be
  	 done here."
  
  	"Iterate over all methods, inferring #void return types, until we reach a fixed point."
+ 	| allMethods |
- 	| firstTime allMethods |
- 	firstTime := true.
  	allMethods := apiMethods
  					ifNil: [methods]
  					ifNotNil: [(Set withAll: methods)
  								addAll: apiMethods;
  								yourself].
+ 	"Make an initial pass to assign the return types of all simple methods that return constants,
+ 	 or those that have explicit return types."						
- 	"Make an initial pass to assign the return types of all simple methods that return constants."						
  	allMethods do:
  		[:m|
+ 		m removeFinalSelfReturnIn: self. "must preceed recordDeclarationsIn: because it may set returnType"
+ 		m recordDeclarationsIn: self.
+ 		(m returnType isNil
+ 		 and: [m isReturnConstant]) ifTrue:
- 		m isReturnConstant ifTrue:
  			[m inferReturnTypeIn: self]].
+ 
+ 	"now iterate until we reach a fixed point"
  	[| changedReturnType |
  	 changedReturnType := false.
  	 allMethods do:
  		[:m|
- 		 firstTime ifTrue:
- 			[m removeFinalSelfReturnIn: self. "must preceed recordDeclarationsIn: because it may set returnType"
- 			 m recordDeclarationsIn: self].
  		 m inferTypesForImplicitlyTypedVariablesIn: self.
  		 (m inferReturnTypeIn: self) ifTrue:
  			[changedReturnType := true]].
- 	 firstTime := false.
  	 changedReturnType] whileTrue.
  
  	"Type all as-yet-untyped methods as the default"
  	methods do:
  		[:m|
  		m returnType ifNil:
+ 			[m returnType: (self implicitReturnTypeFor: m selector)]].
+ 
+ 	"Make a final pass to type anything assigned from the default type"
+ 	allMethods do:
+ 		[:m|
+ 		 m inferTypesForImplicitlyTypedVariablesIn: self]!
- 			[m returnType: (self implicitReturnTypeFor: m selector)]]!

Item was changed:
  ----- Method: CCodeGenerator>>initialize (in category 'public') -----
  initialize
  	translationDict := Dictionary new.
  	inlineList := Array new.
  	constants := Dictionary new: 100.
  	variables := Set new: 100.
  	variableDeclarations := Dictionary new: 100.
  	methods := Dictionary new: 500.
  	kernelReturnTypes := self computeKernelReturnTypes.
  	macros := Dictionary new.
  	self initializeCTranslationDictionary.
  	headerFiles := OrderedCollection new.
  	globalVariableUsage := Dictionary new.
  	useSymbolicConstants := true.
  	generateDeadCode := true.
  	scopeStack := OrderedCollection new.
  	logger := (ProvideAnswerNotification new tag: #logger; signal) ifNil: [Transcript].
  	pools := IdentitySet new.
  	selectorTranslations := IdentityDictionary new.
+ 	suppressAsmLabels := false.
+ 	breakSrcInlineSelectors := IdentitySet new.
+ 	breakDestInlineSelectors := IdentitySet new!
- 	suppressAsmLabels := false!

Item was changed:
  ----- Method: CCodeGenerator>>maybeBreakForInlineOf:in: (in category 'inlining') -----
  maybeBreakForInlineOf: aNode in: aTMethod
  	"convenient for debugging..."
  	(aNode isSend
+ 	and: [breakSrcInlineSelectors size + breakDestInlineSelectors size > 0
+ 	and: [(breakSrcInlineSelectors isEmpty or: [breakSrcInlineSelectors includes: aNode selector])
+ 	and: [(breakDestInlineSelectors isEmpty or: [(breakDestInlineSelectors includes: aTMethod selector)])
+ 	and: [breakOnInline ~~ false]]]]) ifTrue:
- 	and: [(breakSrcInlineSelector notNil or: [breakDestInlineSelector notNil])
- 	and: [(breakSrcInlineSelector ifNil: [true] ifNotNil: [:srcSel| srcSel = aNode selector])
- 	and: [breakDestInlineSelector ifNil: [true] ifNotNil: [:dstSel| dstSel = aTMethod selector
- 	and: [breakOnInline ~~ false]]]]]) ifTrue:
  		[aTMethod halt: aTMethod selector, ' ', aNode selector]!

Item was changed:
  ----- Method: CCodeGenerator>>maybeBreakForTestToInline:in: (in category 'inlining') -----
+ maybeBreakForTestToInline: aNodeOrSelector in: aTMethod
- maybeBreakForTestToInline: aNode in: aTMethod
  	"convenient for debugging..."
+ 	| selector |
+ 	selector := aNodeOrSelector isSymbol
+ 					ifTrue: [aNodeOrSelector]
+ 					ifFalse:
+ 						[aNodeOrSelector isSend
+ 							ifTrue: [aNodeOrSelector selector]
+ 							ifFalse: [^self]].
+ 	(breakSrcInlineSelectors size + breakDestInlineSelectors size > 0
+ 	and: [(breakSrcInlineSelectors isEmpty or: [breakSrcInlineSelectors includes: selector])
+ 	and: [(breakDestInlineSelectors isEmpty or: [(breakDestInlineSelectors includes: aTMethod selector)])
+ 	and: [breakOnInline ~~ true]]]) ifTrue:
+ 		[aTMethod halt: aTMethod selector, ' ', selector]!
- 	(aNode isSend
- 	and: [(breakSrcInlineSelector notNil or: [breakDestInlineSelector notNil])
- 	and: [(breakSrcInlineSelector ifNil: [true] ifNotNil: [:srcSel| srcSel = aNode selector])
- 	and: [(breakDestInlineSelector ifNil: [true] ifNotNil: [:dstSel| dstSel = aTMethod selector])
- 	and: [breakOnInline ~~ true]]]]) ifTrue:
- 		[aTMethod halt: aTMethod selector, ' ', aNode selector]!

Item was changed:
  ----- Method: CCodeGenerator>>nodeToCast:to: (in category 'inlining') -----
  nodeToCast: exprNode to: cType
  	^TSendNode new
+ 		setSelector: #cCoerceSimple:to:
- 		setSelector: #cCoerceSimple:
  		receiver: (TVariableNode new setName: 'self')
  		arguments: { exprNode. TConstantNode new setValue: cType }
  		isBuiltInOp: true!

Item was changed:
  ----- Method: CCodeGenerator>>removeMethodForSelector: (in category 'utilities') -----
  removeMethodForSelector: aSelector
  	"Remove the given method from the code base"
+ 	((breakSrcInlineSelectors includes: aSelector)
+ 	 or: [(breakDestInlineSelectors includes: aSelector)]) ifTrue:
- 	(breakSrcInlineSelector == aSelector
- 	 or: [breakDestInlineSelector == aSelector]) ifTrue:
  		[self halt].
  	methods removeKey:  aSelector ifAbsent: []!

Item was changed:
  ----- Method: CCodeGenerator>>returnTypeForSend:in: (in category 'type inference') -----
  returnTypeForSend: sendNode in: aTMethod
  	"Answer the return type for a send.  Absent sends default to #sqInt.
  	 The bitwise operators answer unsigned versions of their argument types, at least in gcc
  	 although this author can't find that in the C99 spec.  If you can find this, please let me know."
  	| sel |
  	^(self anyMethodNamed: (sel := sendNode selector))
  		ifNil: [kernelReturnTypes
  				at: sel
  				ifAbsent:
  					[^sel
  						caseOf: {
  						[#+]					->	[self typeForArithmetic: sendNode in: aTMethod].
  						[#-]						->	[self typeForArithmetic: sendNode in: aTMethod].
  						[#*]					->	[self typeForArithmetic: sendNode in: aTMethod].
  						[#/]						->	[self typeForArithmetic: sendNode in: aTMethod].
  						[#addressOf:]			->	[(self typeFor: sendNode receiver in: aTMethod)
  														ifNil: [#sqInt]
  														ifNotNil: [:type| type, (type last isLetter ifTrue: [' *'] ifFalse: ['*'])]].
  						[#at:]					->	[self typeForDereference: sendNode in: aTMethod].
  						[#bitAnd:]				->	[self unsignedTypeForBitwiseSend: sendNode in: aTMethod].
  						[#bitOr:]				->	[self unsignedTypeForBitwiseSend: sendNode in: aTMethod].
  						[#bitXor:]				->	[self unsignedTypeForBitwiseSend: sendNode in: aTMethod].
  						[#asFloat]				->	[#double].
+ 						[#atan]					->	[#double].
+ 						[#exp]					->	[#double].
+ 						[#log]					->	[#double].
+ 						[#sin]					->	[#double].
+ 						[#sqrt]					->	[#double].
  						[#asLong]				->	[#long].
  						[#asUnsignedInteger]	->	[#usqInt].
  						[#asUnsignedLong]		->	[#'unsigned long'].
  						[#asVoidPointer]		->	[#'void *'].
  						[#signedIntToLong]		->	[#usqInt]. "c.f. generateSignedIntToLong:on:indent:"
  						[#signedIntToShort]	->	[#usqInt]. "c.f. generateSignedIntToShort:on:indent:"
  						[#cCoerce:to:]			->	[sendNode args last value].
+ 						[#cCoerceSimple:to:]	->	[sendNode args last value].
+ 						[#ifTrue:ifFalse:]		->	[self typeForConditional: sendNode in: aTMethod].
+ 						[#ifFalse:ifTrue:]		->	[self typeForConditional: sendNode in: aTMethod].
+ 						[#ifTrue:]				->	[self typeForConditional: sendNode in: aTMethod].
+ 						[#ifFalse:]				->	[self typeForConditional: sendNode in: aTMethod] }
- 						[#cCoerceSimple:to:]	->	[sendNode args last value] }
  						otherwise: [#sqInt]]]
  		ifNotNil:
  			[:m|
  			 m returnType ifNotNil:
  				[:type|
  				 self baseTypeForType: type]]!

Item was changed:
  ----- Method: CCodeGenerator>>testInliningFor:as: (in category 'utilities') -----
  testInliningFor: selector as: inlineFlagOrSymbol
  	"Test inlining for the method with the given selector.
+ 	 Do all inlining first (cuz that's how the algorithm works).
- 	 Do all inlining first (cuz that's how the algorithm works.
  	 Then try and inline into a copy of the method.  This isn't
  	 exactly what happens in the real deal but is close enough."
  	| meth |
+ 	((breakDestInlineSelectors includes:  selector)
+ 	 or: [(breakSrcInlineSelectors includes:  selector)]) ifTrue:
- 	(breakDestInlineSelector = selector
- 	or: [breakSrcInlineSelector = selector]) ifTrue:
  		[self halt].
  	meth := (self methodNamed: selector) copy.
  	self doBasicInlining: inlineFlagOrSymbol.
  	self halt.
  	meth tryToInlineMethodsIn: self!

Item was added:
+ ----- Method: CCodeGenerator>>typeForConditional:in: (in category 'type inference') -----
+ typeForConditional: sendNode in: aTMethod
+ 	"Answer the return type for a conditional, ifTrue:ifFalse: et al"
+ 	| firstType secondType |
+ 	firstType := self typeFor: sendNode args first statements last in: aTMethod.
+ 	sendNode selector numArgs = 1 ifTrue:
+ 		[^firstType].
+ 	secondType := self typeFor: sendNode args second statements last in: aTMethod.
+ 	((firstType notNil and: [(self isIntegralCType: firstType) or: [self isFloatingPointCType: firstType]])
+ 	 and: [secondType notNil and: [(self isIntegralCType: secondType) or: [self isFloatingPointCType: secondType]]]) ifTrue:
+ 		[^self promoteArithmeticTypes: firstType and: secondType].
+ 	^firstType ifNil: [secondType]!

Item was changed:
  ----- Method: CoInterpreter class>>initializePrimitiveTable (in category 'initialization') -----
  initializePrimitiveTable
  	super initializePrimitiveTable.
+ 	#(216 253) do:
+ 		[:pidx| self assert: (PrimitiveTable at: pidx + 1) = #primitiveFail].
- 	self assert: (PrimitiveTable at: 253 + 1) = #primitiveFail.
- 	PrimitiveTable at: 253 + 1 put: #primitiveCollectCogCodeConstituents.
  	self assert: (PrimitiveTable at: 215 + 1) = #primitiveFlushCacheByMethod.
+ 	PrimitiveTable
+ 		at: 253 + 1 put: #primitiveCollectCogCodeConstituents;
+ 		at: 215 + 1 put: #primitiveVoidVMStateForMethod;
+ 		at: 216 + 1 primitiveMethodXray!
- 	PrimitiveTable at: 215 + 1 put: #primitiveVoidVMStateForMethod!

Item was changed:
  ----- Method: CoInterpreterPrimitives>>primitiveMethodXray (in category 'indexing primitives') -----
  primitiveMethodXray
  	"Lift the veil from a method and answer an integer describing the interior state
  	 of its machine code.
  	 Used for e.g. VM tests so they can verify they're testing what they think they're testing.
  	 0 implies a vanilla method.
+ 	 Bit 0 = method might be compiled to machine code
+ 	 Bit 1 = method is currently compiled to machine code
+ 	 Bit 2 = is compiled frameless.
+ 	 Bit 3 = method refers to young object.
+ 	 Bit 4 = method too big to be jitted (more than 64k of code, or needs more than 1.5Mb of stack space to compile)
+ 	 Bit 5 = method contains unknown/unjittable bytecode
+ 	 Bit 7 = method should not be jitted because it contains a primitive not to be called from machine code (unused)"
- 	 Bit 0 = method is currently compiled to machine code
- 	 Bit 1 = is compiled frameless.
- 	 Bit 2 = method refers to young object"
  	<export: true>
+ 	| alreadyCogged flags cogMethod |
- 	| flags cogMethod |
  	<var: #cogMethod type: #'CogMethod *'>
+ 	(self methodWithHeaderShouldBeCogged: (objectMemory methodHeaderOf: self stackTop))
- 	(self maybeMethodHasCogMethod: self stackTop)
  		ifTrue:
+ 			[alreadyCogged := self maybeMethodHasCogMethod: self stackTop.
+ 			 flags := 1.
+ 			 alreadyCogged ifFalse:
+ 				[cogMethod := cogit cog: self stackTop selector: objectMemory nilObject.
+ 				 (cogMethod = nil
+ 				  and: [cogCompiledCodeCompactionCalledFor]) ifTrue:
+ 					[self commenceCogCompiledCodeCompaction.
+ 					 cogMethod := cogit cog: self stackTop selector: objectMemory nilObject].
+ 			 cogMethod asInteger
+ 				caseOf: {
+ 					[MethodTooBig] -> [flags := 1 + 16].
+ 					[EncounteredUnknownBytecode] -> [1 + 32].
+ 					[ShouldNotJIT] -> [1 + 64] }
+ 				otherwise: [self deny: (cogMethod asInteger between: MaxNegativeErrorCode and: NotFullyInitialized)]].
+ 			 (flags = 1
+ 			  and: [self maybeMethodHasCogMethod: self stackTop]) ifTrue:
+ 				[cogMethod := self cogMethodOf: self stackTop.
+ 				 flags := cogMethod stackCheckOffset = 0 ifTrue: [7] ifFalse: [3].
+ 				 cogMethod cmRefersToYoung ifTrue:
+ 					[flags := flags + 8].
+ 				 alreadyCogged ifFalse:
+ 					[cogit freeMethod: cogMethod]]]
- 			[cogMethod := self cogMethodOf: self stackTop.
- 			 flags := cogMethod stackCheckOffset = 0 ifTrue: [3] ifFalse: [1].
- 			 cogMethod cmRefersToYoung ifTrue:
- 				[flags := flags + 4]]
  		ifFalse: [flags := 0].
  	self pop: 1 thenPush: (objectMemory integerObjectOf: flags)!

Item was changed:
  SharedPool subclass: #CogMethodConstants
  	instanceVariableNames: ''
+ 	classVariableNames: 'CMBlock CMClosedPIC CMFree CMMaxUsageCount CMMethod CMOpenPIC CheckAllocationFillerAfterPrimCall EncounteredUnknownBytecode InsufficientCodeSpace MaxLiteralCountForCompile MaxMethodSize MaxNegativeErrorCode MaxNumArgs MaxStackCheckOffset MethodTooBig NotFullyInitialized PrimCallCollectsProfileSamples PrimCallDoNotJIT PrimCallMayCallBack PrimCallNeedsNewMethod PrimCallNeedsPrimitiveFunction ShouldNotJIT UnimplementedPrimitive YoungSelectorInPIC'
- 	classVariableNames: 'CMBlock CMClosedPIC CMFree CMMaxUsageCount CMMethod CMOpenPIC CheckAllocationFillerAfterPrimCall MaxLiteralCountForCompile MaxMethodSize MaxNumArgs MaxStackCheckOffset PrimCallCollectsProfileSamples PrimCallDoNotJIT PrimCallMayCallBack PrimCallNeedsNewMethod PrimCallNeedsPrimitiveFunction'
  	poolDictionaries: ''
  	category: 'VMMaker-JIT'!

Item was changed:
  ----- Method: CogObjectRepresentationForSqueakV3>>genGetClassTagOf:into:scratchReg: (in category 'compile abstract instructions') -----
  genGetClassTagOf: instReg into: destReg scratchReg: scratchReg
  	"Compatibility with SpurObjectRepresentation/SpurMemoryManager."
  	| entryLabel |
  	<var: #entryLabel type: #'AbstractInstruction *'>
  	cogit AlignmentNops: (objectMemory wordSize max: 8).
  	entryLabel := cogit Label.
+ 	(self genGetClassObjectOf: instReg into: destReg scratchReg: scratchReg instRegIsReceiver: nil) ~= 0 ifTrue:
+ 		[self error: 'internal error'].
- 	self genGetClassObjectOf: instReg into: destReg scratchReg: scratchReg instRegIsReceiver: nil.
  	^entryLabel!

Item was added:
+ ----- Method: CogVMSimulator>>ioMousePoint (in category 'I/O primitives') -----
+ ioMousePoint
+ 	| relPt |
+ 	^displayForm == nil
+ 		ifTrue: [99 < 16 + 66]
+ 		ifFalse: [relPt := Sensor cursorPoint - self displayLocation.
+ 				relPt x << 16 + relPt y]!

Item was removed:
- ----- Method: CogVMSimulator>>primitiveMousePoint (in category 'I/O primitives') -----
- primitiveMousePoint
- 
- 	| relPt |
- 	self pop: 1.
- 	displayForm == nil
- 		ifTrue: [self push: (self makePointwithxValue: 99 yValue: 66)]
- 		ifFalse: [relPt := Sensor cursorPoint - self displayLocation.
- 				self push: (self makePointwithxValue: relPt x yValue: relPt y)]!

Item was changed:
  CogClass subclass: #Cogit
  	instanceVariableNames: 'coInterpreter objectMemory objectRepresentation processor threadManager methodZone methodZoneBase codeBase minValidCallAddress lastNInstructions simulatedAddresses simulatedTrampolines simulatedVariableGetters simulatedVariableSetters printRegisters printInstructions compilationTrace clickConfirm breakPC breakBlock singleStep guardPageSize traceFlags traceStores breakMethod methodObj initialPC endPC methodOrBlockNumArgs inBlock needsFrame hasYoungReferent primitiveIndex backEnd callerSavedRegMask postCompileHook primInvokeLabel methodLabel stackCheckLabel blockEntryLabel blockEntryNoContextSwitch blockNoContextSwitchOffset stackOverflowCall sendMiss missOffset entryPointMask checkedEntryAlignment uncheckedEntryAlignment cmEntryOffset entry cmNoCheckEntryOffset noCheckEntry dynSuperEntry dynSuperEntryAlignment cmDynSuperEntryOffset picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 numPICCases firstCPICCaseOffset cPICCaseSize cPICEndSize closedPICSize openPICSize fixups abstractOpcodes annotations generatorTable primitiveGeneratorTable byte0 byte1 byte2 byte3 bytecodePC bytecodeSetOffset opcodeIndex numAbstractOpcodes annotationIndex blockStarts blockCount labelCounter cStackAlignment expectedSPAlignment expectedFPAlignment codeModified maxLitIndex ceMethodAbortTrampoline cePICAbortTrampoline ceCheckForInterruptTrampoline ceCPICMissTrampoline ceReturnToInterpreterTrampoline ceBaseFrameReturnTrampoline ceSendMustBeBooleanAddTrueTrampoline ceSendMustBeBooleanAddFalseTrampoline ceCannotResumeTrampoline ceEnterCogCodePopReceiverReg ceCallCogCodePopReceiverReg ceCallCogCodePopReceiverAndClassRegs cePrimReturnEnterCogCode cePrimReturnEnterCogCodeProfiling ceNonLocalReturnTrampoline ceFetchContextInstVarTrampoline ceStoreContextInstVarTrampoline ceImplicitReceiverTrampoline ceEnclosingObjectTrampoline ceCaptureCStackPointers ceFlushICache ceCheckFeaturesFunction ceTraceLinkedSendTrampoline ceTraceBlockActivationTrampoline ceTraceStoreTrampoline ceGetSP sendTrampolines superSendTrampolines dynamicSuperSendTrampolines firstSend lastSend realCEEnterCogCodePopReceiverReg realCECallCogCodePopReceiverReg realCECallCogCodePopReceiverAndClassRegs trampolineTableIndex trampolineAddresses objectReferencesInRuntime runtimeObjectRefIndex cFramePointerInUse debugPrimCallStackOffset ceTryLockVMOwner ceUnlockVMOwner cogMethodSurrogateClass cogBlockMethodSurrogateClass extA extB numIRCs indexOfIRC theIRCs'
+ 	classVariableNames: 'AltBlockCreationBytecodeSize AltFirstSpecialSelector AltNSSendIsPCAnnotated AnnotationConstantNames AnnotationShift BlockCreationBytecodeSize Debug DisplacementMask DisplacementX2N EagerInstructionDecoration FirstAnnotation FirstSpecialSelector HasBytecodePC IsAbsPCReference IsDisplacement IsDisplacementX2N IsNSSendCall IsObjectReference IsRelativeCall IsSendCall MapEnd MaxCompiledPrimitiveIndex MaxStackAllocSize MaxUnitDisplacement MaxX2NDisplacement NSSendIsPCAnnotated NumObjRefsInRuntime NumOopsPerIRC NumSendTrampolines NumTrampolines ProcessorClass'
- 	classVariableNames: 'AltBlockCreationBytecodeSize AltFirstSpecialSelector AltNSSendIsPCAnnotated AnnotationConstantNames AnnotationShift BlockCreationBytecodeSize Debug DisplacementMask DisplacementX2N EagerInstructionDecoration EncounteredUnknownBytecode FirstAnnotation FirstSpecialSelector HasBytecodePC InsufficientCodeSpace IsAbsPCReference IsDisplacement IsDisplacementX2N IsNSSendCall IsObjectReference IsRelativeCall IsSendCall MapEnd MaxCompiledPrimitiveIndex MaxNegativeErrorCode MaxStackAllocSize MaxUnitDisplacement MaxX2NDisplacement MethodTooBig NSSendIsPCAnnotated NotFullyInitialized NumObjRefsInRuntime NumOopsPerIRC NumSendTrampolines NumTrampolines ProcessorClass ShouldNotJIT UnimplementedPrimitive YoungSelectorInPIC'
  	poolDictionaries: 'CogCompilationConstants CogMethodConstants CogRTLOpcodes VMBasicConstants VMObjectIndices VMStackFrameOffsets'
  	category: 'VMMaker-JIT'!
  Cogit class
  	instanceVariableNames: 'generatorTable primitiveTable'!
  
  !Cogit commentStamp: 'eem 2/13/2013 15:37' prior: 0!
  I am the code generator for the Cog VM.  My job is to produce machine code versions of methods for faster execution and to manage inline caches for faster send performance.  I can be tested in the current image using my class-side in-image compilation facilities.  e.g. try
  
  	StackToRegisterMappingCogit genAndDis: (Integer >> #benchFib)
  
  I have concrete subclasses that implement different levels of optimization:
  	SimpleStackBasedCogit is the simplest code generator.
  
  	StackToRegisterMappingCogit is the current production code generator  It defers pushing operands
  	to the stack until necessary and implements a register-based calling convention for low-arity sends.
  
  	StackToRegisterMappingCogit is an experimental code generator with support for counting
  	conditional branches, intended to support adaptive optimization.
  
  coInterpreter <CoInterpreterSimulator>
  	the VM's interpreter with which I cooperate
  methodZoneManager <CogMethodZoneManager>
  	the manager of the machine code zone
  objectRepresentation <CogObjectRepresentation>
  	the object used to generate object accesses
  processor <BochsIA32Alien|?>
  	the simulator that executes the IA32/x86 machine code I generate when simulating execution in Smalltalk
  simulatedTrampolines <Dictionary of Integer -> MessageSend>
  	the dictionary mapping trap jump addresses to run-time routines used to warp from simulated machine code in to the Smalltalk run-time.
  simulatedVariableGetters <Dictionary of Integer -> MessageSend>
  	the dictionary mapping trap read addresses to variables in run-time objects used to allow simulated machine code to read variables in the Smalltalk run-time.
  simulatedVariableSetters <Dictionary of Integer -> MessageSend>
  	the dictionary mapping trap write addresses to variables in run-time objects used to allow simulated machine code to write variables in the Smalltalk run-time.
  printRegisters printInstructions clickConfirm <Boolean>
  	flags controlling debug printing and code simulation
  breakPC <Integer>
  	machine code pc breakpoint
  cFramePointer cStackPointer <Integer>
  	the variables representing the C stack & frame pointers, which must change on FFI callback and return
  selectorOop <sqInt>
  	the oop of the methodObj being compiled
  methodObj <sqInt>
  	the bytecode method being compiled
  initialPC endPC <Integer>
  	the start and end pcs of the methodObj being compiled
  methodOrBlockNumArgs <Integer>
  	argument count of current method or block being compiled
  needsFrame <Boolean>
  	whether methodObj or block needs a frame to execute
  primitiveIndex <Integer>
  	primitive index of current method being compiled
  methodLabel <CogAbstractOpcode>
  	label for the method header
  blockEntryLabel <CogAbstractOpcode>
  	label for the start of the block dispatch code
  stackOverflowCall <CogAbstractOpcode>
  	label for the call of ceStackOverflow in the method prolog
  sendMissCall <CogAbstractOpcode>
  	label for the call of ceSICMiss in the method prolog
  entryOffset <Integer>
  	offset of method entry code from start (header) of method
  entry <CogAbstractOpcode>
  	label for the first instruction of the method entry code
  noCheckEntryOffset <Integer>
  	offset of the start of a method proper (after the method entry code) from start (header) of method
  noCheckEntry <CogAbstractOpcode>
  	label for the first instruction of start of a method proper
  fixups <Array of <AbstractOpcode Label | nil>>
  	the labels for forward jumps that will be fixed up when reaching the relevant bytecode.  fixup shas one element per byte in methodObj's bytecode
  abstractOpcodes <Array of <AbstractOpcode>>
  	the code generated when compiling methodObj
  byte0 byte1 byte2 byte3 <Integer>
  	individual bytes of current bytecode being compiled in methodObj
  bytecodePointer <Integer>
  	bytecode pc (same as Smalltalk) of the current bytecode being compiled
  opcodeIndex <Integer>
  	the index of the next free entry in abstractOpcodes (this code is translated into C where OrderedCollection et al do not exist)
  numAbstractOpcodes <Integer>
  	the number of elements in abstractOpcocdes
  blockStarts <Array of <BlockStart>>
  	the starts of blocks in the current method
  blockCount
  	the index into blockStarts as they are being noted, and hence eventuakly teh total number of blocks in the current method
  labelCounter <Integer>
  	a nicety for numbering labels not needed in the production system but probably not expensive enough to worry about
  ceStackOverflowTrampoline <Integer>
  ceSend0ArgsTrampoline <Integer>
  ceSend1ArgsTrampoline <Integer>
  ceSend2ArgsTrampoline <Integer>
  ceSendNArgsTrampoline <Integer>
  ceSendSuper0ArgsTrampoline <Integer>
  ceSendSuper1ArgsTrampoline <Integer>
  ceSendSuper2ArgsTrampoline <Integer>
  ceSendSuperNArgsTrampoline <Integer>
  ceSICMissTrampoline <Integer>
  ceCPICMissTrampoline <Integer>
  ceStoreCheckTrampoline <Integer>
  ceReturnToInterpreterTrampoline <Integer>
  ceBaseFrameReturnTrampoline <Integer>
  ceSendMustBeBooleanTrampoline <Integer>
  ceClosureCopyTrampoline <Integer>
  	the various trampolines (system-call-like jumps from machine code to the run-time).
  	See Cogit>>generateTrampolines for the mapping from trampoline to run-time
  	routine and then read the run-time routine for a funcitonal description.
  ceEnterCogCodePopReceiverReg <Integer>
  	the enilopmart (jump from run-time to machine-code)
  methodZoneBase <Integer>
  !
  Cogit class
  	instanceVariableNames: 'generatorTable primitiveTable'!

Item was changed:
  ----- Method: InterpreterPrimitives>>primitiveMakePoint (in category 'arithmetic integer primitives') -----
  primitiveMakePoint
+ 	<inline: false>
+ 	| rcvr pt |
- 	| rcvr argument pt |
- 	argument := self stackTop.
  	rcvr := self stackValue: 1.
+ 	((self isIntegerObject: rcvr) or: [self isFloatObject: rcvr]) ifFalse:
+ 		[^self primitiveFail].
+ 	pt := objectMemory eeInstantiateSmallClass: (objectMemory splObj: ClassPoint) numSlots: YIndex + 1.
+ 	objectMemory
+ 		storePointerUnchecked: XIndex ofObject: pt withValue: rcvr;
+ 		storePointerUnchecked: YIndex ofObject: pt withValue: self stackTop.
- 	(objectMemory isIntegerObject: rcvr)
- 		ifTrue: [(objectMemory isIntegerObject: argument)
- 				ifTrue: [pt := self makePointwithxValue: (objectMemory integerValueOf: rcvr) yValue: (objectMemory integerValueOf: argument)]
- 				ifFalse: [pt := self makePointwithxValue: (objectMemory integerValueOf: rcvr) yValue: 0.
- 					"Above may cause GC!!"
- 					objectMemory storePointer: 1 ofObject: pt withValue: (self stackValue: 0)]]
- 		ifFalse: [(self isFloatObject: rcvr)
- 				ifFalse: [^ self success: false].
- 			pt := self makePointwithxValue: 0 yValue: 0.
- 			"Above may cause GC!!"
- 			objectMemory storePointer: 0 ofObject: pt withValue: (self stackValue: 1).
- 			objectMemory storePointer: 1 ofObject: pt withValue: (self stackValue: 0)].
- 
  	self pop: 2 thenPush: pt!

Item was changed:
  ----- Method: InterpreterPrimitives>>primitiveMousePoint (in category 'I/O primitives') -----
  primitiveMousePoint
  	"Obsolete on virtually all platforms; old style input polling code.
  	Return a Point indicating current position of the mouse. Note that mouse coordinates may be negative if the mouse moves above or to the left of the top-left corner of the Smalltalk window."
  
  	| pointWord x y |
- 	self pop: 1.
  	pointWord := self ioMousePoint.
  	x := self signExtend16: ((pointWord >> 16) bitAnd: 16rFFFF).
  	y := self signExtend16: (pointWord bitAnd: 16rFFFF).
+ 	self pop: 1 thenPush: (self makePointwithxValue: x  yValue: y)!
- 	self push: (self makePointwithxValue: x  yValue: y).!

Item was changed:
  ----- Method: InterpreterPrimitives>>primitiveScreenSize (in category 'I/O primitives') -----
  primitiveScreenSize
  	"Answer a point indicating the current size of the Smalltalk window.
  	 Currently there is a limit of 65535 in each direction because the
  	 point is encoded into a single 32bit value in the image header.
  	 This might well become a problem one day"
+ 	| pointWord |
+ 	pointWord := self ioScreenSize.
+ 	self pop: 1
+ 		thenPush: (self makePointwithxValue: (pointWord >> 16 bitAnd: 65535)
+ 						yValue: (pointWord bitAnd: 65535))!
- 	self
- 		cCode:
- 			[| pointWord |
- 			 pointWord := self ioScreenSize.
- 			 self pop: 1
- 				thenPush: (self makePointwithxValue: (pointWord >> 16 bitAnd: 65535)
- 								yValue: (pointWord bitAnd: 65535))]
- 		inSmalltalk:
- 			[| size |
- 			"Default to a reasonable size for simulation, unless the window has opened,
- 			 in which case allow the screen to be as large as the simulation window"
- 			 size := (self displayView notNil and: [self savedWindowSize notNil])
- 						ifTrue: [self desiredDisplayExtent]
- 						ifFalse: [self desiredDisplayExtent min: 800 at 640].
- 			 self pop: 1 thenPush: (self makePointwithxValue: size x yValue: size y)]!

Item was changed:
  ----- Method: SpurMemoryManager>>validFreeTreeChunk:parent: (in category 'free space') -----
  validFreeTreeChunk: chunk parent: parent
+ 	<var: 'reason' type: #'const char *'>
+ 	<returnTypeC: #'const char *'>
- 	<returnTypeC: 'const char *'>
  	chunk = 0 ifTrue:
  		[^nil].
  	(self addressCouldBeOldObj: chunk) ifFalse:
  		[^'not in old space'].
  	(self bytesInObject: chunk) / self allocationUnit < self numFreeLists ifTrue:
  		[^'too small'].
  	parent ~= (self fetchPointer: self freeChunkParentIndex ofFreeChunk: chunk) ifTrue:
  		[^'bad parent'].
  	(self validFreeTreeChunk: (self fetchPointer: self freeChunkSmallerIndex ofFreeChunk: chunk) parent: chunk) ifNotNil:
  		[:reason| ^reason].
  	(self validFreeTreeChunk: (self fetchPointer: self freeChunkLargerIndex ofFreeChunk: chunk) parent: chunk) ifNotNil:
  		[:reason| ^reason].
  	^nil!

Item was changed:
  ----- Method: SpurSegmentManager>>readHeapFromImageFile:dataBytes: (in category 'snapshot') -----
  readHeapFromImageFile: f dataBytes: numBytes
  	"Read numBytes of image data from f into memory at memoryBaseForImageRead.
  	 Answer the number of bytes written.  In addition, read each segment, build up the
  	 segment info for swizzling, while eliminating the bridge objects at the end of each
  	 segment that specify the distance to and the size of the subsequent segment."
  	<var: #f type: #sqImageFile>
  	<inline: false>
  	| bytesRead totalBytesRead bridgehead bridge nextSegmentSize oldBase newBase segInfo bridgeSpan |
  	<var: 'segInfo' type: #'SpurSegmentInfo *'>
  	self allocateOrExtendSegmentInfos.
  
  	"segment sizes include the two-header-word bridge at the end of each segment."
  	numSegments := totalBytesRead := 0.
  	oldBase := 0. "N.B. still must be adjusted by oldBaseAddr."
  	newBase := manager oldSpaceStart.
  	nextSegmentSize := firstSegmentSize.
  	bridgehead := firstSegmentSize + manager oldSpaceStart - manager bridgeSize.
  	[segInfo := self addressOf: (segments at: numSegments).
  	 segInfo
  		segStart: oldBase;					"N.B. still must be adjusted by oldBaseAddr."
  		segSize: nextSegmentSize;
  		swizzle: newBase - oldBase.	"N.B. still must be adjusted by oldBaseAddr."
  	 bytesRead := self readHeapFrom: f at: newBase dataBytes: nextSegmentSize.
  	 bytesRead > 0 ifTrue:
  			[totalBytesRead := totalBytesRead + bytesRead].
  	 bytesRead ~= nextSegmentSize ifTrue:
  		[^totalBytesRead].
  	 numSegments := numSegments + 1.
  	 bridge := bridgehead + manager baseHeaderSize.
  	 bridgeSpan := (manager rawNumSlotsOf: bridgehead) = 0
  						ifTrue: [0]
  						ifFalse: [manager bytesPerOop * (manager rawOverflowSlotsOf: bridge)].
  	 oldBase := oldBase + nextSegmentSize + bridgeSpan.
  	 newBase := newBase + nextSegmentSize - manager bridgeSize.
+ 	 nextSegmentSize := (manager long64At: bridge) asUnsignedInteger.
- 	 nextSegmentSize := manager long64At: bridge.
  	 nextSegmentSize ~= 0] whileTrue:
  		[bridgehead := bridgehead - manager bridgeSize + nextSegmentSize].
  	"newBase should point just past the last bridge. all others should have been eliminated."
  	self assert: newBase - manager oldSpaceStart
  				= (totalBytesRead - (numSegments * manager bridgeSize)).
  	"set freeOldSpaceStart now for adjustAllOopsBy:"
  	manager setFreeOldSpaceStart: newBase.
  	"we're done. nil firstSegmentSize for a subsequent snapshot."
  	firstSegmentSize := nil.
  	^totalBytesRead!

Item was changed:
  ----- Method: StackInterpreter>>dynamicSuperSendBytecode (in category 'send bytecodes') -----
  dynamicSuperSendBytecode
  "Send a message to self, starting lookup in the superclass of the method application of the currently executing method's mixin."
  "Assume: messageSelector and argumentCount have been set, and that the receiver and arguments have been pushed onto the stack," 
  "WE WANT THE RECEIVER PUSHED IMPLICITLY, BUT IT IS NOT - SO FAR"
  "Note: This method is inlined into the interpreter dispatch loop."
  	| rcvr mClassMixin mixinApplication |
  	<inline: true>
  	argumentCount := self fetchByte.
  	messageSelector := self literal: self fetchByte.
  	"To maintain the invariant that all receivers are unforwarded we need an explicit
  	 read barrier in the super send cases."
  	self ensureReceiverUnforwarded.
  	rcvr := self internalStackValue: argumentCount.
  	mClassMixin := self methodClassOf: method.
  	mixinApplication := self 
  							findApplicationOfTargetMixin: mClassMixin
  							startingAtBehavior: (objectMemory fetchClassOf: rcvr).
  	lkupClassTag := objectMemory classTagForClass: (self superclassOf: mixinApplication).
+ 	CheckPrivacyViolations ifTrue:
+             [isPrivateSend := true].
  	self commonSend!

Item was changed:
  ----- Method: StackInterpreter>>lookupMethodInClass: (in category 'message sending') -----
  lookupMethodInClass: class
  	| currentClass dictionary found |
  	<inline: false>
  	self assert: (self addressCouldBeClassObj: class).
  	currentClass := class.
  	[currentClass ~= objectMemory nilObject] whileTrue:
  		[dictionary := objectMemory followObjField: MethodDictionaryIndex ofObject: currentClass.
  		dictionary = objectMemory nilObject ifTrue:
  			["MethodDict pointer is nil (hopefully due a swapped out stub)
  				-- raise exception #cannotInterpret:."
  			self createActualMessageTo: class.
  			messageSelector := objectMemory splObj: SelectorCannotInterpret.
  			self sendBreakpoint: messageSelector receiver: nil.
  			^self lookupMethodInClass: (self superclassOf: currentClass)].
  		found := self lookupMethodInDictionary: dictionary.
+ 		found ifTrue:
+ 			[self maybeCheckPrivacyOfNewMethod: currentClass.
+ 			 ^currentClass].
- 		(NewspeakVM
- 		 and: [CheckPrivacyViolations
- 		 and: [isPrivateSend not
- 		 and: [messageSelector ~= (objectMemory splObj: SelectorDoesNotUnderstand)
- 		 and: [(self accessModifierOfMethod: newMethod) ~= 0]]]]) ifTrue:
- 			[self print: (self nameOfClass: currentClass); space.
- 			 self printStringOf: (messageSelector); print: ' from '.
- 			 self printStringOf: (self maybeSelectorOfMethod: method); cr].
- 		found ifTrue: [^currentClass].
  		currentClass := self superclassOf: currentClass].
  
  	"Could not find #doesNotUnderstand: -- unrecoverable error."
  	messageSelector = (objectMemory splObj: SelectorDoesNotUnderstand) ifTrue:
  		[self error: 'Recursive not understood error encountered'].
  
  	"Cound not find a normal message -- raise exception #doesNotUnderstand:"
  	self createActualMessageTo: class.
  	messageSelector := objectMemory splObj: SelectorDoesNotUnderstand.
  	self sendBreak: messageSelector + objectMemory baseHeaderSize
  		point: (objectMemory lengthOf: messageSelector)
  		receiver: nil.
  	^self lookupMethodInClass: class!

Item was added:
+ ----- Method: StackInterpreter>>maybeCheckPrivacyOfNewMethod: (in category 'message sending') -----
+ maybeCheckPrivacyOfNewMethod: currentClass
+ 	<inline: true>
+ 	(NewspeakVM
+ 	  and: [CheckPrivacyViolations
+ 	  and: [isPrivateSend not
+ 	  and: [messageSelector ~= (objectMemory splObj: SelectorDoesNotUnderstand)
+ 	  and: [(self accessModifierOfMethod: newMethod) ~= 0]]]]) ifTrue:
+ 		[self print: (self nameOfClass: currentClass); space.
+ 		 self printStringOf: (messageSelector); print: ' from '.
+ 		 self printStringOf: (self maybeSelectorOfMethod: method); cr]!

Item was added:
+ ----- Method: StackInterpreterSimulator>>ioMousePoint (in category 'I/O primitives') -----
+ ioMousePoint
+ 	| relPt |
+ 	^displayForm == nil
+ 		ifTrue: [99 < 16 + 66]
+ 		ifFalse: [relPt := Sensor cursorPoint - self displayLocation.
+ 				relPt x << 16 + relPt y]!

Item was removed:
- ----- Method: StackInterpreterSimulator>>primitiveMousePoint (in category 'I/O primitives') -----
- primitiveMousePoint
- 
- 	| relPt |
- 	self pop: 1.
- 	displayForm == nil
- 		ifTrue: [self push: (self makePointwithxValue: 99 yValue: 66)]
- 		ifFalse: [relPt := Sensor cursorPoint - self displayLocation.
- 				self push: (self makePointwithxValue: relPt x yValue: relPt y)]!

Item was changed:
  ----- Method: TMethod>>argAssignmentsFor:send:in: (in category 'inlining') -----
  argAssignmentsFor: meth send: aSendNode in: aCodeGen
  	"Return a collection of assignment nodes that assign the given argument expressions to the formal parameter variables of the given method."
  	"Optimization: If the actual parameters are either constants or local variables in the target method (the receiver), substitute them directly into the body of meth. Note that global variables cannot be subsituted because the inlined method might depend on the exact ordering of side effects to the globals."
  
  	| stmtList substitutionDict argList |
+ 	meth args size > (argList := aSendNode args) size ifTrue:
- 	stmtList := OrderedCollection new: 100.
- 	substitutionDict := Dictionary new: 100.
- 	argList := aSendNode args.
- 	
- 	meth args size > aSendNode args size ifTrue:
  		[self assert: (meth args first beginsWith: 'self_in_').
  		 argList := {aSendNode receiver}, aSendNode args].
+ 	
+ 	stmtList := OrderedCollection new: argList size.
+ 	substitutionDict := Dictionary new: 100.
  	meth args with: argList do:
  		[ :argName :exprNode |
  		(self isNode: exprNode substitutableFor: argName inMethod: meth in: aCodeGen)
  			ifTrue:
  				[substitutionDict
  					at: argName
+ 					put: (self node: exprNode
+ 							typeCompatibleWith: argName
+ 							inliningInto: meth selector
+ 							in: aCodeGen).
- 					put: (((exprNode isSend or: [exprNode isVariable])
- 						  and: [(self typeFor: argName in: aCodeGen) notNil
- 						  and: [(aCodeGen typeFor: exprNode in: self) isNil]])
- 							ifTrue: [aCodeGen nodeToCast: exprNode to: (self typeFor: argName in: aCodeGen)]
- 							ifFalse: [exprNode]).
  				 locals remove: argName]
  			ifFalse:
+ 				[stmtList addLast:
+ 					(TAssignmentNode new
+ 						setVariable: (TVariableNode new setName: argName)
+ 						expression: (self node: exprNode copy
+ 										typeCompatibleWith: argName
+ 										inliningInto: meth selector
+ 										in: aCodeGen))]].
- 				[stmtList add: (TAssignmentNode new
- 								setVariable: (TVariableNode new setName: argName)
- 								expression: exprNode copy)]].
  	meth parseTree: (meth parseTree bindVariablesIn: substitutionDict).
  	^stmtList!

Item was changed:
  ----- Method: TMethod>>inferTypesForImplicitlyTypedVariablesIn: (in category 'type inference') -----
  inferTypesForImplicitlyTypedVariablesIn: aCodeGen
  	"infer types for untyped variables from assignments and arithmetic uses.
  	 For debugging answer a Dictionary from var to the nodes that determined types
  	 This for debugging:
  		(self copy inferTypesForImplicitlyTypedVariablesIn: aCodeGen)"
  	| alreadyExplicitlyTyped effectiveNodes |
+ 	aCodeGen maybeBreakForTestToInline: selector in: self.
- 	"selector = #transformColor: ifTrue:
- 		[self halt]."
  	alreadyExplicitlyTyped := declarations keys asSet.
  	effectiveNodes := Dictionary new. "this for debugging"
  	parseTree nodesDo:
  		[:node| | type var |
  		"If there is something of the form i >= 0, then i should be signed, not unsigned."
  		(node isSend
  		 and: [(locals includes: (var := node receiver variableNameOrNil))
  		 and: [(alreadyExplicitlyTyped includes: var) not "don't be fooled by inferred unsigned types"
  		 and: [(#(<= < >= >) includes: node selector)
  		 and: [node args first isConstant
  		 and: [node args first value = 0
  		 and: [(type := self typeFor: var in: aCodeGen) notNil
  		 and: [type first == $u]]]]]]]) ifTrue:
  			[declarations at: var put: (aCodeGen signedTypeForIntegralType: type), ' ', var.
  			 effectiveNodes at: var put: { declarations at: var. node }].
  		"if an assignment to an untyped local of a known type, set the local's type to that type.
  		 Only observe known sends (methods in the current set) and typed local variables."
  		(node isAssignment
  		 and: [(locals includes: (var := node variable name))
  		 and: [(alreadyExplicitlyTyped includes: var) not "don't be fooled by previously inferred types"
  		 and: [(type := node expression isSend
  						ifTrue: [aCodeGen returnTypeForSend: node expression in: self]
  						ifFalse: [self typeFor: node expression in: aCodeGen]) notNil
  		 and: [type ~= #void]]]]) ifTrue:
  			[aCodeGen mergeTypeOf: var in: declarations with: type.
  			 effectiveNodes at: var put: { declarations at: var. node }, (effectiveNodes at: var ifAbsent: [#()])]].
  	^effectiveNodes!

Item was changed:
  ----- Method: TMethod>>inlineSend:directReturn:exitVar:in: (in category 'inlining') -----
  inlineSend: aSendNode directReturn: directReturn exitVar: exitVar in: aCodeGen
  	"Answer a collection of statements to replace the given send.  directReturn indicates
  	 that the send is the expression in a return statement, so returns can be left in the
  	 body of the inlined method. If exitVar is nil, the value returned by the send is not
  	 used; thus, returns need not assign to the output variable.
  
  	 Types are propagated to as-yet-untyped variables when inlining a send that is assigned,
  	 otherwise the assignee variable type must match the return type of the inlinee.  Return
  	 types are not propagated."
  
  	| sel meth methArgs exitLabel inlineStmts label exitType |
  	sel := aSendNode selector.
  	meth := aCodeGen methodNamed: sel.
  	methArgs := meth args.
  	"convenient for debugging..."
  	aCodeGen maybeBreakForInlineOf: aSendNode in: self.
  	(methArgs notEmpty and: [methArgs first beginsWith: 'self_in_']) ifTrue:
  		[methArgs := methArgs allButFirst].
  	methArgs size = aSendNode args size ifFalse:
  		[^nil].
- 	methArgs with: aSendNode args do:
- 		[:formal :actual|
- 		(actual isVariable
- 		and: [(aCodeGen
- 				variableOfType: (self typeFor: formal using: aCodeGen)
- 				acceptsValueOfType: (self typeFor: actual name in: aCodeGen)) not]) ifTrue:
- 			[aCodeGen logger
- 				nextPutAll:
- 					'type mismatch for formal ', formal, ' and actual ', actual name,
- 					' when inlining ', sel, ' in ', selector, '. Use a cast.';
- 				cr; flush]]. 
  	meth := meth copy.
  
  	"Propagate the return type of an inlined method"
+ 	(directReturn or: [exitVar notNil]) ifTrue:
+ 		[exitType := directReturn 
+ 						ifTrue: [returnType] 
+ 						ifFalse: [(self typeFor: exitVar in: aCodeGen) ifNil: [#sqInt]].
+ 		(exitType = #void or: [exitType = meth returnType]) ifFalse:
+ 			[meth propagateReturnIn: aCodeGen]].
- 	(directReturn or:[exitVar notNil]) ifTrue:[
- 		exitType := directReturn 
- 			ifTrue:[returnType] 
- 			ifFalse:[(self typeFor: exitVar in: aCodeGen) ifNil:[#sqInt]].
- 		(exitType = #void or:[exitType = meth returnType]) 
- 			ifFalse:[meth propagateReturnIn: aCodeGen]].
  
  	meth renameVarsForInliningInto: self except: #() in: aCodeGen.
  	meth renameLabelsForInliningInto: self.
  	self addVarsDeclarationsAndLabelsOf: meth except: #().
+ 	meth hasReturn ifTrue:
+ 		[directReturn ifFalse:
+ 			[exitLabel := self unusedLabelForInliningInto: self.
+ 			 (meth exitVar: exitVar label: exitLabel) "is label used?"
- 	meth hasReturn ifTrue:[
- 		directReturn ifFalse:[
- 			exitLabel := self unusedLabelForInliningInto: self.
- 			(meth exitVar: exitVar label: exitLabel) "is label used?"
  				ifTrue: [ labels add: exitLabel ]
  				ifFalse: [ exitLabel := nil ]]].
  	(inlineStmts := OrderedCollection new: 100)
  		add: (label := TLabeledCommentNode new setComment: 'begin ', sel);
  		addAll: (self argAssignmentsFor: meth send: aSendNode in: aCodeGen);
  		addAll: meth statements.  "method body"
  	(directReturn
  	 and: [meth endsWithReturn not]) ifTrue:
  		[inlineStmts add:
  			(TReturnNode new setExpression: (TVariableNode new setName: 'nil'))].
  	exitLabel ~= nil ifTrue:
  		[inlineStmts add:
  			(TLabeledCommentNode new setLabel:
  				exitLabel comment: 'end ', meth selector)].
  	^inlineStmts!

Item was changed:
  ----- Method: TMethod>>isNode:substitutableFor:inMethod:in: (in category 'inlining') -----
  isNode: aNode substitutableFor: argName inMethod: targetMeth in: aCodeGen
  	"Answer if the given parameter node may be substituted directly into the body of
  	 the method during inlining, instead of being bound to the actual parameter variable.
  	 We allow a constant, a local variable, or a formal parameter, or simple expressions
  	 involving only these to to be directly substituted. 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."
  
  	| madeNonTrivialCall count constantExpression usageCount |
  	aNode isConstant ifTrue: [^true].
  
  	aNode isVariable ifTrue:
  		[((locals includes: aNode name)
  		 or: [(args includes: aNode name)
  		 or: [#('self' 'true' 'false' 'nil') includes: aNode name]]) ifTrue: [^true].
  		"We can substitute any variable provided it is only read in the method being inlined,
  		 and if it is not read after any non-trivial call (which may update the variable)."
  		madeNonTrivialCall := false.
  		(targetMeth isComplete
  		 and: [targetMeth parseTree
  				noneSatisfy:
  					[:node|
  					 (node isSend
  					  and: [(aCodeGen isBuiltinSelector: node selector) not]) ifTrue:
  						[madeNonTrivialCall := true].
  					 (madeNonTrivialCall and: [node isVariable and: [node name = argName]])
  					 or: [node isAssignment
  						  and: [node variable name = argName]]]
  				unless:
  					[:node|
  					node isSend and: [aCodeGen isAssertSelector: node selector]]]) ifTrue:
  			[^true].
  		^targetMeth maySubstituteGlobal: aNode name in: aCodeGen].
  
+ 	"don't much up asserts with complex expansions"
+ 	(targetMeth usesVariableUninlinably: argName in: aCodeGen) ifTrue:
+ 		[^false].
+ 
  	"For now allow literal blocks to be substituted.  They better be accessed only
  	 with value[:value:*] messages though!!"
  	aNode isStmtList ifTrue: [^true].
  
  	"Don't inline expressions unless type-compatible,"
  	aNode isSend ifTrue:
  		[(aCodeGen
  				isActualType: (aCodeGen returnTypeForSend: aNode in: self)
  				compatibleWithFormalType: (self typeFor: argName in: aCodeGen)) ifFalse:
  			[^false]].
  
  	count := 0.
  	constantExpression := true.
  	"scan expression tree; must contain only constants, builtin ops, and inlineable vars"
  	aNode nodesDo:
  		[:node|
  		node isConstant
  			ifTrue: [] ifFalse:
  		[node isSend
  			ifTrue:
  				[((VMBasicConstants mostBasicConstantSelectors includes: node selector)
  				  or: [node isBuiltinOperator]) ifFalse: [^false].
  				 count := count + 1] ifFalse:
  		[node isVariable ifTrue:
  			[(aCodeGen isNonArgumentImplicitReceiverVariableName: node name) ifFalse:
  				[constantExpression := false.
  				((locals includes: node name)
  				 or: [(args includes: node name)
  				 or: [(#('self' 'true' 'false' 'nil') includes: node name)
  				 or: [targetMeth maySubstituteGlobal: node name in: aCodeGen]]]) ifFalse: [^false]]] ifFalse:
  		[^false]]]].
  	"inline constant expressions"
  	constantExpression ifNil: [^true].
  
  	"scan target to find usage count"
  	usageCount := 0.
  	targetMeth parseTree nodesDo:
  		[:node|
  		(node isVariable and: [node name = argName]) ifTrue:
  			[usageCount := usageCount + 1]].
  	"(usageCount > 1 and: [count <= usageCount]) ifTrue:
  		[[UsageCounts := Dictionary new.
  		  self removeClassVarName: #UsageCounts].
  		 (UsageCounts at: usageCount ifAbsentPut: [Set new]) add: ({targetMeth. argName. aNode})]."
  	"Now only inline expressions if they are used only once or are simple
  	 w.r.t. the usage count, and the usage count is not large; a heuristic that seems to work well enough."
  	^usageCount = 1 or: [usageCount <= 7 and: [count <= usageCount]]!

Item was added:
+ ----- Method: TMethod>>node:typeCompatibleWith:inliningInto:in: (in category 'inlining') -----
+ node: exprNode typeCompatibleWith: argName inliningInto: inlineSelector in: aCodeGen
+ 	"Answer either exprNode or, iff argName is typed and exprNode is untyped, a cast of exprNode to the type of argName."
+ 	| formalType actualType |
+ 	^((exprNode isSend or: [exprNode isVariable])
+ 	   and: [(formalType := self typeFor: argName in: aCodeGen) notNil
+ 	   and: [(actualType := aCodeGen typeFor: exprNode in: self) isNil
+ 			or: [(aCodeGen variableOfType: formalType acceptsValueOfType: actualType) not]]])
+ 		ifTrue: [aCodeGen nodeToCast: exprNode to: formalType]
+ 		ifFalse:
+ 			[((exprNode isSend or: [exprNode isVariable])
+ 			  and: [(aCodeGen
+ 					variableOfType: (aCodeGen typeFor: exprNode in: self)
+ 					acceptsValueOfType: (self typeFor: argName in: aCodeGen)) not]) ifTrue:
+ 				[aCodeGen logger
+ 					nextPutAll:
+ 						'type mismatch for formal ', argName, ' and actual ', exprNode asString,
+ 						' when inlining ', inlineSelector, ' in ', selector, '. Use a cast.';
+ 					cr; flush]. 
+ 			exprNode]!

Item was changed:
  ----- Method: TMethod>>returnType: (in category 'accessing') -----
  returnType: aString
+ 	"Set the type of the values returned by this method.
+ 	 This string will be used in the C declaration of this function.
+ 	 If the type exists as a symbol, use that."
- 	"Set the type of the values returned by this method. This string will be used in the C declaration of this function."
  
+ 	returnType := (Symbol findInterned: aString) ifNil: [aString]!
- 	returnType := aString!

Item was changed:
  ----- Method: TMethod>>transformToStructClassMethodFor: (in category 'transformations') -----
  transformToStructClassMethodFor: aCCodeGenerator
  	"Transform this method so that it can be used on an instance of a struct class (VMStructType subclass).
  	 Convert inst var refs into field dereferences of self.  Add selfSelector as the first argument with the
  	 right struct type. As a complete hack to avoid breaking the inlinert don't use 'self' as the name for self
  	 as this causes serious type redefinitions ``somewhere'' in the inliner."
  	| replacements selfNode typeForSelf |
  	self isStructAccessor ifTrue:
+ 		[^self returnType: (definingClass returnTypeForAccessor: selector)].
- 		[^returnType := definingClass returnTypeForAccessor: selector].
  	replacements := IdentityDictionary new.
  	selfNode := TVariableNode new setName: 'self_in_', (aCCodeGenerator cFunctionNameFor: selector).
  	args do:
  		[:var|
  		(definingClass isAccessor: var) ifTrue:
  			[self error: 'In ', definingClass name, '>>', selector, ' ', var, ' arg shadows struct field and will break during translation!!']].
  	parseTree nodesDo:
  		[:node|
  		node isVariable ifTrue:
  			[node name = 'self' ifTrue:
  				[replacements at: node put: selfNode copy].
  			 (definingClass isAccessor: node name) ifTrue:
  				[replacements
  					at: node
  					put: (TSendNode new
  							setSelector: node name asSymbol
  							receiver: selfNode
  							arguments: #())]]].
  	replacements notEmpty ifTrue:
  		[parseTree := parseTree replaceNodesIn: replacements].
  	typeForSelf := self typeForSelf.
  	self assert: (typeForSelf notNil and: [typeForSelf ~~ #implicit]).
  	self declarationAt: (args addFirst: selfNode name)
  		put: (declarations removeKey: 'self'), '_in_', (aCCodeGenerator cFunctionNameFor: selector)!

Item was removed:
- ----- Method: TMethod>>typeFor:using: (in category 'utilities') -----
- typeFor: aVariable using: aCCodeGen
- 	"Answer the type for aVariable.  Answer nil for variables without types.  nil for
- 	 typelessness is required by the type propagation logic in inlineSend:directReturn:exitVar:in:."
- 	^aCCodeGen extractTypeFor: aVariable fromDeclaration: (declarations at: aVariable asString ifAbsent: [^nil])!

Item was added:
+ ----- Method: TMethod>>usesVariableUninlinably:in: (in category 'inlining') -----
+ usesVariableUninlinably: argName in: aCodeGen
+ 	^parseTree anySatisfy:
+ 		[:node|
+ 		node isSend
+ 		and: [(aCodeGen isAssertSelector: node selector)
+ 		and: [node args anySatisfy:
+ 				[:argNode|
+ 				 argNode anySatisfy:
+ 					[:subNode|
+ 					 subNode isVariable and: [subNode name = argName]]]]]]!



More information about the Vm-dev mailing list