Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3239.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3239
Author: eem
Time: 2 August 2022, 11:38:13.454341 am
UUID: 0e58a24c-1ebf-4b6d-b796-1ee9681e3f32
Ancestors: VMMaker.oscog-eem.3238
ThreadedFFIPlugin:
Add __riscv et al to the set of names defined at compile time.
=============== Diff against VMMaker.oscog-eem.3238 ===============
Item was changed:
----- Method: VMBasicConstants class>>namesDefinedAtCompileTime (in category 'C translation') -----
namesDefinedAtCompileTime
"Answer the set of names for variables that should be defined at compile time.
Some of these get default values during simulation, and hence get defaulted in
the various initializeMiscConstants methods. But that they have values should
/not/ cause the code generator to do dead code elimination based on their
default values. In particular, methods marked with <option: ANameDefinedAtCompileTime>
will be emitted within #if defined(ANameDefinedAtCompileTime)...#endif.
And of course this is backwards. We'd like to define names that are defined at translation time.
But doing so would entail defining (or referencing) hundreds of class and pool variables. This way
+ is marginally more manageable."
- is more manageable"
^#(VMBIGENDIAN
IMMUTABILITY
STACKVM COGVM COGMTVM SPURVM
PharoVM "Pharo vs Squeak"
TerfVM VM_TICKER "Terf vs Squeak & Qwaq/Teleplace/Terf high-priority thread support"
EnforceAccessControl "Newspeak"
CheckRememberedInTrampoline "IMMUTABILITY"
BIT_IDENTICAL_FLOATING_POINT PLATFORM_SPECIFIC_FLOATING_POINT "Alternatives for using fdlibm for floating-point"
ITIMER_HEARTBEAT "older linux's woultn't allow a higher priority thread, hence no threaded heartbeat."
TestingPrimitives
OBSOLETE_ALIEN_PRIMITIVES "Ancient crap in the IA32ABI plugin"
LLDB "As of lldb-370.0.42 Swift-3.1, passing function parameters to printOopsSuchThat fails with Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call. Turning off link time optimization with -fno-lto has no effect. hence we define some debugging functions as being <option: LLDB>"
LRPCheck "Optional checking for long running primitives"
"ThreadedFFIPlugin related"
ALLOCA_LIES_SO_SETSP_BEFORE_CALL PLATFORM_API_USES_CALLEE_POPS_CONVENTION SQUEAK_BUILTIN_PLUGIN STACK_ALIGN_BYTES
"processor related"
+ __ARM_ARCH__ __arm__ __arm32__ ARM32
+ __arm64__ __arm64 __aarch64__ ARM64
- __ARM_ARCH__ __arm__ __arm32__ ARM32 __arm64__ ARM64
- _M_I386 _X86_ i386 i486 i586 i686 __i386__ __386__ X86 I386
- x86_64 __amd64 __x86_64 __amd64__ __x86_64__ _M_AMD64 _M_X64
-
__mips__ __mips
__powerpc __powerpc__ __powerpc64__ __POWERPC__
__ppc__ __ppc64__ __PPC__ __PPC64__
+ __riscv__ __riscv64__ __riscv __riscv64
__sparc__ __sparc __sparc_v8__ __sparc_v9__ __sparcv8 __sparcv9
+ _M_I386 _X86_ i386 i486 i586 i686 __i386__ __386__ X86 I386
+ x86_64 __amd64 __x86_64 __amd64__ __x86_64__ _M_AMD64 _M_X64
"Compiler brand related"
-
__ACK__
__CC_ARM
__clang__
__GNUC__
_MSC_VER
__ICC
-
__SUNPRO_C
"os related"
ACORN
-
_AIX
__ANDROID__
__APPLE__
__BEOS__
+ EPLAN9
+ __FreeBSD__ __NetBSD__ __OpenBSD__
__linux__
__MACH__
__MINGW32__
- __FreeBSD__ __NetBSD__ __OpenBSD__
__osf__
-
- EPLAN9
__unix__ __unix UNIX
WIN32 _WIN32 _WIN32_WCE
WIN64 _WIN64 _WIN64_WCE)!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3238.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3238
Author: eem
Time: 2 August 2022, 11:19:29.444062 am
UUID: 4b576e04-5ba8-4fc4-93c9-efdc751e9877
Ancestors: VMMaker.oscog-eem.3237
...close, but no cigar.
=============== Diff against VMMaker.oscog-eem.3237 ===============
Item was changed:
----- Method: CCodeGenerator>>addMethodFor:selector: (in category 'utilities') -----
addMethodFor: aClass selector: selector
"Add the given method to the code base and answer its translation
or nil if it shouldn't be translated."
| method tmethod |
method := aClass compiledMethodAt: selector.
(method pragmaAt: #doNotGenerate) ifNotNil:
["only remove a previous method if this one overrides it, i.e. this is a subclass method.
If the existing method is in a different hierarchy this method must be merely a redeirect."
(methods at: selector ifAbsent: []) ifNotNil:
[:tm|
(aClass includesBehavior: tm definingClass) ifTrue:
[self removeMethodForSelector: selector]].
^nil].
method isSubclassResponsibility ifTrue:
[^nil].
(self shouldIncludeMethodFor: aClass selector: selector) ifFalse:
[^nil].
tmethod := self compileToTMethodSelector: selector in: aClass.
"Even though we exclude initialize methods, we must consider their
global variable usage, otherwise globals may be incorrectly localized."
selector == #initialize ifTrue:
[self checkForGlobalUsage: (tmethod allReferencedVariablesUsing: self) in: tmethod.
^nil].
self addMethod: tmethod.
"If the method has a macro then add the macro. But keep the method
for analysis purposes (e.g. its variable accesses)."
(method pragmaAt: #cmacro:) ifNotNil:
[:pragma|
self addMacro: (pragma argumentAt: 1) for: selector.
(inlineList includes: selector) ifTrue:
[inlineList := inlineList copyWithout: selector]].
(method pragmaAt: #cmacro) ifNotNil:
[:pragma| | literal | "Method should be just foo ^const"
self assert: (self isValidMacroMethod: method).
+ literal := (method isQuick or: [method numArgs = 1])
- literal := method isQuick
ifTrue: [method decompile quickMethodReturnLiteral]
ifFalse: [method literalAt: 1].
self addMacro: '() ', (method isReturnField
ifTrue: [literal]
ifFalse: [self cLiteralFor: literal value name: method selector]) for: selector.
(inlineList includes: selector) ifTrue:
[inlineList := inlineList copyWithout: selector]].
^tmethod!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3236.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3236
Author: eem
Time: 2 August 2022, 10:44:55.758739 am
UUID: 84f6abb0-16f4-4566-9af4-1271286f3061
Ancestors: VMMaker.oscog-eem.3235
...and the same for ObjectMemory>>isForwarded:
=============== Diff against VMMaker.oscog-eem.3235 ===============
Item was changed:
----- Method: ObjectMemory>>isForwarded: (in category 'interpreter access') -----
isForwarded: oop
+ "Compatibility wth SpurMemoryManager. In ObjectMemory,
+ no forwarding pointers are visible to the execution engine."
- "Compatibility wth SpurMemoryManager. In ObjectMemory, no forwarding pointers
- are visible to the VM."
<api>
+ <cmacro>
+ <inline: #always>
- <cmacro: '(oop) false'>
- <inline: true>
^false!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3235.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3235
Author: eem
Time: 2 August 2022, 10:37:25.594485 am
UUID: 9592c817-2a86-489b-abdf-60838525c8ab
Ancestors: VMMaker.oscog-eem.3234
...and hence redefine numRegArgs to use cmacro, not cmacro:.
=============== Diff against VMMaker.oscog-eem.3234 ===============
Item was changed:
----- Method: CogObjectRepresentationForSpur>>numRegArgs (in category 'calling convention') -----
numRegArgs
"Define how many register arguments a StackToRegisterMappingCogit can
and should use with the receiver. The value must be 0, 1 or 2. Note that a
SimpleStackBasedCogit always has 0 register args (although the receiver is
passed in a register). The Spur object representation is simple enough that
implementing at:put: is straight-forward and hence 2 register args are worth
while. The method must be inlined in CoInterpreter, and dead code eliminated
so that the register-popping enilopmarts such as enterRegisterArgCogMethod:-
at:receiver: do not have to be implemented in SimpleStackBasedCogit."
<api>
<option: #StackToRegisterMappingCogit>
+ <cmacro> "to allow inlining optimization across the CoInterpreter/Cogit api..."
- <cmacro: '() 2'> "to allow inlining optimization across the CoInterpreter/Cogit api..."
^2!
Item was changed:
----- Method: CogObjectRepresentationForSqueakV3>>numRegArgs (in category 'calling convention') -----
numRegArgs
"Define how many register arguments a StackToRegisterMappingCogit can
and should use with the receiver. The value must be 0, 1 or 2. Note that a
SimpleStackBasedCogit always has 0 register args (although the receiver is
passed in a register). CogObjectRepresentationForSqueakV3 only implements
at most 1-arg primitives, because the complexity of the object representation
makes it difficult to implement at:put:, the most performance-critical 2-argument
primitive.. The method must be inlined in CoInterpreter, and dead code eliminated
so that the register-popping enilopmarts such as enterRegisterArgCogMethod:-
at:receiver: do not have to be implemented in SimpleStackBasedCogit."
<api>
<option: #StackToRegisterMappingCogit>
+ <cmacro> "to allow inlining optimization across the CoInterpreter/Cogit api..."
- <cmacro: '() 1'> "to allow inlining optimization across the CoInterpreter/Cogit api..."
^1!
Item was changed:
----- Method: SimpleStackBasedCogit>>numRegArgs (in category 'testing') -----
numRegArgs
<api>
+ <cmacro> "to allow inlining optimization across the CoInterpreter/Cogit api..."
- <cmacro: '() 0'> "to allow inlining optimization across the CoInterpreter/Cogit api..."
^0!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3234.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3234
Author: eem
Time: 2 August 2022, 10:26:11.98735 am
UUID: 32fdb3bb-ea3a-4a14-9755-2ad589a8d3c2
Ancestors: VMMaker.oscog-eem.3233
Slang:
Fix an inlining regression in VMMaker.oscog-eem.3233. We still want to inline methods marked as macros for inlining across the cointerp/cogit boundary (e.g. shiftForWord). So distinguish between cmacro: methods (no inlining) and cmacro methods (inlining is fine).
=============== Diff against VMMaker.oscog-eem.3233 ===============
Item was changed:
----- Method: CCodeGenerator>>isConstantNode:valueInto: (in category 'utilities') -----
isConstantNode: aNode valueInto: aBlock
"Answer if aNode evaluates to a constant, and if so, evaluate aBlock with the value of that constant."
aNode isConstant ifTrue:
[(aNode isDefine
and: [self defineAtCompileTime: aNode name]) ifTrue:
[^false].
aBlock value: aNode value.
^true].
(aNode isVariable
and: [aNode name = #nil]) ifTrue:
[aBlock value: nil.
^true].
aNode isSend ifTrue:
[(self anyMethodNamed: aNode selector)
ifNil:
[(VMBasicConstants valueOfBasicSelector: aNode selector) ifNotNil:
[:value|
aBlock value: value.
^true].
aNode constantNumbericValueOrNil ifNotNil:
[:value|
aBlock value: value.
^true]]
ifNotNil:
[:m|
+ (m isMacroWithDefinition not
- (m definedAsMacro not
and: [m statements size = 1
and: [m statements last isReturn]]) ifTrue:
[^self isConstantNode: m statements last expression valueInto: aBlock]]].
^false!
Item was changed:
----- Method: CCodeGenerator>>isMacroSelector: (in category 'utilities') -----
isMacroSelector: sel
+ "Answer if the given selector is one of the selectors implemented as a macro in platform header files."
- "Answer if the given selector is one of the selectors implemented as a macro in platform header fiels."
^(self isKernelSelector: sel)
or: [(VMBasicConstants mostBasicConstantSelectors includes: sel)
or: [(self methodNamed: sel)
ifNil: [false]
ifNotNil: [:m| m definedAsMacro]]]!
Item was added:
+ ----- Method: TMethod>>isMacroWithDefinition (in category 'testing') -----
+ isMacroWithDefinition
+ "Answer if the method has a macro that has a definition, rather than being defined as a macro.
+ c.f. definedAsMacro.
+ Certain api methods are defined as cmacro (isMacroWithDefinition is false) so that their value can be defined,
+ e.g. shiftForWord. Many others are defined as macros proper, with a defintiion. The former is simply for
+ optimization and does not imply we want to disable inlining based on them. But we *do* want to disable
+ inlining of methods that are isMacroWithDefinition. hence the distinction."
+ ^properties notNil
+ and: [(properties includesKey: #cmacro:)]!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3232.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3232
Author: eem
Time: 2 August 2022, 9:40:11.234938 am
UUID: 054d025b-2745-4f1f-8a8b-af032b6c9841
Ancestors: VMMaker.oscog-eem.3231
ThreadedFFIPlugins:
Do inline ffiCall:ArgArrayOrNil:NumArgs:. We should err on teh side of speed. The code size shouldn't affect icache performance because the primitiveCalloutWithArgs path is used very rarely in practice.
Have all plugins assign BytesPerWord in their initialize methods, to inline positiveMachineIntegerFor: et al. Override InterpreterPlugin class>>#shouldGenerateDeadCode since these are very platform-specific and the dead code decision well under control.
Slang: fix isConstantNode:valueInto: to not be fooled by/ignore the cmacro part of macro methods.
Initialize generateDeadCode in VMPluginCodeGenerator>>#pluginClass:
=============== Diff against VMMaker.oscog-eem.3231 ===============
Item was changed:
----- Method: CCodeGenerator>>isConstantNode:valueInto: (in category 'utilities') -----
isConstantNode: aNode valueInto: aBlock
"Answer if aNode evaluates to a constant, and if so, evaluate aBlock with the value of that constant."
aNode isConstant ifTrue:
[(aNode isDefine
and: [(vmClass ifNil: [VMBasicConstants]) defineAtCompileTime: aNode name]) ifTrue:
[^false].
aBlock value: aNode value.
^true].
(aNode isVariable
and: [aNode name = #nil]) ifTrue:
[aBlock value: nil.
^true].
aNode isSend ifTrue:
[(self anyMethodNamed: aNode selector)
ifNil:
[(VMBasicConstants valueOfBasicSelector: aNode selector) ifNotNil:
[:value|
aBlock value: value.
^true].
aNode constantNumbericValueOrNil ifNotNil:
[:value|
aBlock value: value.
^true]]
ifNotNil:
[:m|
+ (m definedAsMacro not
+ and: [m statements size = 1
+ and: [m statements last isReturn]]) ifTrue:
- (m statements size = 1
- and: [m statements last isReturn]) ifTrue:
[^self isConstantNode: m statements last expression valueInto: aBlock]]].
^false!
Item was changed:
----- Method: ThreadedARM32FFIPlugin class>>initialize (in category 'class initialization') -----
initialize
super initialize.
+ BytesPerWord := 4.
NumIntRegArgs := 4.
NumFloatRegArgs := 16!
Item was changed:
----- Method: ThreadedARM64FFIPlugin class>>initialize (in category 'class initialization') -----
initialize
super initialize.
+ BytesPerWord := 8.
NumIntRegArgs := 8.
NumFloatRegArgs := 8!
Item was changed:
----- Method: ThreadedFFIPlugin class>>initialize (in category 'class initialization') -----
initialize
"c.f. ExternalFunction allInstVarNames
old: #('handle' 'flags' 'argTypes')
new: #('handle' 'flags' 'argTypes' 'stackSize')"
ExternalFunctionAddressIndex := 0.
ExternalFunctionFlagsIndex := 1.
ExternalFunctionArgTypesIndex := 2.
ExternalFunctionStackSizeIndex := 3.
"c.f. e.g. CoInterpreter class initializeMiscConstants"
MaxNumArgs := 15.
DefaultMaxStackSize := 1024 * 16.
PluginVersionInfo := CCodeGenerator shortMonticelloDescriptionForClass: self.
+ PluginVersionInfo := PluginVersionInfo allButFirst: (PluginVersionInfo indexOf: Character space) - 1.
+
+ BytesPerWord := #subclassResponsibility "i.e. every subclass must define this explicitly"!
- PluginVersionInfo := PluginVersionInfo allButFirst: (PluginVersionInfo indexOf: Character space) - 1!
Item was added:
+ ----- Method: ThreadedFFIPlugin class>>shouldGenerateDeadCode (in category 'translation') -----
+ shouldGenerateDeadCode
+ "Answer if the code generator should generate dead code, e.g. in false ifTrue: [dead] ifFalse: [live].
+ Since plugin source is shared between different VM builds it is unsafe to assume any code is dead.
+ However, theThreadedFFIPlugin is much more platform-specific and we've done a lot of work on
+ inlining, so code which depends on defined-at-compiled-time constants should not be considered
+ dead. So..."
+
+ ^false!
Item was changed:
----- Method: ThreadedFFIPlugin>>ffiCall:ArgArrayOrNil:NumArgs: (in category 'callout support') -----
ffiCall: externalFunction ArgArrayOrNil: argArrayOrNil NumArgs: nArgs
"Generic callout. Does the actual work. If argArrayOrNil is nil it takes args from the stack
and the spec from the method. If argArrayOrNil is not nil takes args from argArrayOrNil
and the spec from the receiver."
| flags argTypeArray address argType oop argSpec argClass err theCalloutState calloutState requiredStackSize stackSize allocation result primNumArgs |
+ <inline: #always>
- <inline: false>
<var: #theCalloutState type: #'CalloutState'>
<var: #calloutState type: #'CalloutState *'>
<var: #allocation type: #'char *'>
primNumArgs := interpreterProxy methodArgumentCount.
(interpreterProxy is: externalFunction KindOfClass: interpreterProxy classExternalFunction) ifFalse:
[^self ffiFail: FFIErrorNotFunction].
"Load and check the values in the externalFunction before we call out"
flags := interpreterProxy fetchInteger: ExternalFunctionFlagsIndex ofObject: externalFunction.
interpreterProxy failed ifTrue:
[^self ffiFail: FFIErrorBadArgs].
"This must come early for compatibility with the old FFIPlugin. Image-level code
may assume the function pointer is loaded eagerly. Thanks to Nicolas Cellier."
address := self ffiLoadCalloutAddress: externalFunction.
interpreterProxy failed ifTrue:
[^0 "error code already set by ffiLoadCalloutAddress:"].
argTypeArray := interpreterProxy fetchPointer: ExternalFunctionArgTypesIndex ofObject: externalFunction.
"must be array of arg types"
((interpreterProxy isArray: argTypeArray)
and: [(interpreterProxy slotSizeOf: argTypeArray) = (nArgs + 1)]) ifFalse:
[^self ffiFail: FFIErrorBadArgs].
"check if the calling convention is supported"
self cppIf: COGMTVM
ifTrue:
[(self ffiSupportsCallingConvention: (flags bitAnd: FFICallTypesMask)) ifFalse:
[^self ffiFail: FFIErrorCallType]]
ifFalse: "not masking causes threaded calls to fail, which is as they should if the plugin is not threaded."
[(self ffiSupportsCallingConvention: flags) ifFalse:
[^self ffiFail: FFIErrorCallType]].
requiredStackSize := self externalFunctionHasStackSizeSlot
ifTrue: [interpreterProxy
fetchInteger: ExternalFunctionStackSizeIndex
ofObject: externalFunction]
ifFalse: [-1].
interpreterProxy failed ifTrue:
[^interpreterProxy primitiveFailFor: (argArrayOrNil isNil
ifTrue: [PrimErrBadMethod]
ifFalse: [PrimErrBadReceiver])].
stackSize := requiredStackSize < 0 ifTrue: [DefaultMaxStackSize] ifFalse: [requiredStackSize].
self cCode: [] inSmalltalk: [theCalloutState := self class calloutStateClass new].
calloutState := self addressOf: theCalloutState.
self cCode: [self memset: calloutState _: 0 _: (self sizeof: #CalloutState)].
calloutState callFlags: flags.
"Fetch return type and args"
argType := interpreterProxy fetchPointer: 0 ofObject: argTypeArray.
argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
(err := self ffiCheckReturn: argSpec With: argClass in: calloutState) ~= 0 ifTrue:
[^self ffiFail: err]. "cannot return"
"alloca the outgoing stack frame, leaving room for marshalling args, and including space for the return struct, if any.
Additional space reserved for saving register args like mandated by Win64 X64 or PPC ABI, will be managed by the call itself"
allocation := self alloca: stackSize + calloutState structReturnSize + self cStackAlignment.
self mustAlignStack ifTrue:
[allocation := self cCoerce: (allocation asUnsignedIntegerPtr bitClear: self cStackAlignment - 1) to: #'char *'].
calloutState
argVector: allocation;
currentArg: allocation;
limit: allocation + stackSize.
(self nonRegisterStructReturnIsViaImplicitFirstArgument
and: [calloutState structReturnSize > 0
and: [(self returnStructInRegisters: calloutState) not]]) ifTrue:
[err := self ffiPushPointer: calloutState limit in: calloutState.
err ~= 0 ifTrue:
[self cleanupCalloutState: calloutState.
self cppIf: COGMTVM ifTrue:
[err = PrimErrObjectMayMove negated ifTrue:
[^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
^self ffiFail: err]].
1 to: nArgs do:
[:i|
argType := interpreterProxy fetchPointer: i ofObject: argTypeArray.
argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
+ oop := argArrayOrNil
+ ifNil: [interpreterProxy stackValue: nArgs - i]
+ ifNotNil: [interpreterProxy fetchPointer: i - 1 ofObject: argArrayOrNil].
- oop := argArrayOrNil isNil
- ifTrue: [interpreterProxy stackValue: nArgs - i]
- ifFalse: [interpreterProxy fetchPointer: i - 1 ofObject: argArrayOrNil].
err := self ffiArgument: oop Spec: argSpec Class: argClass in: calloutState.
err ~= 0 ifTrue:
[self cleanupCalloutState: calloutState.
self cppIf: COGMTVM ifTrue:
[err = PrimErrObjectMayMove negated ifTrue:
[^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
^self ffiFail: err]]. "coercion failed or out of stack space"
"Failures must be reported back from ffiArgument:Spec:Class:in:.
Should not fail from here on in."
self assert: interpreterProxy failed not.
self ffiLogCallout: externalFunction.
(requiredStackSize < 0
and: [self externalFunctionHasStackSizeSlot]) ifTrue:
[stackSize := calloutState currentArg - calloutState argVector.
interpreterProxy storeInteger: ExternalFunctionStackSizeIndex ofObject: externalFunction withValue: stackSize].
"Go out and call this guy"
result := self ffiCalloutTo: address SpecOnStack: argArrayOrNil notNil in: calloutState.
self cleanupCalloutState: calloutState.
"Can not safely use argumentCount (via e.g. methodReturnValue:) since it may have been changed by a callback."
interpreterProxy pop: primNumArgs + 1 thenPush: result.
^result!
Item was added:
+ ----- Method: ThreadedIA32FFIPlugin class>>initialize (in category 'class initialization') -----
+ initialize
+ super initialize.
+ BytesPerWord := 4!
Item was removed:
- ----- Method: ThreadedRiscV64FFIPlugin class>>initialize (in category 'class initialization') -----
- initialize
- super initialize.
- NumIntRegArgs := 8.
- NumFloatRegArgs := 8!
Item was changed:
----- Method: ThreadedX64FFIPlugin class>>initialize (in category 'class initialization') -----
initialize
+ BytesPerWord := WordSize := 8.
- WordSize := 8.
NumIntRegArgs := 6.
NumFloatRegArgs := 8!
Item was changed:
----- Method: ThreadedX64SysVFFIPlugin class>>initialize (in category 'class initialization') -----
initialize
+ super initialize.
- WordSize := 8.
NumIntRegArgs := 6.
NumFloatRegArgs := 8!
Item was changed:
----- Method: ThreadedX64Win64FFIPlugin class>>initialize (in category 'class initialization') -----
initialize
+ super initialize.
- WordSize := 8.
NumIntRegArgs := 4.
NumFloatRegArgs := 4!
Item was changed:
----- Method: VMBasicConstants class>>namesDefinedAtCompileTime (in category 'C translation') -----
namesDefinedAtCompileTime
"Answer the set of names for variables that should be defined at compile time.
Some of these get default values during simulation, and hence get defaulted in
the various initializeMiscConstants methods. But that they have values should
/not/ cause the code generator to do dead code elimination based on their
default values. In particular, methods marked with <option: ANameDefinedAtCompileTime>
will be emitted within #if defined(ANameDefinedAtCompileTime)...#endif.
And of course this is backwards. We'd like to define names that are defined at translation time.
But doing so would entail defining (or referencing) hundreds of class and pool variables. This way
is more manageable"
^#(VMBIGENDIAN
IMMUTABILITY
STACKVM COGVM COGMTVM SPURVM
PharoVM "Pharo vs Squeak"
TerfVM VM_TICKER "Terf vs Squeak & Qwaq/Teleplace/Terf high-priority thread support"
EnforceAccessControl "Newspeak"
CheckRememberedInTrampoline "IMMUTABILITY"
BIT_IDENTICAL_FLOATING_POINT PLATFORM_SPECIFIC_FLOATING_POINT "Alternatives for using fdlibm for floating-point"
ITIMER_HEARTBEAT "older linux's woultn't allow a higher priority thread, hence no threaded heartbeat."
TestingPrimitives
OBSOLETE_ALIEN_PRIMITIVES "Ancient crap in the IA32ABI plugin"
LLDB "As of lldb-370.0.42 Swift-3.1, passing function parameters to printOopsSuchThat fails with Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call. Turning off link time optimization with -fno-lto has no effect. hence we define some debugging functions as being <option: LLDB>"
LRPCheck "Optional checking for long running primitives"
+ "ThreadedFFIPlugin related"
+ ALLOCA_LIES_SO_SETSP_BEFORE_CALL PLATFORM_API_USES_CALLEE_POPS_CONVENTION SQUEAK_BUILTIN_PLUGIN STACK_ALIGN_BYTES
+
"processor related"
__ARM_ARCH__ __arm__ __arm32__ ARM32 __arm64__ ARM64
_M_I386 _X86_ i386 i486 i586 i686 __i386__ __386__ X86 I386
x86_64 __amd64 __x86_64 __amd64__ __x86_64__ _M_AMD64 _M_X64
__mips__ __mips
__powerpc __powerpc__ __powerpc64__ __POWERPC__
__ppc__ __ppc64__ __PPC__ __PPC64__
__sparc__ __sparc __sparc_v8__ __sparc_v9__ __sparcv8 __sparcv9
"Compiler brand related"
__ACK__
__CC_ARM
__clang__
__GNUC__
_MSC_VER
__ICC
__SUNPRO_C
"os related"
ACORN
_AIX
__ANDROID__
__APPLE__
__BEOS__
__linux__
__MACH__
__MINGW32__
__FreeBSD__ __NetBSD__ __OpenBSD__
__osf__
EPLAN9
__unix__ __unix UNIX
WIN32 _WIN32 _WIN32_WCE
WIN64 _WIN64 _WIN64_WCE)!
Item was changed:
----- Method: VMPluginCodeGenerator>>pluginClass: (in category 'public') -----
pluginClass: aPluginClass
"Set the plugin class and name when generating plugins.
And for run-time use, answer the name string."
| packageId |
pluginClass := aPluginClass.
+ aPluginClass ifNotNil:
+ [generateDeadCode := aPluginClass shouldGenerateDeadCode].
packageId := self shortMonticelloDescriptionForClass: pluginClass.
(packageId beginsWith: pluginClass name) ifTrue:
[packageId := packageId allButFirst: pluginClass name size].
(packageId beginsWith: pluginClass moduleName) ifTrue:
[packageId := packageId allButFirst: pluginClass moduleName size].
^self declareModuleName: pluginClass moduleNameAndVersion, packageId!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3231.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3231
Author: eem
Time: 2 August 2022, 8:32:42.653469 am
UUID: d0507db9-2a9c-40e6-9274-8e05bf0a2483
Ancestors: VMMaker.oscog-eem.3230
Fix nonRegisterStructReturnIsViaImplicitFirstArgument for ThreadedRiscV64FFIPlugin. Make it inline always on all of them, along with a few others.
Delete the ARMv8 and RISCV versions of ffiCall:ArgArrayOrNil:NumArgs:, which were added as a misunderstanding. Mark ffiCall:ArgArrayOrNil:NumArgs: as inline: false. It saves about 10k, sharing the code between primitiveCallout & primitiveCalloutWithArgs.
=============== Diff against VMMaker.oscog-eem.3230 ===============
Item was changed:
----- Method: InterpreterPlugin>>isAlien: (in category 'alien support') -----
isAlien: oop
"Answer if oop is an Alien. We could ask if isWordsOrBytes: first, but that doesn't help. We still have to do the is:KindOf: walk.
We're not interested in fast falsehood, but as fast as possible truth, and with the current API this is it."
+ <inline: #always>
- <inline: true>
^interpreterProxy is: oop KindOfClass: interpreterProxy classAlien!
Item was removed:
- ----- Method: ThreadedARM64FFIPlugin>>ffiCall:ArgArrayOrNil:NumArgs: (in category 'callout support') -----
- ffiCall: externalFunction ArgArrayOrNil: argArrayOrNil NumArgs: nArgs
- "Generic callout. Does the actual work. If argArrayOrNil is nil it takes args from the stack
- and the spec from the method. If argArrayOrNil is not nil takes args from argArrayOrNil
- and the spec from the receiver."
- | flags argTypeArray address argType oop argSpec argClass err theCalloutState calloutState requiredStackSize stackSize allocation result primNumArgs |
- <inline: false>
- <var: #theCalloutState type: #'CalloutState'>
- <var: #calloutState type: #'CalloutState *'>
- <var: #allocation type: #'char *'>
-
- primNumArgs := interpreterProxy methodArgumentCount.
- (interpreterProxy is: externalFunction KindOfClass: interpreterProxy classExternalFunction) ifFalse:
- [^self ffiFail: FFIErrorNotFunction].
- "Load and check the values in the externalFunction before we call out"
- flags := interpreterProxy fetchInteger: ExternalFunctionFlagsIndex ofObject: externalFunction.
- interpreterProxy failed ifTrue:
- [^self ffiFail: FFIErrorBadArgs].
-
- "This must come early for compatibility with the old FFIPlugin. Image-level code
- may assume the function pointer is loaded eagerly. Thanks to Nicolas Cellier."
- address := self ffiLoadCalloutAddress: externalFunction.
- interpreterProxy failed ifTrue:
- [^0 "error code already set by ffiLoadCalloutAddress:"].
-
- argTypeArray := interpreterProxy fetchPointer: ExternalFunctionArgTypesIndex ofObject: externalFunction.
- "must be array of arg types"
- ((interpreterProxy isArray: argTypeArray)
- and: [(interpreterProxy slotSizeOf: argTypeArray) = (nArgs + 1)]) ifFalse:
- [^self ffiFail: FFIErrorBadArgs].
- "check if the calling convention is supported"
- self cppIf: COGMTVM
- ifTrue:
- [(self ffiSupportsCallingConvention: (flags bitAnd: FFICallTypesMask)) ifFalse:
- [^self ffiFail: FFIErrorCallType]]
- ifFalse: "not masking causes threaded calls to fail, which is as they should if the plugin is not threaded."
- [(self ffiSupportsCallingConvention: flags) ifFalse:
- [^self ffiFail: FFIErrorCallType]].
-
- requiredStackSize := self externalFunctionHasStackSizeSlot
- ifTrue: [interpreterProxy
- fetchInteger: ExternalFunctionStackSizeIndex
- ofObject: externalFunction]
- ifFalse: [-1].
- interpreterProxy failed ifTrue:
- [^interpreterProxy primitiveFailFor: (argArrayOrNil isNil
- ifTrue: [PrimErrBadMethod]
- ifFalse: [PrimErrBadReceiver])].
- stackSize := requiredStackSize < 0 ifTrue: [DefaultMaxStackSize] ifFalse: [requiredStackSize].
- self cCode: [] inSmalltalk: [theCalloutState := self class calloutStateClass new].
- calloutState := self addressOf: theCalloutState.
- self cCode: [self memset: calloutState _: 0 _: (self sizeof: #CalloutState)].
- calloutState callFlags: flags.
- "Fetch return type and args"
- argType := interpreterProxy fetchPointer: 0 ofObject: argTypeArray.
- argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
- argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
- (err := self ffiCheckReturn: argSpec With: argClass in: calloutState) ~= 0 ifTrue:
- [^self ffiFail: err]. "cannot return"
- "alloca the outgoing stack frame, leaving room for marshalling args, and including space for the return struct, if any.
- Additional space reserved for saving register args like mandated by Win64 X64 or PPC ABI, will be managed by the call itself"
- allocation := self alloca: stackSize + calloutState structReturnSize + self cStackAlignment.
- self mustAlignStack ifTrue:
- [allocation := self cCoerce: (allocation asUnsignedIntegerPtr bitClear: self cStackAlignment - 1) to: #'char *'].
- calloutState
- argVector: allocation;
- currentArg: allocation;
- limit: allocation + stackSize.
- 1 to: nArgs do:
- [:i|
- argType := interpreterProxy fetchPointer: i ofObject: argTypeArray.
- argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
- argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
- oop := argArrayOrNil isNil
- ifTrue: [interpreterProxy stackValue: nArgs - i]
- ifFalse: [interpreterProxy fetchPointer: i - 1 ofObject: argArrayOrNil].
- err := self ffiArgument: oop Spec: argSpec Class: argClass in: calloutState.
- err ~= 0 ifTrue:
- [self cleanupCalloutState: calloutState.
- self cppIf: COGMTVM ifTrue:
- [err = PrimErrObjectMayMove negated ifTrue:
- [^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
- ^self ffiFail: err]]. "coercion failed or out of stack space"
- "Failures must be reported back from ffiArgument:Spec:Class:in:.
- Should not fail from here on in."
- self assert: interpreterProxy failed not.
- self ffiLogCallout: externalFunction.
- (requiredStackSize < 0
- and: [self externalFunctionHasStackSizeSlot]) ifTrue:
- [stackSize := calloutState currentArg - calloutState argVector.
- interpreterProxy storeInteger: ExternalFunctionStackSizeIndex ofObject: externalFunction withValue: stackSize].
- "Go out and call this guy"
- result := self ffiCalloutTo: address SpecOnStack: argArrayOrNil notNil in: calloutState.
- self cleanupCalloutState: calloutState.
- "Can not safely use argumentCount (via e.g. methodReturnValue:) since it may have been changed by a callback."
- interpreterProxy pop: primNumArgs + 1 thenPush: result.
- ^result!
Item was changed:
----- Method: ThreadedARM64FFIPlugin>>nonRegisterStructReturnIsViaImplicitFirstArgument (in category 'marshalling') -----
nonRegisterStructReturnIsViaImplicitFirstArgument
"Answer if a struct returned in memory is returned to the
+ referent of a pointer passed as an implicit first argument.
+ It almost always is, but isn't in ARMv8 and RISCV 64 ABIs."
+ <inline: #always>
- referent of a pointer passed as an implciit first argument.
- It almost always is. Subclasses can override if not."
^false!
Item was changed:
----- Method: ThreadedFFICalloutStateForX64>>incrementFloatRegisterIndex (in category 'accessing') -----
incrementFloatRegisterIndex
+ <inline: #always>
^floatRegisterIndex := floatRegisterIndex + 1!
Item was changed:
----- Method: ThreadedFFICalloutStateForX64>>incrementIntegerRegisterIndex (in category 'accessing') -----
incrementIntegerRegisterIndex
+ <inline: #always>
^integerRegisterIndex := integerRegisterIndex + 1!
Item was changed:
----- Method: ThreadedFFICalloutStateForX64Win64>>incrementFloatRegisterIndex (in category 'accessing') -----
incrementFloatRegisterIndex
"There are only 4 args passed by register int or float.
So we can't distinguish the float index from the integer index.
So we have to increment both.
Consequently, floatRegisterIndex cannot be used anymore to detect presence of float parameter.
However, we set a signature bitmap indicating which register position is used to pass a float.
IMPLEMENTATION NOTES:
There are code generator hacks that bypass the accessors.
So we cannot just redefine the method floatRegisterIndex as ^integerRegisterIndex.
Instead we must maintain the two indices"
+ <inline: #always>
floatRegisterSignature := floatRegisterSignature + (1 << floatRegisterIndex).
^integerRegisterIndex := floatRegisterIndex := floatRegisterIndex + 1!
Item was changed:
----- Method: ThreadedFFICalloutStateForX64Win64>>incrementIntegerRegisterIndex (in category 'accessing') -----
incrementIntegerRegisterIndex
"There are only 4 args passed by register int or float.
So we can't distinguish the float index from the integer index.
So we have to increment both.
IMPLEMENTATION NOTES:
There are code generator hacks that bypass the accessors.
So we cannot just redefine the method floatRegisterIndex as ^integerRegisterIndex.
Instead we must maintain the two indices"
+ <inline: #always>
^floatRegisterIndex := integerRegisterIndex := integerRegisterIndex + 1!
Item was changed:
----- Method: ThreadedFFIPlugin>>ffiCall:ArgArrayOrNil:NumArgs: (in category 'callout support') -----
ffiCall: externalFunction ArgArrayOrNil: argArrayOrNil NumArgs: nArgs
"Generic callout. Does the actual work. If argArrayOrNil is nil it takes args from the stack
and the spec from the method. If argArrayOrNil is not nil takes args from argArrayOrNil
and the spec from the receiver."
| flags argTypeArray address argType oop argSpec argClass err theCalloutState calloutState requiredStackSize stackSize allocation result primNumArgs |
+ <inline: false>
- <inline: true>
<var: #theCalloutState type: #'CalloutState'>
<var: #calloutState type: #'CalloutState *'>
<var: #allocation type: #'char *'>
primNumArgs := interpreterProxy methodArgumentCount.
(interpreterProxy is: externalFunction KindOfClass: interpreterProxy classExternalFunction) ifFalse:
[^self ffiFail: FFIErrorNotFunction].
"Load and check the values in the externalFunction before we call out"
flags := interpreterProxy fetchInteger: ExternalFunctionFlagsIndex ofObject: externalFunction.
interpreterProxy failed ifTrue:
[^self ffiFail: FFIErrorBadArgs].
"This must come early for compatibility with the old FFIPlugin. Image-level code
may assume the function pointer is loaded eagerly. Thanks to Nicolas Cellier."
address := self ffiLoadCalloutAddress: externalFunction.
interpreterProxy failed ifTrue:
[^0 "error code already set by ffiLoadCalloutAddress:"].
argTypeArray := interpreterProxy fetchPointer: ExternalFunctionArgTypesIndex ofObject: externalFunction.
"must be array of arg types"
((interpreterProxy isArray: argTypeArray)
and: [(interpreterProxy slotSizeOf: argTypeArray) = (nArgs + 1)]) ifFalse:
[^self ffiFail: FFIErrorBadArgs].
"check if the calling convention is supported"
self cppIf: COGMTVM
ifTrue:
[(self ffiSupportsCallingConvention: (flags bitAnd: FFICallTypesMask)) ifFalse:
[^self ffiFail: FFIErrorCallType]]
ifFalse: "not masking causes threaded calls to fail, which is as they should if the plugin is not threaded."
[(self ffiSupportsCallingConvention: flags) ifFalse:
[^self ffiFail: FFIErrorCallType]].
requiredStackSize := self externalFunctionHasStackSizeSlot
ifTrue: [interpreterProxy
fetchInteger: ExternalFunctionStackSizeIndex
ofObject: externalFunction]
ifFalse: [-1].
interpreterProxy failed ifTrue:
[^interpreterProxy primitiveFailFor: (argArrayOrNil isNil
ifTrue: [PrimErrBadMethod]
ifFalse: [PrimErrBadReceiver])].
stackSize := requiredStackSize < 0 ifTrue: [DefaultMaxStackSize] ifFalse: [requiredStackSize].
self cCode: [] inSmalltalk: [theCalloutState := self class calloutStateClass new].
calloutState := self addressOf: theCalloutState.
self cCode: [self memset: calloutState _: 0 _: (self sizeof: #CalloutState)].
calloutState callFlags: flags.
"Fetch return type and args"
argType := interpreterProxy fetchPointer: 0 ofObject: argTypeArray.
argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
(err := self ffiCheckReturn: argSpec With: argClass in: calloutState) ~= 0 ifTrue:
[^self ffiFail: err]. "cannot return"
"alloca the outgoing stack frame, leaving room for marshalling args, and including space for the return struct, if any.
Additional space reserved for saving register args like mandated by Win64 X64 or PPC ABI, will be managed by the call itself"
allocation := self alloca: stackSize + calloutState structReturnSize + self cStackAlignment.
self mustAlignStack ifTrue:
[allocation := self cCoerce: (allocation asUnsignedIntegerPtr bitClear: self cStackAlignment - 1) to: #'char *'].
calloutState
argVector: allocation;
currentArg: allocation;
limit: allocation + stackSize.
+ (self nonRegisterStructReturnIsViaImplicitFirstArgument
+ and: [calloutState structReturnSize > 0
- (calloutState structReturnSize > 0
- and: [self nonRegisterStructReturnIsViaImplicitFirstArgument
and: [(self returnStructInRegisters: calloutState) not]]) ifTrue:
[err := self ffiPushPointer: calloutState limit in: calloutState.
err ~= 0 ifTrue:
[self cleanupCalloutState: calloutState.
self cppIf: COGMTVM ifTrue:
[err = PrimErrObjectMayMove negated ifTrue:
[^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
^self ffiFail: err]].
1 to: nArgs do:
[:i|
argType := interpreterProxy fetchPointer: i ofObject: argTypeArray.
argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
oop := argArrayOrNil isNil
ifTrue: [interpreterProxy stackValue: nArgs - i]
ifFalse: [interpreterProxy fetchPointer: i - 1 ofObject: argArrayOrNil].
err := self ffiArgument: oop Spec: argSpec Class: argClass in: calloutState.
err ~= 0 ifTrue:
[self cleanupCalloutState: calloutState.
self cppIf: COGMTVM ifTrue:
[err = PrimErrObjectMayMove negated ifTrue:
[^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
^self ffiFail: err]]. "coercion failed or out of stack space"
"Failures must be reported back from ffiArgument:Spec:Class:in:.
Should not fail from here on in."
self assert: interpreterProxy failed not.
self ffiLogCallout: externalFunction.
(requiredStackSize < 0
and: [self externalFunctionHasStackSizeSlot]) ifTrue:
[stackSize := calloutState currentArg - calloutState argVector.
interpreterProxy storeInteger: ExternalFunctionStackSizeIndex ofObject: externalFunction withValue: stackSize].
"Go out and call this guy"
result := self ffiCalloutTo: address SpecOnStack: argArrayOrNil notNil in: calloutState.
self cleanupCalloutState: calloutState.
"Can not safely use argumentCount (via e.g. methodReturnValue:) since it may have been changed by a callback."
interpreterProxy pop: primNumArgs + 1 thenPush: result.
^result!
Item was changed:
----- Method: ThreadedFFIPlugin>>ffiSupportsCallingConvention: (in category 'callout support') -----
ffiSupportsCallingConvention: aCallingConvention
"Check that the calling convention is valid. This test also filters out attempts
to do a threaded call in the non-threaded VM/plugin combinatioin."
+ <inline: #always>
- <inline: true>
^aCallingConvention = FFICallTypeCDecl or: [aCallingConvention = FFICallTypeApi]!
Item was changed:
----- Method: ThreadedFFIPlugin>>isDirectAlien: (in category 'primitive support') -----
isDirectAlien: oop
+ <inline: #always>
self assert: (self isAlien: oop).
^(self sizeField: oop) > 0!
Item was changed:
----- Method: ThreadedFFIPlugin>>isFloatAtomicType: (in category 'primitive support') -----
isFloatAtomicType: atomicTypeCode
+ <inline: #always>
- <inline: true>
"(atomicTypeCode >> 1) = (FFITypeSingleFloat >> 1)"
^atomicTypeCode >> 1 = 6 !
Item was changed:
----- Method: ThreadedFFIPlugin>>nonRegisterStructReturnIsViaImplicitFirstArgument (in category 'marshalling-struct') -----
nonRegisterStructReturnIsViaImplicitFirstArgument
"Answer if a struct returned in memory is returned to the
+ referent of a pointer passed as an implicit first argument.
- referent of a pointer passed as an implciit first argument.
It almost always is. Subclasses can override if not."
+ <inline: #always>
^true!
Item was removed:
- ----- Method: ThreadedRiscV64FFIPlugin>>ffiCall:ArgArrayOrNil:NumArgs: (in category 'callout support') -----
- ffiCall: externalFunction ArgArrayOrNil: argArrayOrNil NumArgs: nArgs
- "Generic callout. Does the actual work. If argArrayOrNil is nil it takes args from the stack
- and the spec from the method. If argArrayOrNil is not nil takes args from argArrayOrNil
- and the spec from the receiver."
- | flags argTypeArray address argType oop argSpec argClass err theCalloutState calloutState requiredStackSize stackSize allocation result primNumArgs |
- <inline: false>
- <var: #theCalloutState type: #'CalloutState'>
- <var: #calloutState type: #'CalloutState *'>
- <var: #allocation type: #'char *'>
-
- primNumArgs := interpreterProxy methodArgumentCount.
- (interpreterProxy is: externalFunction KindOfClass: interpreterProxy classExternalFunction) ifFalse:
- [^self ffiFail: FFIErrorNotFunction].
- "Load and check the values in the externalFunction before we call out"
- flags := interpreterProxy fetchInteger: ExternalFunctionFlagsIndex ofObject: externalFunction.
- interpreterProxy failed ifTrue:
- [^self ffiFail: FFIErrorBadArgs].
-
- "This must come early for compatibility with the old FFIPlugin. Image-level code
- may assume the function pointer is loaded eagerly. Thanks to Nicolas Cellier."
- address := self ffiLoadCalloutAddress: externalFunction.
- interpreterProxy failed ifTrue:
- [^0 "error code already set by ffiLoadCalloutAddress:"].
-
- argTypeArray := interpreterProxy fetchPointer: ExternalFunctionArgTypesIndex ofObject: externalFunction.
- "must be array of arg types"
- ((interpreterProxy isArray: argTypeArray)
- and: [(interpreterProxy slotSizeOf: argTypeArray) = (nArgs + 1)]) ifFalse:
- [^self ffiFail: FFIErrorBadArgs].
- "check if the calling convention is supported"
- self cppIf: COGMTVM
- ifTrue:
- [(self ffiSupportsCallingConvention: (flags bitAnd: FFICallTypesMask)) ifFalse:
- [^self ffiFail: FFIErrorCallType]]
- ifFalse: "not masking causes threaded calls to fail, which is as they should if the plugin is not threaded."
- [(self ffiSupportsCallingConvention: flags) ifFalse:
- [^self ffiFail: FFIErrorCallType]].
-
- requiredStackSize := self externalFunctionHasStackSizeSlot
- ifTrue: [interpreterProxy
- fetchInteger: ExternalFunctionStackSizeIndex
- ofObject: externalFunction]
- ifFalse: [-1].
- interpreterProxy failed ifTrue:
- [^interpreterProxy primitiveFailFor: (argArrayOrNil isNil
- ifTrue: [PrimErrBadMethod]
- ifFalse: [PrimErrBadReceiver])].
- stackSize := requiredStackSize < 0 ifTrue: [DefaultMaxStackSize] ifFalse: [requiredStackSize].
- self cCode: [] inSmalltalk: [theCalloutState := self class calloutStateClass new].
- calloutState := self addressOf: theCalloutState.
- self cCode: [self memset: calloutState _: 0 _: (self sizeof: #CalloutState)].
- calloutState callFlags: flags.
- "Fetch return type and args"
- argType := interpreterProxy fetchPointer: 0 ofObject: argTypeArray.
- argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
- argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
- (err := self ffiCheckReturn: argSpec With: argClass in: calloutState) ~= 0 ifTrue:
- [^self ffiFail: err]. "cannot return"
- "alloca the outgoing stack frame, leaving room for marshalling args, and including space for the return struct, if any.
- Additional space reserved for saving register args like mandated by Win64 X64 or PPC ABI, will be managed by the call itself"
- allocation := self alloca: stackSize + calloutState structReturnSize + self cStackAlignment.
- self mustAlignStack ifTrue:
- [allocation := self cCoerce: (allocation asUnsignedIntegerPtr bitClear: self cStackAlignment - 1) to: #'char *'].
- calloutState
- argVector: allocation;
- currentArg: allocation;
- limit: allocation + stackSize.
- "This next bit overrides the ARM64 code to pass return struct pointer in A0"
- (calloutState structReturnSize > 0
- and: [self nonRegisterStructReturnIsViaImplicitFirstArgument
- and: [(self returnStructInRegisters: calloutState) not]]) ifTrue:
- [err := self ffiPushPointer: calloutState limit in: calloutState.
- err ~= 0 ifTrue:
- [self cleanupCalloutState: calloutState.
- self cppIf: COGMTVM ifTrue:
- [err = PrimErrObjectMayMove negated ifTrue:
- [^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
- ^self ffiFail: err]].
- "Aside from the bit above, code identical with arm64"
- 1 to: nArgs do:
- [:i|
- argType := interpreterProxy fetchPointer: i ofObject: argTypeArray.
- argSpec := interpreterProxy fetchPointer: 0 ofObject: argType.
- argClass := interpreterProxy fetchPointer: 1 ofObject: argType.
- oop := argArrayOrNil isNil
- ifTrue: [interpreterProxy stackValue: nArgs - i]
- ifFalse: [interpreterProxy fetchPointer: i - 1 ofObject: argArrayOrNil].
- err := self ffiArgument: oop Spec: argSpec Class: argClass in: calloutState.
- err ~= 0 ifTrue:
- [self cleanupCalloutState: calloutState.
- self cppIf: COGMTVM ifTrue:
- [err = PrimErrObjectMayMove negated ifTrue:
- [^PrimErrObjectMayMove]]. "N.B. Do not fail if object may move because caller will GC and retry."
- ^self ffiFail: err]]. "coercion failed or out of stack space"
- "Failures must be reported back from ffiArgument:Spec:Class:in:.
- Should not fail from here on in."
- self assert: interpreterProxy failed not.
- self ffiLogCallout: externalFunction.
- (requiredStackSize < 0
- and: [self externalFunctionHasStackSizeSlot]) ifTrue:
- [stackSize := calloutState currentArg - calloutState argVector.
- interpreterProxy storeInteger: ExternalFunctionStackSizeIndex ofObject: externalFunction withValue: stackSize].
- "Go out and call this guy"
- result := self ffiCalloutTo: address SpecOnStack: argArrayOrNil notNil in: calloutState.
- self cleanupCalloutState: calloutState.
- "Can not safely use argumentCount (via e.g. methodReturnValue:) since it may have been changed by a callback."
- interpreterProxy pop: primNumArgs + 1 thenPush: result.
- ^result!
Item was removed:
- ----- Method: ThreadedRiscV64FFIPlugin>>nonRegisterStructReturnIsViaImplicitFirstArgument (in category 'marshalling') -----
- nonRegisterStructReturnIsViaImplicitFirstArgument
- "Answer if a struct returned in memory is returned to the
- referent of a pointer passed as an implciit first argument.
- It almost always is. Subclasses can override if not."
- ^true!