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

commits at source.squeak.org commits at source.squeak.org
Sun Aug 1 22:04:32 UTC 2021


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

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

Name: VMMaker.oscog-eem.3010
Author: eem
Time: 1 August 2021, 3:04:23.430035 pm
UUID: db70c36d-4f15-4c05-b173-491815f4b8a7
Ancestors: VMMaker.oscog-eem.3009

Support a FastCPrimitiveAlignForFloatsFlag to accompany FastCPrimitiveFlag. Use the facility in primitiveFFI{Double,Float}At[Put]. On win32 clang emits instructions that insist on a more aligned stack (in this commit I'm hoping 16 byte alignment is sufficient; it may prove to be more).

Hence identify fast machine code primitives with the FastCPrimitiveUseCABIFlag flag, refactor compileOnStackExternalPrimitive: to compileOnStackExternalPrimitive:flags:, extend the set of PrimCall flags, etc.

Remember to set the native stack pointer for a FastCPrimitive on ARMv8.  This is almost certainly the cause of current Cog crashes on ARMv8.

Simulation:
Make addressOf:put: support multiple byte writes.  It will call the put block once for every byte, but the last one will at least include all the data written.  This allows primitiveFFI{Double,Float}At[Put] to simulate correctly.

In-Image Compilation: take baby steps towards supporting in-image compilation of FastCPrimitive methods.  The issue is how to get functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: to work with the in-image framework.  This probably needs CurrentImageCoInterpreterFacade to have a CurrentImageObjectMemoryFacade to intercept memory accesses.

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

Item was added:
+ ----- Method: CArray>>asVoidPointer (in category 'converting') -----
+ asVoidPointer
+ 	^self!

Item was changed:
  ----- Method: CArray>>coerceTo:sim: (in category 'converting') -----
  coerceTo: cTypeString sim: interpreterSimulator
  
  	^cTypeString caseOf: {
  		['int']				-> [self ptrAddress].
  		['float *']			-> [self asSingleFloatAccessor].
  		['double *']			-> [self asDoubleFloatAccessor].
  		['sqInt *']			-> [self shallowCopy unitSize: interpreter bytesPerOop; beSigned].
  		['unsigned int *']	-> [self shallowCopy unitSize: 4; beUnsigned].
  		['int *']				-> [self shallowCopy unitSize: 4; beSigned].
  		['unsigned short *']	-> [self shallowCopy unitSize: 2; beUnsigned].
  		['short *']			-> [self shallowCopy unitSize: 2; beSigned].
  		['unsigned char *']	-> [self shallowCopy unitSize: 1; beUnsigned].
  		['char *']			-> [self shallowCopy unitSize: 1; beUnsigned]. "C is ambivalent on the issue; sigh... SocketPlugin assumes unsigned"
  		['unsigned']		-> [self ptrAddress].
  		['sqInt']				-> [self ptrAddress].
  		['usqInt']			-> [self ptrAddress].
+ 		['sqIntptr_t']		-> [self] "this is used, in e.g. ffiAddressOf:startingAt:size: to coerce a pointer to an integer; it does not imply a real conversion. it is just to be able to pass the pointer as an integer" }!
- 		['sqIntptr_t']		-> [self shallowCopy unitSize: interpreter bytesPerOop; yourself] }!

Item was changed:
  ----- Method: CCodeGenerator>>generateBitInvert:on:indent: (in category 'C translation') -----
  generateBitInvert: msgNode on: aStream indent: level
  	"Generate the C code for this message onto the given stream.
  	 If the selector is bitInvert32 then cast to unsigned int to ensure
  	 a 32-bit value on 64-bit platforms."
  
  	| castToUnsignedInt castToUnsignedLong castToUnsignedLongLong |
+ 	msgNode selector == #bitInvert ifTrue: "quick hack; can do the full job (of coercing -1 to the right type) later..."
+ 		[aStream nextPutAll: '(-1 - ('.
+ 		 self emitCExpression: msgNode receiver on: aStream.
+ 		 aStream next: 2 put: $).
+ 		 ^self].
  	(castToUnsignedInt := msgNode selector = #bitInvert32) ifTrue:
  		[aStream nextPutAll: '(unsigned int)'].
  	aStream nextPut: $~.
  	(castToUnsignedLong := msgNode selector = #bitInvert64 and: [vmClass notNil and: [vmClass objectMemoryClass wordSize = 8]]) ifTrue:
  		[aStream nextPutAll: '(usqIntptr_t)'].
  	(castToUnsignedLongLong := msgNode selector = #bitInvert64 and: [vmClass isNil or: [vmClass objectMemoryClass wordSize = 4]]) ifTrue:
  		[aStream nextPutAll: '(unsigned long long)'].
  	self assert: castToUnsignedInt asBit + castToUnsignedLong asBit + castToUnsignedLongLong asBit <= 1. "We should only do a single cast"
  	self emitCExpression: msgNode receiver on: aStream!

Item was changed:
  ----- Method: CCodeGenerator>>initializeCTranslationDictionary (in category 'C translation support') -----
  initializeCTranslationDictionary 
  	"Initialize the dictionary mapping message names to actions for C code generation."
  
  	| pairs |
  	
  	translationDict := Dictionary new: 200.
  	pairs := #(
  	#&				#generateAnd:on:indent:
  	#|				#generateOr:on:indent:
  	#abs			#generateAbs:on:indent:
  	#and:			#generateSequentialAnd:on:indent:
  	#or:			#generateSequentialOr:on:indent:
  	#not			#generateNot:on:indent:
  
  	#+				#generatePlus:on:indent:
  	#-				#generateMinus:on:indent:
  	#negated		#generateNegated:on:indent:
  	#*				#generateTimes:on:indent:
  	#/				#generateDivide:on:indent:
  	#//				#generateDivide:on:indent:
  	#\\				#generateModulo:on:indent:
  	#<<			#generateShiftLeft:on:indent:
  	#>>			#generateShiftRight:on:indent:
  	#>>>			#generateSignedShiftRight:on:indent:
  	#,				#generateComma:on:indent:
  	#min:			#generateMin:on:indent:
  	#max:			#generateMax:on:indent:
  	#between:and:	#generateBetweenAnd:on:indent:
  
  	#bitAnd:			#generateBitAnd:on:indent:
  	#bitOr:				#generateBitOr:on:indent:
  	#bitXor:			#generateBitXor:on:indent:
  	#bitShift:			#generateBitShift:on:indent:
  	#signedBitShift:	#generateSignedBitShift:on:indent:
+ 	#bitInvert			#generateBitInvert:on:indent:
  	#bitInvert32		#generateBitInvert:on:indent:
  	#bitInvert64		#generateBitInvert:on:indent:
  	#bitClear:			#generateBitClear:on:indent:
  	#truncateTo:		#generateTruncateTo:on:indent:
  	#rounded			#generateRounded:on:indent:
  	#even				#generateEven:on:indent:
  	#odd				#generateOdd:on:indent:
  
  	#byteSwap32		#generateByteSwap32:on:indent:
  	#byteSwap64		#generateByteSwap64:on:indent:
  	#byteSwapped32IfBigEndian:	generateByteSwap32IfBigEndian:on:indent:
  	#byteSwapped64IfBigEndian:	generateByteSwap64IfBigEndian:on:indent:
  	
  	#<				#generateLessThan:on:indent:
  	#<=			#generateLessThanOrEqual:on:indent:
  	#=				#generateEqual:on:indent:
  	#>				#generateGreaterThan:on:indent:
  	#>=			#generateGreaterThanOrEqual:on:indent:
  	#~=			#generateNotEqual:on:indent:
  	#==			#generateEqual:on:indent:
  	#~~			#generateNotEqual:on:indent:
  	#isNil			#generateIsNil:on:indent:
  	#notNil			#generateNotNil:on:indent:
  
  	#whileTrue: 	#generateWhileTrue:on:indent:
  	#whileFalse:	#generateWhileFalse:on:indent:
  	#whileTrue 	#generateDoWhileTrue:on:indent:
  	#whileFalse		#generateDoWhileFalse:on:indent:
  	#to:do:			#generateToDo:on:indent:
  	#to:by:do:		#generateToByDo:on:indent:
  	#repeat 		#generateRepeat:on:indent:
  	#timesRepeat:	#generateTimesRepeat:on:indent:
  
  	#ifTrue:			#generateIfTrue:on:indent:
  	#ifFalse:		#generateIfFalse:on:indent:
  	#ifTrue:ifFalse:	#generateIfTrueIfFalse:on:indent:
  	#ifFalse:ifTrue:	#generateIfFalseIfTrue:on:indent:
  
  	#ifNotNil:		#generateIfNotNil:on:indent:
  	#ifNil:			#generateIfNil:on:indent:
  	#ifNotNil:ifNil:	#generateIfNotNilIfNil:on:indent:
  	#ifNil:ifNotNil:	#generateIfNilIfNotNil:on:indent:
  
  	#at:			#generateAt:on:indent:
  	#at:put:		#generateAtPut:on:indent:
  	#basicAt:		#generateAt:on:indent:
  	#basicAt:put:	#generateAtPut:on:indent:
  
  	#integerValueOf:			#generateIntegerValueOf:on:indent:
  	#integerObjectOf:			#generateIntegerObjectOf:on:indent:
  	#isIntegerObject: 			#generateIsIntegerObject:on:indent:
  	#cCode:					#generateInlineCCode:on:indent:
  	#cCode:inSmalltalk:		#generateInlineCCode:on:indent:
  	#cPreprocessorDirective:	#generateInlineCPreprocessorDirective:on:indent:
  	#cppIf:ifTrue:ifFalse:				#generateInlineCppIfElse:on:indent:
  	#cppIf:ifTrue:cppIf:ifTrue:ifFalse:	#generateInlineCppIfElse:on:indent:
  	#cppIf:ifTrue:						#generateInlineCppIfElse:on:indent:
  	#cppIf:ifFalse:						#generateInlineCppIfElse:on:indent:
  	#cCoerce:to:				#generateCCoercion:on:indent:
  	#cCoerceSimple:to:			#generateCCoercion:on:indent:
  	#addressOf:				#generateAddressOf:on:indent:
  	#addressOf:put:			#generateAddressOf:on:indent:
  	#asAddress:put:			#generateAsAddress:on:indent:
  	#signedIntFromLong64	#generateSignedIntFromLong64:on:indent:
  	#signedIntFromLong		#generateSignedIntFromLong:on:indent:
  	#signedIntFromShort		#generateSignedIntFromShort:on:indent:
  	#signedIntToLong64		#generateSignedIntToLong64:on:indent:
  	#signedIntToLong			#generateSignedIntToLong:on:indent:
  	#signedIntToShort			#generateSignedIntToShort:on:indent:
  	#preIncrement				#generatePreIncrement:on:indent:
  	#preDecrement			#generatePreDecrement:on:indent:
  	#inline:						#generateInlineDirective:on:indent:
  	#asFloat					#generateAsFloat:on:indent:
  	#asInteger					#generateAsInteger:on:indent:
  	#asIntegerPtr				#generateAsIntegerPtr:on:indent:
  	#asUnsignedInteger		#generateAsUnsignedInteger:on:indent:
  	#asUnsignedIntegerPtr		#generateAsUnsignedIntegerPtr:on:indent:
  	#asLong					#generateAsLong:on:indent:
  	#asUnsignedLong			#generateAsUnsignedLong:on:indent:
  	#asUnsignedLongLong		#generateAsUnsignedLongLong:on:indent:
  	#asVoidPointer				#generateAsVoidPointer:on:indent:
  	#asSymbol					#generateAsSymbol:on:indent:
  	#flag:						#generateFlag:on:indent:
  	#anyMask:					#generateAnyMask:on:indent:
  	#allMask:					#generateAllMask:on:indent:
  	#noMask:					#generateNoMask:on:indent:
  	#raisedTo:					#generateRaisedTo:on:indent:
  	#touch:					#generateTouch:on:indent:
  
  	#bytesPerOop 				#generateBytesPerOop:on:indent:
  	#bytesPerWord 			#generateBytesPerWord:on:indent:
  	#wordSize		 			#generateBytesPerWord:on:indent:
  	#baseHeaderSize			#generateBaseHeaderSize:on:indent:
  	#minSmallInteger			#generateSmallIntegerConstant:on:indent:
  	#maxSmallInteger			#generateSmallIntegerConstant:on:indent:
  	
  	#sharedCodeNamed:inCase:		#generateSharedCodeDirective:on:indent:
  
  	#perform:							#generatePerform:on:indent:
  	#perform:with:						#generatePerform:on:indent:
  	#perform:with:with:				#generatePerform:on:indent:
  	#perform:with:with:with:			#generatePerform:on:indent:
  	#perform:with:with:with:with:		#generatePerform:on:indent:
  	#perform:with:with:with:with:with:	#generatePerform:on:indent:
  
  	#value									#generateValue:on:indent:
  	#value:									#generateValue:on:indent:
  	#value:value:							#generateValue:on:indent:
  	#value:value:value:						#generateValue:on:indent:
  	#value:value:value:value:				#generateValue:on:indent:
  	#value:value:value:value:value:			#generateValue:on:indent:
  	#value:value:value:value:value:value:	#generateValue:on:indent:
  
  	#deny:								#generateDeny:on:indent:
  
  	#shouldNotImplement				#generateSmalltalkMetaError:on:indent:
  	#shouldBeImplemented			#generateSmalltalkMetaError:on:indent:
  	#subclassResponsibility				#generateSmalltalkMetaError:on:indent:
  	).
  
  	1 to: pairs size by: 2 do: [:i |
  		translationDict at: (pairs at: i) put: (pairs at: i + 1)].
  
  	pairs := #(
  	#ifTrue:				#generateIfTrueAsArgument:on:indent:	
  	#ifFalse:				#generateIfFalseAsArgument:on:indent:
  	#ifTrue:ifFalse:			#generateIfTrueIfFalseAsArgument:on:indent:
  	#ifFalse:ifTrue:			#generateIfFalseIfTrueAsArgument:on:indent:
  	#ifNotNil:				#generateIfNotNilAsArgument:on:indent:	
  	#ifNil:					#generateIfNilAsArgument:on:indent:
  	#ifNotNil:ifNil:			#generateIfNotNilIfNilAsArgument:on:indent:
  	#ifNil:ifNotNil:			#generateIfNilIfNotNilAsArgument:on:indent:
  	#cCode:				#generateInlineCCodeAsArgument:on:indent:
  	#cCode:inSmalltalk:	#generateInlineCCodeAsArgument:on:indent:
  	#cppIf:ifTrue:ifFalse:	#generateInlineCppIfElseAsArgument:on:indent:
  	#cppIf:ifTrue:			#generateInlineCppIfElseAsArgument:on:indent:
  
  	#value					#generateValueAsArgument:on:indent:
  	#value:					#generateValueAsArgument:on:indent:
  	#value:value:			#generateValueAsArgument:on:indent:
  	).
  
  	asArgumentTranslationDict := Dictionary new: 8.
  	1 to: pairs size by: 2 do: [:i |
  		asArgumentTranslationDict at: (pairs at: i) put: (pairs at: i + 1)].
  !

Item was changed:
  ----- Method: CFloatArray>>coerceTo:sim: (in category 'converting') -----
  coerceTo: cTypeString sim: interpreterSimulator
  
  	^cTypeString caseOf: {
+ 		['float *']		-> [self shallowCopy unitSize: 4; yourself].
+ 		['double *']		-> [self shallowCopy unitSize: 8; yourself].
+ 		['sqIntptr_t']	-> [self] "this is used, in e.g. ffiAddressOf:startingAt:size: to coerce a pointer to an integer; it does not imply a real conversion. it is just to be able to pass the pointer as an integer" }
- 		['float *']	-> [self shallowCopy unitSize: 4; yourself].
- 		['double *']	-> [self shallowCopy unitSize: 8; yourself] }
  		otherwise: [self asNonFloatAccessor coerceTo: cTypeString sim: interpreterSimulator]!

Item was changed:
  ----- Method: CoInterpreter class>>initializeMiscConstants (in category 'initialization') -----
  initializeMiscConstants
  
  	super initializeMiscConstants.
  	COGVM := true.
  
  	MinBackwardJumpCountForCompile := 40.
  
  	MaxNumArgs := 15.
  	PrimCallNeedsNewMethod := 1.
  	PrimCallNeedsPrimitiveFunction := 2.
  	PrimCallMayEndureCodeCompaction := 4.
  	PrimCallCollectsProfileSamples := 8.
  	PrimCallDoNotJIT := 16.
  	PrimCallIsExternalCall := 32.
  	"CheckAllocationFillerAfterPrimCall := 32. this has never been successfully used in all the years we've had it; nuking it"
  	PrimCallOnSmalltalkStack := 64. "Speed up simple external prims by avoiding stack switch"
+ 	PrimCallOnSmalltalkStackAlign2x := 128. "Align stack to a 2 x word size boundary, e.g. for MMX instructions etc"
+ 	FastCPrimitiveUseCABIFlag := 256.
  
+ 	"Flags for use in export:flags:, shifted to overlap with the PrimCallXXX flags above"
+ 	FastCPrimitiveFlag := 1.				"a.k.a. PrimCallOnSmalltalkStack"
+ 	FastCPrimitiveAlignForFloatsFlag := 2.	"a.k.a. PrimCallOnSmalltalkStackAlign2x"
+ 	FastCPrimitiveUseCABIFlag := 4.		"a.k.a. FastCPrimitiveUseCABIFlag"
- 	"Flags for use in export:flags:, shifted and combined with the PrimCallXXX flags above"
- 	FastCPrimitiveFlag := 1.
  	PrimitiveMetadataFlagsShift := PrimCallOnSmalltalkStack highBit - FastCPrimitiveFlag highBit.
  
  	"the primitive trace log; a record of the last 256 named/external primitives or significant events invoked."
  	PrimTraceLogSize := 256. "Room for 256 selectors.  Must be 256 because we use a byte to hold the index"
  	TraceBufferSize := 256 * 3. "Room for 256 events"
  	TraceContextSwitch := self objectMemoryClass basicNew integerObjectOf: 1.
  	TraceBlockActivation := self objectMemoryClass basicNew integerObjectOf: 2.
  	TraceBlockCreation := self objectMemoryClass basicNew integerObjectOf: 3.
  	TraceIncrementalGC := self objectMemoryClass basicNew integerObjectOf: 4.
  	TraceFullGC := self objectMemoryClass basicNew integerObjectOf: 5.
  	TraceCodeCompaction := self objectMemoryClass basicNew integerObjectOf: 6.
  	TraceOwnVM := self objectMemoryClass basicNew integerObjectOf: 7.
  	TraceDisownVM := self objectMemoryClass basicNew integerObjectOf: 8.
  	TraceThreadSwitch := self objectMemoryClass basicNew integerObjectOf: 9.
  	TracePreemptDisowningThread := self objectMemoryClass basicNew integerObjectOf: 10.
  	TraceVMCallback := self objectMemoryClass basicNew integerObjectOf: 11.
  	TraceVMCallbackReturn := self objectMemoryClass basicNew integerObjectOf: 12.
  	TraceStackOverflow := self objectMemoryClass basicNew integerObjectOf: 13.
  	TracePrimitiveFailure := self objectMemoryClass basicNew integerObjectOf: 14.
  	TracePrimitiveRetry := self objectMemoryClass basicNew integerObjectOf: 15.
  
  	TraceIsFromMachineCode := 1.
  	TraceIsFromInterpreter := 2.
  	CSCallbackEnter := 3.
  	CSCallbackLeave := 4.
  	CSEnterCriticalSection := 5.
  	CSExitCriticalSection := 6.
  	CSResume := 7.
  	CSSignal := 8.
  	CSSuspend := 9.
  	CSWait := 10.
  	CSYield := 11.
  	CSCheckEvents := 12.
  	CSThreadSchedulingLoop := 13.
  	CSOwnVM := 14.
  	CSThreadBind := 15.
  	CSSwitchIfNeccessary := 16.
  
  	TraceSources := CArrayAccessor on: #('?' 'm' 'i' 'callbackEnter' 'callbackLeave' 'enterCritical' 'exitCritical' 'resume' 'signal'  'suspend' 'wait' 'yield' 'eventcheck' 'threadsched' 'ownVM' 'bindToThread' 'switchIfNecessary').
  
  	"this is simulation only"
  	RumpCStackSize := 4096!

Item was changed:
  ----- Method: CoInterpreter class>>metadataFlagsForPrimitive: (in category 'spur compilation support') -----
  metadataFlagsForPrimitive: aPrimitiveMethodOrNil
  	"We allow methods to decorate themselves with 8 flags (only one specified so far: FastCPrimitiveFlag)
  	 using the export:flags: pragma."
  	aPrimitiveMethodOrNil ifNil: [^0].
  	^(aPrimitiveMethodOrNil pragmaAt: #export:flags:)
  		ifNil: [0]
  		ifNotNil: [:pragma| | flags |
  			(flags := pragma arguments second) isInteger
  				ifTrue: [flags]
+ 				ifFalse: [self valueOfMetadataFlag: flags]]!
- 				ifFalse:
- 					[(self bindingOf: flags)
- 						ifNil: [self error: 'could not find primitive flag'. 0]
- 						ifNotNil:
- 							[:binding| | flagValue |
- 							((flagValue := binding value) isInteger and: [flagValue between: 1 and: 255])
- 								ifTrue: [flagValue]
- 								ifFalse: [self error: 'integer flag required'. 0]]]]!

Item was added:
+ ----- Method: CoInterpreter class>>valueOfMetadataFlag: (in category 'spur compilation support') -----
+ valueOfMetadataFlag: symbolOrArray
+ 	"We allow methods to decorate themselves with 8 flags (only one specified so far: FastCPrimitiveFlag)
+ 	 using the export:flags: pragma.  A single flag is supplied as a symbol.  Multiple flags are supplied as
+ 	 an array of symbols (pragmas take only literal arguments)."
+ 	^symbolOrArray isArray
+ 		ifTrue:
+ 			[symbolOrArray inject: 0 into: [:flags :flagName| flags + (self valueOfMetadataFlag: flagName)]]
+ 		ifFalse:
+ 			[(self bindingOf: symbolOrArray)
+ 				ifNil: [self error: 'could not find primitive flag'. 0]
+ 				ifNotNil:
+ 					[:binding| | flagValue |
+ 					((flagValue := binding value) isInteger and: [flagValue between: 1 and: 255])
+ 						ifTrue: [flagValue]
+ 						ifFalse: [self error: 'integer flag required'. 0]]]!

Item was changed:
  ----- Method: CoInterpreter>>functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: (in category 'cog jit support') -----
  functionPointerForCompiledMethod: methodObj primitiveIndex: primitiveIndex primitivePropertyFlagsInto: flagsPtr
  	<api>
  	<returnTypeC: 'void (*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void)'>
  	| functionPointer |
  	<var: #functionPointer declareC: #'void (*functionPointer)(void)'>
  	flagsPtr ifNotNil:
  		[flagsPtr at: 0 put: (self primitivePropertyFlags: primitiveIndex)].
  	functionPointer := self functionPointerFor: primitiveIndex inClass: nil.
  	functionPointer == #primitiveCalloutToFFI ifTrue:
  		[^self functionForPrimitiveCallout].
  	functionPointer == #primitiveExternalCall ifTrue:
  		[| lit |
  		 lit := self attemptToLinkExternalPrimitive: methodObj.
  		 "N.B. We only support the FastCPrimitiveFlag on Spur because Spur
  		  will *not* run a GC to satisfy an allocation in a primitive. The V3
  		  ObjectMemory will and hence the depth of stack needed in a V3
  		  primitive is probably too large to safely execute on a stack page."
  		  objectMemory hasSpurMemoryManagerAPI ifTrue:
  			[| flags shiftedMetadataFlags |
  			 flags := objectMemory fetchPointer: ExternalCallLiteralFlagsIndex ofObject: lit.
  		 	 (objectMemory isIntegerObject: flags) ifTrue:
  				[shiftedMetadataFlags := ((objectMemory integerValueOf: flags) bitAnd: 16rFF) bitShift: PrimitiveMetadataFlagsShift.
+ 				 shiftedMetadataFlags ~= 0 ifTrue:
+ 					[flagsPtr at: 0 put: shiftedMetadataFlags]]].
- 				 flagsPtr at: 0 put: ((flagsPtr at: 0) bitOr: shiftedMetadataFlags)]].
  		 ^self functionForPrimitiveExternalCall: methodObj].
  	^functionPointer!

Item was changed:
  ----- Method: CoInterpreter>>primitivePropertyFlagsForSpur: (in category 'cog jit support') -----
  primitivePropertyFlagsForSpur: primIndex
  	<inline: true>
  	"Answer any special requirements of the given primitive.  Spur always needs to set
  	 primitiveFunctionPointer and newMethod so primitives can retry on failure due to forwarders."
  	| baseFlags |
  	self cCode: [] inSmalltalk: [#(mcprimHashMultiply: primitiveExternalCall primitiveCalloutToFFI)]. "For senders..."
  	primIndex = PrimNumberHashMultiply ifTrue:
+ 		[^PrimCallOnSmalltalkStack+FastCPrimitiveUseCABIFlag].
- 		[^PrimCallOnSmalltalkStack].
  	baseFlags := PrimCallNeedsPrimitiveFunction + PrimCallNeedsNewMethod.
  	profileSemaphore ~= objectMemory nilObject ifTrue:
  		[baseFlags := baseFlags bitOr: PrimCallCollectsProfileSamples].
  
  	(self isCalloutPrimitiveIndex: primIndex) "For callbacks & module unloading"
  		ifTrue: [baseFlags := baseFlags bitOr: PrimCallMayEndureCodeCompaction + PrimCallIsExternalCall]
  		ifFalse:
  			[(self isCodeCompactingPrimitiveIndex: primIndex) ifTrue: "For code reclamations"
  				[baseFlags := baseFlags bitOr: PrimCallMayEndureCodeCompaction]].
  
  	^baseFlags!

Item was changed:
  ----- Method: CogARMv8Compiler>>computeMaximumSize (in category 'generate machine code') -----
  computeMaximumSize
  	"Because we don't use Thumb, each ARMv8 instruction has 4 bytes. Several
  	 abstract opcodes need more than one instruction. Instructions that refer to
  	 constants and/or literals depend on literals being stored out-of-line or encoded
  	 in immediate instruction fields (i.e. we only support OutOfLineLiteralsManager.
  
  	 N.B.  The ^N forms are to get around the old bytecode compiler's long branch
  	 limits which are exceeded when each case jumps around the otherwise."
  
  	opcode
  		caseOf: {
  		"Noops & Pseudo Ops"
  		[Label]						-> [^0].
  		[Literal]						-> [^self literalSize].
  		[AlignmentNops]			-> [^(operands at: 0) - 4].
  		"Control"
  		[CallFull]					-> [^8].
  		[JumpFull]					-> [^8].
  		[JumpLongZero]			-> [^8].
  		[JumpLongNonZero]		-> [^8].
  		[JumpMulOverflow]			-> [^8].
  		[JumpNoMulOverflow]		-> [^8].
  		[JumpFPOrdered]			-> [^8].
  		[JumpFPUnordered]		-> [^8].
  		[RetN]						-> [^(operands at: 0) = 0 ifTrue: [4] ifFalse: [8]].
  		[NativeRetN]				-> [^(operands at: 0) = 0 ifTrue: [4] ifFalse: [8]].
  
  		"Arithmetic"
  		[AddCqR]						-> [^self isPossiblyShiftableNegatableImm12: (operands at: 0) signedIntFromLong64
  												ifTrue: [:ign|4] ifFalse: [8]].
  		[AddCqRR]						-> [^self isPossiblyShiftableNegatableImm12: (operands at: 0) signedIntFromLong64
  												ifTrue: [:ign|4] ifFalse: [8]].
  		[CmpCqR]						-> [^self isPossiblyShiftableNegatableImm12: (operands at: 0) signedIntFromLong64
  												ifTrue: [:ign|4] ifFalse: [8]].
  		[SubCqR]						-> [^self isPossiblyShiftableNegatableImm12: (operands at: 0) signedIntFromLong64
  												ifTrue: [:ign|4] ifFalse: [8]].
  		[LoadEffectiveAddressMwrR]	-> [^self isPossiblyShiftableNegatableImm12: (operands at: 0) signedIntFromLong64
  												ifTrue: [:ign|4] ifFalse: [8]].
  		[AndCqR]					-> [^self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8]].
+ 		[AndCqRR]					-> ["N.B. For three operand logical ops only support AndCqRR with a NativeSPReg target, used for alignment purposes."
+ 										^(self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8])
+ 										+ ((operands at: 2) = NativeSPReg ifTrue: [4] ifFalse: [0])].
- 		[AndCqRR]					-> [^self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8]].
  		[OrCqR]					-> [^self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8]].
  		[OrCqRR]					-> [^self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8]].
  		[TstCqR]					-> [^self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8]].
  		[XorCqR]					-> [^self isImmNImmSImmREncodableBitmask: (operands at: 0) ifTrue: [:n :imms :immr|4] ifFalse:[8]].
  		[AddCwR]					-> [^8].
  		[AndCwR]					-> [^8].
  		[CmpCwR]					-> [^8].
  		[CmpC32R]					-> [^8].
  		[OrCwR]					-> [^8].
  		[SubCwR]					-> [^8].
  		[XorCwR]					-> [^8].
  		[SubRR]					-> [^(operands at: 0) = SP ifTrue: [8] ifFalse: [4]].
  		[SubRRR]					-> [^(operands at: 0) = SP ifTrue: [8] ifFalse: [4]].
  
  		"ARMv8 Specific Arithmetic"
  		[MulOverflowRRR]			-> [^12].
  		"Data Movement"						
  		[MoveAwR]				-> [^((self isAddressRelativeToVarBase: (operands at: 0))
  									    or: [cogit addressIsInCurrentCompilation: (operands at: 0)])
  										ifTrue: [(operands at: 1) ~= SP ifTrue: [4] ifFalse: [8]]
  										ifFalse: [(operands at: 1) ~= SP ifTrue: [8] ifFalse: [12]]].
  		[MoveRAw]				-> [^((self isAddressRelativeToVarBase: (operands at: 1))
  									    or: [cogit addressIsInCurrentCompilation: (operands at: 1)])
  										ifTrue: [(operands at: 0) ~= SP ifTrue: [4] ifFalse: [8]]
  										ifFalse: [(operands at: 0) ~= SP ifTrue: [8] ifFalse: [12]]].
  		[MoveAwRR]			-> [self assert: (self isAddressRelativeToVarBase: (operands at: 0)).
  									^((operands at: 1) = SP or: [(operands at: 2) = SP])
  										ifTrue: [8] ifFalse: [4]].
  		[MoveRRAw]			-> [self assert: (self isAddressRelativeToVarBase: (operands at: 2)).
  									^((operands at: 0) = SP or: [(operands at: 1) = SP])
  										ifTrue: [8] ifFalse: [4]].
  		[MoveAbR]				-> [^(self isAddressRelativeToVarBase: (operands at: 0)) ifTrue: [4] ifFalse: [8]].
  		[MoveRAb]				-> [^(self isAddressRelativeToVarBase: (operands at: 1)) ifTrue: [4] ifFalse: [8]].
  		[MoveMwrR]			-> [^(self isImm12orImm9offset: (operands at: 0)) ifTrue: [4] ifFalse: [8]].
  		[MoveRMwr]			-> [^(self isImm12orImm9offset: (operands at: 1)) ifTrue: [4] ifFalse: [8]].
  		[MoveM32rR]			-> [^(self isImm12orImm9offset: (operands at: 0)) ifTrue: [4] ifFalse: [8]].
  		[MoveRM32r]			-> [^(self isImm12orImm9offset: (operands at: 1)) ifTrue: [4] ifFalse: [8]].
  		[MoveM16rR]			-> [^(self isImm12orImm9offset: (operands at: 0)) ifTrue: [4] ifFalse: [8]].
  		[MoveRM16r]			-> [^(self isImm12orImm9offset: (operands at: 1)) ifTrue: [4] ifFalse: [8]].
  		[MoveMbrR]			-> [^(self isImm12orImm9offset: (operands at: 0)) ifTrue: [4] ifFalse: [8]].
  		[MoveRMbr]			-> [^(self isImm12orImm9offset: (operands at: 1)) ifTrue: [4] ifFalse: [8]].
  		[MoveM64rRd]			-> [^(self isUnsigned12BitMultipleOf8: (operands at: 0)) ifTrue: [4] ifFalse: [8]].
  		[MoveRdM64r]			-> [^(self isUnsigned12BitMultipleOf8: (operands at: 1)) ifTrue: [4] ifFalse: [8]].
  		[PushCw]				-> [^8].
  		[PushCq]				-> [^8].
  		}
  		otherwise: [^4].
  	^0 "to keep C compiler quiet"
  !

Item was changed:
  ----- Method: CogARMv8Compiler>>concretizeLogicalOp:CqRDest: (in category 'generate machine code - concretize') -----
  concretizeLogicalOp: op CqRDest: destReg
  	"AND	(immediate) - 64-bit variant on page C6-775
  	 ORR	(immediate) - 64-bit variant on page C6-1125
  	 EOR	(immediate) - 64-bit variant on page C6-896
  	 ANDS	(immediate) - 64-bit variant on page C6-779
  	 C6.2.329	TST (immediate)	C6-1346"
  	<inline: false>
+ 	| srcReg constant effectiveDestReg offset |
- 	| srcReg constant |
  	constant := operands at: 0.
  	srcReg := operands at: 1.
+ 	"N.B. For three operand logical ops only support AndCq: const R: reg R: NativeSPReg, which is used for alignment."
+ 	effectiveDestReg := (opcode = AndCqRR and: [destReg = NativeSPReg]) ifTrue: [RISCTempReg] ifFalse: [destReg].
+ 	self isImmNImmSImmREncodableBitmask: constant
- 	^self isImmNImmSImmREncodableBitmask: constant
  		ifTrue:
  			[:n :imms :immr|
  			 machineCode
  				at: 0
  				put: 2r1001001 << 25
  					+ (op << 29)
  					+ (n << 22)
  					+ (immr << 16)
  					+ (imms << 10)
  					+ (srcReg << 5)
+ 					+ effectiveDestReg.
+ 			 offset := 4]
- 					+ destReg.
- 			 4]
  		ifFalse:
+ 			[offset := self emitMoveCw: constant intoR: RISCTempReg at: 0.
- 			[| offset |
- 			offset := self emitMoveCw: constant intoR: RISCTempReg at: 0.
  			"OPC	N
  			 00		0	AND (shifted register) - 64-bit variant on page C6-777
  			 00		1	BIC (shifted register) - 64-bit variant on page C6-808
  			 01		0	ORR (shifted register) - 64-bit variant on page C6-1127
  			 01		1	ORN (shifted register) - 64-bit variant on page C6-1123
  			 10		0	EOR (shifted register) - 64-bit variant on page C6-898
  			 10		1	EON (shifted register) - 64-bit variant on page C6-894
  			 11		0	ANDS (shifted register) - 64-bit variant on page C6-781
  			 11		0	BICS (shifted register) - 64-bit variant on page C6-810"
  			machineCode
+ 				at: offset // 4
+ 				put: 2r1000101 << 25
+ 					+ (op << 29)
+ 					+ (RISCTempReg << 16)
+ 					+ (srcReg << 5)
+ 					+ effectiveDestReg.
+ 			offset := offset + 4].
+ 	(opcode = AndCqRR and: [destReg = NativeSPReg]) ifFalse:
+ 		[^offset].
+ 	machineCode at: offset // 4 put: (self movern: effectiveDestReg rd: destReg).
+ 	^offset + 4
- 						at: offset // 4
- 						put: 2r1000101 << 25
- 							+ (op << 29)
- 							+ (RISCTempReg << 16)
- 							+ (srcReg << 5)
- 							+ destReg.
- 			offset + 4]
  	"cogit processor disassembleInstructionAt: 0 In: machineCode object"
  	"cogit processor disassembleInstructionAt: 4 In: machineCode object"
  	"cogit processor disassembleInstructionAt: 8 In: machineCode object"!

Item was added:
+ ----- Method: CogARMv8Compiler>>genLoadNativeSPRegWithAlignedSPReg (in category 'smalltalk calling convention') -----
+ genLoadNativeSPRegWithAlignedSPReg
+ 	cogit AndCq: (objectMemory wordSize * 2 - 1) bitInvert R: SPReg R: NativeSPReg!

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

Item was changed:
  CogClass subclass: #CurrentImageCoInterpreterFacade
  	instanceVariableNames: 'memory cogit coInterpreter objectMemory objectMap headerToMethodMap cachedObject cachedOop variables cFramePointer cStackPointer'
  	classVariableNames: ''
+ 	poolDictionaries: 'CogMethodConstants VMBasicConstants VMBytecodeConstants VMObjectIndices VMSqueakClassIndices'
- 	poolDictionaries: 'CogMethodConstants VMBasicConstants VMObjectIndices VMSqueakClassIndices'
  	category: 'VMMaker-Support'!
  
  !CurrentImageCoInterpreterFacade commentStamp: 'eem 8/6/2014 14:59' prior: 0!
  A CurrentImageCoInterpreterFacade is a stand-in for an object memory (ObjectMemory, SpurMemoryManager, etc) that allows the Cogits to access image objects as if they were in the simulator VM's heap.  hence it allows the Cogits to generate code for methdos in the current image, for testing, etc.
  
  Instance Variables
  	cachedObject:			<Object>
  	cachedOop:			<Integer>
  	coInterpreter:			<CoInterpreter>
  	cogit:					<Cogit>
  	headerToMethodMap:	<Dictionary>
  	memory:				<ByteArray>
  	objectMap:				<IdentityDictionary>
  	objectMemory:			<NewObjectMemory|SpurMemoryManager>
  	variables:				<Dictionary>
  
  cachedObject
  	- the object matching cachedOop, to speed-up oop to obejct mapping
  
  cachedOop
  	- the last used oop
  
  coInterpreter
  	- the CoInterpreter simulator used by the cogit.
  
  cogit
  	- the code egnerator in use
  
  headerToMethodMap
  	- a map from header to CompiledMethod
  
  memory
  	- a rump memory for holding various interpreter variables (e.g. stackLimit) that are accessed as memory locations by generated code
  
  objectMap
  	- map from objects to their oops
  
  objectMemory
  	- the object memory used to encode various values, answer queries, etc
  
  variables
  	- a map from the names of variables to their addresses in memory
  !

Item was added:
+ ----- Method: CurrentImageCoInterpreterFacade>>fakeLinkedExternalLiteralFor: (in category 'private') -----
+ fakeLinkedExternalLiteralFor: methodOop
+ 	| method |
+ 	method := self objectForOop: methodOop.
+ 	self assert: (method isCompiledMethod and: [method primitive = PrimNumberExternalCall]).
+ 	self shouldBeImplemented!

Item was changed:
  ----- Method: CurrentImageCoInterpreterFacade>>functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: (in category 'accessing') -----
  functionPointerForCompiledMethod: methodOop primitiveIndex: primIndex primitivePropertyFlagsInto: flagsPtr
  	^([coInterpreter
  		functionPointerForCompiledMethod: methodOop
  		primitiveIndex: primIndex
  		primitivePropertyFlagsInto: flagsPtr]
  			on: Error
  			do: [:ex|
+ 				"N.B. THIS IS WORK IN PROGRESS!! NO TIME TO MAKE fakeLinkedExternalLiteralFor: WORK PROPERLY AT THE MOMENT"
+ 				(ex signalerContext findContextSuchThat: [:ctxt| ctxt selector == #attemptToLinkExternalPrimitive:])
+ 					ifNotNil: [ex resume: (self fakeLinkedExternalLiteralFor: methodOop)]
+ 					ifNil: [#someExternalPrimitive]]) ifNotNil:
- 				#someExternalPrimitive]) ifNotNil:
  		[:symbol|
  		self addressForLabel: symbol]!

Item was added:
+ ----- Method: CurrentImageCoInterpreterFacade>>hasSpurMemoryManagerAPI (in category 'testing') -----
+ hasSpurMemoryManagerAPI
+ 	^objectMemory hasSpurMemoryManagerAPI!

Item was added:
+ ----- Method: CurrentImageCoInterpreterFacade>>recordPrimTraceForMethod: (in category 'cog jit support') -----
+ recordPrimTraceForMethod: methodOop
+ 	^coInterpreter recordPrimTraceForMethod: methodOop!

Item was added:
+ ----- Method: InterpreterPlugin>>objectMemory (in category 'simulation support') -----
+ objectMemory
+ 	^interpreterProxy objectMemory!

Item was removed:
- ----- Method: SimpleStackBasedCogit>>compileOnStackExternalPrimitive: (in category 'primitive generators') -----
- compileOnStackExternalPrimitive: primitiveRoutine
- 	"Compile a fast call of a C primitive using the current stack page, avoiding the stack switch except on failure.
- 	 This convention still uses stackPointer and argumentCount to access operands.  Push all operands to the stack,
- 	 assign stackPointer, argumentCount, and zero primFailCode.  Make the call (saving a LinkReg if required).
- 	 Test for failure and return.  On failure on Spur, if there is an accessor depth, assign framePointer and newMethod,
- 	 do the stack switch, call checkForAndFollowForwardedPrimitiveState, and loop back if forwarders are found.
- 	 Fall through to frame build."
- 	 
- 	<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
- 	| jmp retry calleeSavedReg |
- 	"Clear the primFailCode and set argumentCount"
- 	self MoveCq: 0 R: TempReg.
- 	self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
- 	methodOrBlockNumArgs ~= 0 ifTrue:
- 		[self MoveCq: methodOrBlockNumArgs R: TempReg].
- 	self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
- 	self genExternalizeStackPointerForFastPrimitiveCall.
- 	backEnd hasLinkRegister ifTrue:
- 		[self PushR: LinkReg].
- 	retry := self Label.
- 	calleeSavedReg := NoReg.
- 	(SPReg ~= NativeSPReg
- 	 and: [(self isCalleeSavedReg: SPReg) not]) ifTrue:
- 		[calleeSavedReg := self availableRegisterOrNoneIn: ABICalleeSavedRegisterMask.
- 		 self deny: calleeSavedReg = NoReg.
- 		 self MoveR: SPReg R: calleeSavedReg].
- 	self CallFullRT: primitiveRoutine.
- 	self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
- 	calleeSavedReg ~= NoReg ifTrue:
- 		[self MoveR: calleeSavedReg R: SPReg].
- 	self CmpCq: 0 R: TempReg.
- 	jmp := self JumpNonZero: 0.
- 	backEnd hasLinkRegister
- 		ifTrue: [self PopR: LinkReg]
- 		ifFalse: [self PopR: TempReg]. "i.e. save retpc"
- 	self MoveAw: coInterpreter stackPointerAddress R: SPReg.
- 	self PopR: ReceiverResultReg.
- 	backEnd hasLinkRegister ifFalse: [self PushR: TempReg]. "i.e. restore retpc"
- 	self RetN: 0.
- 
- 	jmp jmpTarget: self Label.
- 	(objectRepresentation hasSpurMemoryManagerAPI
- 	 and: [(coInterpreter accessorDepthForExternalPrimitiveMethod: methodObj) >= 0]) ifTrue:
- 		[| skip |
- 		 "Given that following primitive state to the accessor depth is recursive, we're asking for
- 		  trouble if we run the fixup on the Smalltalk stack page.  Run it on the full C stack instead.
- 		 This won't be a performance issue since primitive failure should be very rare."
- 		self MoveR: FPReg Aw: coInterpreter framePointerAddress.
- 		self MoveCw: primitiveRoutine asInteger R: TempReg.
- 		self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress.
- 		self genLoadCStackPointersForPrimCall.
- 		methodLabel addDependent:
- 			(self annotateAbsolutePCRef:
- 				(self MoveCw: methodLabel asInteger R: ClassReg)).
- 		self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
- 		self MoveR: TempReg Aw: coInterpreter newMethodAddress.
- 		self CallFullRT: (self cCode: [#checkForAndFollowForwardedPrimitiveState asUnsignedIntegerPtr]
- 							   inSmalltalk: [self simulatedTrampolineFor: #checkForAndFollowForwardedPrimitiveState]).
- 		backEnd genLoadStackPointersForFastPrimCall: ClassReg.
- 		self CmpCq: 0 R: ABIResultReg.
- 		skip := self JumpZero: 0.
- 		self MoveCq: 0 R: TempReg.
- 		self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
- 		self Jump: retry.
- 		skip jmpTarget: self Label].
- 	"Finally remember to reload ReceiverResultReg if required.  Even if
- 	 arguments have been pushed, the prolog sequence assumes it is live."
- 	(self register: ReceiverResultReg isInMask: ABICallerSavedRegisterMask) ifTrue:
- 		[self MoveMw: (backEnd hasLinkRegister ifTrue: [methodOrBlockNumArgs] ifFalse: [methodOrBlockNumArgs + 1]) * objectMemory wordSize
- 			r: SPReg
- 			R: ReceiverResultReg].
- 	^0!

Item was added:
+ ----- Method: SimpleStackBasedCogit>>compileOnStackExternalPrimitive:flags: (in category 'primitive generators') -----
+ compileOnStackExternalPrimitive: primitiveRoutine flags: flags
+ 	"Compile a fast call of a C primitive using the current stack page, avoiding the stack switch except on failure.
+ 	 This convention still uses stackPointer and argumentCount to access operands.  Push all operands to the stack,
+ 	 assign stackPointer, argumentCount, and zero primFailCode.  Make the call (saving a LinkReg if required).
+ 	 Test for failure and return.  On failure on Spur, if there is an accessor depth, assign framePointer and newMethod,
+ 	 do the stack switch, call checkForAndFollowForwardedPrimitiveState, and loop back if forwarders are found.
+ 	 Fall through to frame build."
+ 	 
+ 	<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
+ 	| jmp retry calleeSavedReg |
+ 	self assert: (flags anyMask: PrimCallOnSmalltalkStack).
+ 
+ 	"Clear the primFailCode and set argumentCount"
+ 	self MoveCq: 0 R: TempReg.
+ 	self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
+ 	methodOrBlockNumArgs ~= 0 ifTrue:
+ 		[self MoveCq: methodOrBlockNumArgs R: TempReg].
+ 	self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
+ 	self genExternalizeStackPointerForFastPrimitiveCall.
+ 	backEnd hasLinkRegister ifTrue:
+ 		[self PushR: LinkReg].
+ 	retry := self Label.
+ 	calleeSavedReg := NoReg.
+ 	(SPReg ~= NativeSPReg
+ 	 and: [(self isCalleeSavedReg: SPReg) not]) ifTrue:
+ 		[calleeSavedReg := self availableRegisterOrNoneIn: ABICalleeSavedRegisterMask.
+ 		 self deny: calleeSavedReg = NoReg.
+ 		 self MoveR: SPReg R: calleeSavedReg].
+ 	(flags anyMask: PrimCallOnSmalltalkStackAlign2x)
+ 		ifTrue: [self AndCq: (objectMemory wordSize * 2 - 1) bitInvert R: SPReg R: NativeSPReg]
+ 		ifFalse:
+ 			[SPReg ~= NativeSPReg ifTrue:
+ 				[backEnd genLoadNativeSPRegWithAlignedSPReg]].
+ 	self CallFullRT: primitiveRoutine.
+ 	self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
+ 	calleeSavedReg ~= NoReg ifTrue:
+ 		[self MoveR: calleeSavedReg R: SPReg].
+ 	self CmpCq: 0 R: TempReg.
+ 	jmp := self JumpNonZero: 0.
+ 	backEnd hasLinkRegister
+ 		ifTrue: [self PopR: LinkReg]
+ 		ifFalse: [self PopR: TempReg]. "i.e. save retpc"
+ 	self MoveAw: coInterpreter stackPointerAddress R: SPReg.
+ 	self PopR: ReceiverResultReg.
+ 	backEnd hasLinkRegister ifFalse: [self PushR: TempReg]. "i.e. restore retpc"
+ 	self RetN: 0.
+ 
+ 	jmp jmpTarget: self Label.
+ 	(objectRepresentation hasSpurMemoryManagerAPI
+ 	 and: [(coInterpreter accessorDepthForExternalPrimitiveMethod: methodObj) >= 0]) ifTrue:
+ 		[| skip |
+ 		 "Given that following primitive state to the accessor depth is recursive, we're asking for
+ 		  trouble if we run the fixup on the Smalltalk stack page.  Run it on the full C stack instead.
+ 		 This won't be a performance issue since primitive failure should be very rare."
+ 		self MoveR: FPReg Aw: coInterpreter framePointerAddress.
+ 		self MoveCw: primitiveRoutine asInteger R: TempReg.
+ 		self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress.
+ 		self genLoadCStackPointersForPrimCall.
+ 		methodLabel addDependent:
+ 			(self annotateAbsolutePCRef:
+ 				(self MoveCw: methodLabel asInteger R: ClassReg)).
+ 		self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
+ 		self MoveR: TempReg Aw: coInterpreter newMethodAddress.
+ 		self CallFullRT: (self cCode: [#checkForAndFollowForwardedPrimitiveState asUnsignedIntegerPtr]
+ 							   inSmalltalk: [self simulatedTrampolineFor: #checkForAndFollowForwardedPrimitiveState]).
+ 		backEnd genLoadStackPointersForFastPrimCall: ClassReg.
+ 		self CmpCq: 0 R: ABIResultReg.
+ 		skip := self JumpZero: 0.
+ 		self MoveCq: 0 R: TempReg.
+ 		self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
+ 		self Jump: retry.
+ 		skip jmpTarget: self Label].
+ 	"Finally remember to reload ReceiverResultReg if required.  Even if
+ 	 arguments have been pushed, the prolog sequence assumes it is live."
+ 	(self register: ReceiverResultReg isInMask: ABICallerSavedRegisterMask) ifTrue:
+ 		[self MoveMw: (backEnd hasLinkRegister ifTrue: [methodOrBlockNumArgs] ifFalse: [methodOrBlockNumArgs + 1]) * objectMemory wordSize
+ 			r: SPReg
+ 			R: ReceiverResultReg].
+ 	^0!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>compilePrimitive (in category 'primitive generators') -----
  compilePrimitive
  	"Compile a primitive.  If possible, performance-critical primitives will
  	 be generated by their own routines (primitiveGenerator).  Otherwise,
  	 if there is a primitive at all, we call the C routine with the usual
  	 stack-switching dance, test the primFailCode and then either return
  	 on success or continue to the method body."
  	<inline: false>
  	| primitiveDescriptor primitiveRoutine flags |
  	<var: #primitiveDescriptor type: #'PrimitiveDescriptor *'>
  	<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
  	primitiveIndex = 0 ifTrue: [^0].
  	"If a descriptor specifies an argument count (by numArgs >= 0) then it must match
  	 for the generated code to be correct.  For example for speed many primitives use
  	 ResultReceiverReg instead of accessing the stack, so the receiver better be at
  	 numArgs down the stack.  Use the interpreter version if not."
  	((primitiveDescriptor := self primitiveGeneratorOrNil) notNil
  	 and: [primitiveDescriptor primitiveGenerator notNil
  	 and: [(primitiveDescriptor primNumArgs < 0 "means generator doesn't care"
  		   or: [primitiveDescriptor primNumArgs = (coInterpreter argumentCountOf: methodObj)])]]) ifTrue:
  		[| opcodeIndexAtPrimitive code |
  		"Note opcodeIndex so that any arg load instructions
  		 for unimplemented primitives can be discarded."
  		 opcodeIndexAtPrimitive := opcodeIndex.
  		 code := objectRepresentation perform: primitiveDescriptor primitiveGenerator.
  
  		(code < 0 and: [code ~= UnimplementedPrimitive]) ifTrue: "Generator failed, so no point continuing..."
  			[^code].
  		"If the primitive can never fail then there is nothing more that needs to be done."
  		code = UnfailingPrimitive ifTrue:
  			[^0].
  		"If the machine code version handles all cases the only reason to call the interpreter
  		 primitive is to reap the primitive error code.  Don't bother if it isn't used."
  		(code = CompletePrimitive
  		 and: [(self methodUsesPrimitiveErrorCode: methodObj header: methodHeader) not]) ifTrue:
  			[^0].
  		"Discard any arg load code generated by the primitive generator."
  		code = UnimplementedPrimitive ifTrue:
  			[opcodeIndex := opcodeIndexAtPrimitive]].
  
  	primitiveRoutine := coInterpreter
  							functionPointerForCompiledMethod: methodObj
  							primitiveIndex: primitiveIndex
  							primitivePropertyFlagsInto: (self addressOf: flags put: [:val| flags := val]).
  	(flags anyMask: PrimCallDoNotJIT) ifTrue:
  		[^ShouldNotJIT].
  
- 	(flags anyMask: PrimCallOnSmalltalkStack) ifTrue:
- 		[(flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue: "TEMPORARY HACK!!!!"
- 			[^self compileOnStackExternalPrimitive: primitiveRoutine].
- 		 ^self compileMachineCodeInterpreterPrimitive: (self cCoerceSimple: (coInterpreter mcprimFunctionForPrimitiveIndex: primitiveIndex)
- 															to: 'void (*)(void)')].
- 
  	(primitiveRoutine = 0 "no primitive"
  	or: [primitiveRoutine = (self cCoerceSimple: #primitiveFail to: 'void (*)(void)')]) ifTrue:
  		[^self genFastPrimFail].
+ 
+ 	(flags anyMask: PrimCallOnSmalltalkStack) ifTrue:
+ 		[self deny: ((flags anyMask: FastCPrimitiveUseCABIFlag) and: [flags anyMask: PrimCallOnSmalltalkStackAlign2x]).
+ 		(flags anyMask: FastCPrimitiveUseCABIFlag) ifTrue:
+ 			[^self compileMachineCodeInterpreterPrimitive: (self cCoerceSimple: (coInterpreter mcprimFunctionForPrimitiveIndex: primitiveIndex)
+ 															to: 'void (*)(void)')].
+ 		^self compileOnStackExternalPrimitive: primitiveRoutine flags: flags].
  	minValidCallAddress := minValidCallAddress min: primitiveRoutine asUnsignedInteger.
  	^self compileInterpreterPrimitive: primitiveRoutine flags: flags!

Item was changed:
  ----- Method: SpurMemoryManager>>memcpy:_:_: (in category 'simulation') -----
  memcpy: destAddress _: sourceAddress _: bytes
  	"For SpurGenerationScavenger>>copyToFutureSpace:bytes:. N.B. If ranges overlap, must use memmove."
  	<doNotGenerate>
  	| nToCopy offset |
  	(destAddress isInteger and: [sourceAddress isInteger]) ifFalse: "CogMethodProxies..."
+ 		[(sourceAddress isCollection "String, etc..."
+ 		 or: [sourceAddress isCArray
+ 		 or: [destAddress isCArray]]) ifTrue: 
- 		[sourceAddress isCollection ifTrue: "CArray, String, etc..."
  			[^super memcpy: destAddress _: sourceAddress _: bytes].
  		 ^self memcpy: destAddress asInteger _: sourceAddress asInteger _: bytes].
  	self deny: ((destAddress <= sourceAddress and: [destAddress + bytes > sourceAddress])
  				or: [sourceAddress <= destAddress and: [sourceAddress + bytes > destAddress]]).
  	self assert: (destAddress \\ 8) + (sourceAddress \\ 8) = 0. "for now..."
  	nToCopy := bytes.
  	offset := 0.
  	memory bytesPerElement = 8 ifTrue:
  		[0 to: nToCopy - 8 by: 8 do:
  			[:i| self long64At: destAddress + i put: (self long64At: sourceAddress + i)].
  		 offset := nToCopy - (nToCopy \\ 8).
  		 nToCopy := nToCopy \\ 8].
  	nToCopy >= 4 ifTrue:
  		[0 to: nToCopy - 4 by: 4 do:
  			[:i| self long32At: destAddress + i + offset put: (self long32At: sourceAddress + i + offset)].
  		 offset := offset + nToCopy - (nToCopy \\ 4).
  		 nToCopy := nToCopy \\ 4].
  	0 to: nToCopy - 1 do:
  		[:i| self byteAt: destAddress + i + offset put: (self byteAt: sourceAddress + i + offset)].
  	^destAddress!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>primitiveFFIDoubleAt (in category 'primitives') -----
  primitiveFFIDoubleAt
  	"Answer a 64-bit IEEE double the given byte offset."
+ 	<export: true flags: #(FastCPrimitiveFlag FastCPrimitiveAlignForFloatsFlag)>
- 	<export: true flags: #FastCPrimitiveFlag>
  	| byteOffset rcvr floatValue |
  	<var: #floatValue type: #double>
  	byteOffset := interpreterProxy stackValue: 0.
  	rcvr := interpreterProxy stackValue: 1.
  	(interpreterProxy isIntegerObject: byteOffset) ifFalse:
  		[^interpreterProxy primitiveFailFor: PrimErrBadArgument].
  	(self ffiAddressOf: rcvr startingAt: (interpreterProxy integerValueOf: byteOffset) size: (self sizeof: #double))
  		ifNil: [^interpreterProxy primitiveFailFor: PrimErrBadIndex]
  		ifNotNil:
  			[:addr|
  			self memcpy: (self addressOf: floatValue) _: addr _: (self sizeof: #double).
  			^interpreterProxy methodReturnFloat: floatValue]!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>primitiveFFIDoubleAtPut (in category 'primitives') -----
  primitiveFFIDoubleAtPut
  	"Store a 64-bit IEEE double the given byte offset."
+ 	<export: true flags: #(FastCPrimitiveFlag FastCPrimitiveAlignForFloatsFlag)>
- 	<export: true flags: #FastCPrimitiveFlag>
  	| byteOffset rcvr floatValue valueOop |
  	<var: #floatValue type: #double>
  	valueOop := interpreterProxy stackValue: 0.
  	(interpreterProxy isFloatObject: valueOop)
  		ifTrue: [floatValue := self cCoerce: (interpreterProxy floatValueOf: valueOop) to: #double]
  		ifFalse:
  			[(interpreterProxy isIntegerObject: valueOop)
  				ifTrue: [floatValue := self cCoerce: (interpreterProxy integerValueOf: valueOop) to: #double]
  				ifFalse: [^interpreterProxy primitiveFailFor: PrimErrBadArgument]].
  	byteOffset := interpreterProxy stackValue: 1.
  	(interpreterProxy isIntegerObject: byteOffset) ifFalse:
  		[^interpreterProxy primitiveFailFor: PrimErrBadArgument].
  	rcvr := interpreterProxy stackValue: 2.
  	(self ffiAddressOf: rcvr startingAt: (interpreterProxy integerValueOf: byteOffset) size: (self sizeof: #double))
  		ifNil: [^interpreterProxy primitiveFailFor: PrimErrBadIndex]
  		ifNotNil:
  			[:addr|
+ 			self memcpy: addr _: (self addressOf: floatValue put: [:v| floatValue := Float fromIEEE64Bit: v]) _: (self sizeof: #double).
- 			self memcpy: addr _: (self addressOf: floatValue) _: (self sizeof: floatValue).
  			^interpreterProxy methodReturnValue: valueOop]!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>primitiveFFIFloatAt (in category 'primitives') -----
  primitiveFFIFloatAt
  	"Answer a 32-bit IEEE float the given byte offset."
+ 	<export: true flags: #(FastCPrimitiveFlag FastCPrimitiveAlignForFloatsFlag)>
- 	<export: true flags: #FastCPrimitiveFlag>
  	| byteOffset rcvr floatValue |
  	<var: #floatValue type: #float>
  	byteOffset := interpreterProxy stackValue: 0.
  	rcvr := interpreterProxy stackValue: 1.
  	(interpreterProxy isIntegerObject: byteOffset) ifFalse:
  		[^interpreterProxy primitiveFailFor: PrimErrBadArgument].
  	(self ffiAddressOf: rcvr startingAt: (interpreterProxy integerValueOf: byteOffset) size: (self sizeof: #float))
  		ifNil: [^interpreterProxy primitiveFailFor: PrimErrBadIndex]
  		ifNotNil:
  			[:addr|
+ 			self memcpy: (self addressOf: floatValue put: [:v| floatValue := Float fromIEEE32Bit: v]) _: addr _: (self sizeof: #float).
- 			self memcpy: (self addressOf: floatValue) _: addr _: (self sizeof: #float).
  			^interpreterProxy methodReturnFloat: floatValue]!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>primitiveFFIFloatAtPut (in category 'primitives') -----
  primitiveFFIFloatAtPut
  	"Store a 32-bit IEEE float the given byte offset."
+ 	<export: true flags: #(FastCPrimitiveFlag FastCPrimitiveAlignForFloatsFlag)>
- 	<export: true flags: #FastCPrimitiveFlag>
  	| byteOffset rcvr floatValue valueOop |
  	<var: #floatValue type: #float>
  	valueOop := interpreterProxy stackValue: 0.
  	(interpreterProxy isFloatObject: valueOop)
  		ifTrue: [floatValue := self cCoerce: (interpreterProxy floatValueOf: valueOop) to: #float]
  		ifFalse:
  			[(interpreterProxy isIntegerObject: valueOop)
  				ifTrue: [floatValue := self cCoerce: (interpreterProxy integerValueOf: valueOop) to: #float]
  				ifFalse: [^interpreterProxy primitiveFailFor: PrimErrBadArgument]].
  	byteOffset := interpreterProxy stackValue: 1.
  	(interpreterProxy isIntegerObject: byteOffset) ifFalse:
  		[^interpreterProxy primitiveFailFor: PrimErrBadArgument].
  	rcvr := interpreterProxy stackValue: 2.
  	(self ffiAddressOf: rcvr startingAt: (interpreterProxy integerValueOf: byteOffset) size: (self sizeof: #float))
  		ifNil: [^interpreterProxy primitiveFailFor: PrimErrBadIndex]
  		ifNotNil:
  			[:addr|
  			self memcpy: addr _: (self addressOf: floatValue) _: (self sizeof: floatValue).
  			^interpreterProxy methodReturnValue: valueOop]!

Item was changed:
  SharedPool subclass: #VMBasicConstants
  	instanceVariableNames: ''
+ 	classVariableNames: 'BaseHeaderSize BytecodeSetHasExtensions BytesPerOop BytesPerWord COGMTVM COGVM CloneOnGC CloneOnScavenge DisownVMForFFICall DisownVMForThreading DoAssertionChecks DoExpensiveAssertionChecks FastCPrimitiveAlignForFloatsFlag FastCPrimitiveFlag FastCPrimitiveUseCABIFlag GCCheckFreeSpace GCCheckImageSegment GCCheckPrimCall GCCheckShorten GCModeBecome GCModeFull GCModeIncremental GCModeNewSpace HashMultiplyConstant HashMultiplyMask IMMUTABILITY LowcodeVM MULTIPLEBYTECODESETS NewspeakVM PharoVM PrimErrBadArgument PrimErrBadIndex PrimErrBadMethod PrimErrBadNumArgs PrimErrBadReceiver PrimErrCallbackError PrimErrFFIException PrimErrGenericFailure PrimErrInappropriate PrimErrInternalError PrimErrLimitExceeded PrimErrNamedInternal PrimErrNeedCompaction PrimErrNoCMemory PrimErrNoMemory PrimErrNoModification PrimErrNotFound PrimErrOSError PrimErrObjectIsPinned PrimErrObjectMayMove PrimErrObjectMoved PrimErrObjectNotPinned PrimErrOperationFailed PrimErrUninitialized PrimErr
 Unsupported PrimErrWritePastObject PrimNoErr PrimNumberHandlerMarker PrimNumberNoContextSwitchMarker PrimNumberUnwindMarker SPURVM STACKVM SistaVM TempVectReadBarrier VMBIGENDIAN'
- 	classVariableNames: 'BaseHeaderSize BytecodeSetHasExtensions BytesPerOop BytesPerWord COGMTVM COGVM CloneOnGC CloneOnScavenge DisownVMForFFICall DisownVMForThreading DoAssertionChecks DoExpensiveAssertionChecks FastCPrimitiveFlag GCCheckFreeSpace GCCheckImageSegment GCCheckPrimCall GCCheckShorten GCModeBecome GCModeFull GCModeIncremental GCModeNewSpace HashMultiplyConstant HashMultiplyMask IMMUTABILITY LowcodeVM MULTIPLEBYTECODESETS NewspeakVM PharoVM PrimErrBadArgument PrimErrBadIndex PrimErrBadMethod PrimErrBadNumArgs PrimErrBadReceiver PrimErrCallbackError PrimErrFFIException PrimErrGenericFailure PrimErrInappropriate PrimErrInternalError PrimErrLimitExceeded PrimErrNamedInternal PrimErrNeedCompaction PrimErrNoCMemory PrimErrNoMemory PrimErrNoModification PrimErrNotFound PrimErrOSError PrimErrObjectIsPinned PrimErrObjectMayMove PrimErrObjectMoved PrimErrObjectNotPinned PrimErrOperationFailed PrimErrUninitialized PrimErrUnsupported PrimErrWritePastObject PrimNoErr PrimNumberHand
 lerMarker PrimNumberNoContextSwitchMarker PrimNumberUnwindMarker SPURVM STACKVM SistaVM TempVectReadBarrier VMBIGENDIAN'
  	poolDictionaries: ''
  	category: 'VMMaker-Interpreter'!
  
  !VMBasicConstants commentStamp: '<historical>' prior: 0!
  I am a shared pool for basic constants upon which the VM as a whole depends.
  
  self ensureClassPool.
  self classPool declare: #BytesPerWord from: VMSqueakV3ObjectRepresentationConstants classPool.
  self classPool declare: #BaseHeaderSize from: VMSqueakV3ObjectRepresentationConstants classPool
  (ObjectMemory classPool keys select: [:k| k beginsWith: 'Byte']) do:
  	[:k| self classPool declare: k from: ObjectMemory classPool]!

Item was changed:
  ----- Method: VMClass>>addressOf:put: (in category 'translation support') -----
  addressOf: anObject put: aBlock
  	<doNotGenerate>
  	"Simulate a C pointer.  Translates into &anObject in C. Provides something
  	 that evaluates aBlock with the new value in response to at:put:"
+ 	| thing firstIndex things |
- 	| thing |
  	thing := anObject.
  	^CPluggableAccessor new
  		setObject: nil;
  		atBlock: [:obj :idx| thing]
+ 		atPutBlock:
+ 			[:obj :idx :val|
+ 			"Handle the common case of a single assignment as gracefully as possible"
+ 			(firstIndex isNil or: [firstIndex = idx])
+ 				ifTrue: [firstIndex := idx. thing := val]
+ 				ifFalse: "handle the rare case of multiple writes through the pointer, a multi-byte assignment from memcpy:_:_: et al"
+ 					[self assert: (#(#'memcpy:_:_:' #'memmove:_:_:' #'strncpy:_:_:') includes: thisContext sender sender selector).
+ 					 things ifNil: [things := OrderedCollection with: thing].
+ 					 self assert: things size + firstIndex = idx.
+ 					 things addLast: val.
+ 					 thing := (self objectMemory endianness == #little
+ 									ifTrue: [things]
+ 									ifFalse: [things reverse]) inject: 0 into: [:word :byte| (word bitShift: 8) + byte]].
+ 			aBlock value: thing]!
- 		atPutBlock: [:obj :idx :val| aBlock value: (thing := val)]!



More information about the Vm-dev mailing list