Hi Eliot,
On Wed, 24 Jun 2020, commits@source.squeak.org wrote:
>
> Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
> http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2761.mcz
>
> ==================== Summary ====================
>
> Name: VMMaker.oscog-eem.2761
> Author: eem
> Time: 24 June 2020, 11:15:52.846414 am
> UUID: 34b29eae-6069-4cb7-8a89-7365fb398dfb
> Ancestors: VMMaker.oscog-eem.2760
>
> Cherry Picking from various recent commits, but avoiding the extremely desirable, but as yet unfinished, VMMaker.oscog-nice.2761 transformInAssignmentTo: changes.the "thisContext method includes: 42" crash.
>
> MiscPrimitivePlugin: fix several uses of sizeOfSTArrayFromCPrimitive: that don''t check for potential failure if e.g. invoked on a CompiledMethod. Returning from a primitive normally when the primtiive has failed leads to disaster since the stack gets cu back but shouldn't be. For CompiledMethod isBytes: is true but isWordsOrBytes: is false. sizeOfSTArrayFromCPrimitive: checks for isWordsOrBytes:. The primtiives check that the argument is isBytes: but don't check if sizeOfSTArrayFromCPrimitive: fails. More general fixes, such as fixing isBytes: to be false for CompiledMethod, or introducing isPureBytes: and using it, are not quick fixes. hence this limited fix here.
>
> Primitive infrastructure: consequently guard the various methodReturnXXX''s with an assert to check that a primitive has not failed.
>
> Also make sure that the SpurNBitCoMemoryManagers do not follow any reference to a Cog method in the first field of a CompiledMethod.
>
> Cosmetic changes for ThreadedFFIPlugins from VMMaker.oscog-nice.2762
>
> Do not try to generate SHA256Plugin, it's obsolete and absent from latest cryptography packages. This from VMMaker.oscog-nice.2761.
While it's true that SHA256Plugin is now obsolete, simply removing it is
not a solution. Its replacement, SHA2Plugin along with fixes to the other
plugins is available in CryptographyPlugins-ul.22.
IMO 3 steps are required to have the plugins built and shipped with the
VM:
1. Use CryptographyPlugins-ul.22 to generate the plugin sources, and push
the generated files to the git repository.
2. Apply the change from VMMaker.oscog-ul.2763.mcz. It's in the VMMaker
Inbox[1].
3. Update plugins.ext across the git repository to include all the
plugins.
I can do 2, I can create a pull request for 3, but I can't do 1.
Levente
[1] http://lists.squeakfoundation.org/pipermail/vm-dev/2020-June/034065.html
>
> Slang:
> Fix TParseNode>>isSameAs: implementations to incluyde an identity check. TReturnNode always answered false to this in the past.
>
> Optimize inlineFunctionCall:in: to avoid a rewrite of the copied parse tree being inlined if the actuals match the formals. Uses the improved bindVariablesIn:.
>
> Use a setter for variable & expression in TAssignmentNode to ease breakpointing/debugging.
>
> =============== Diff against VMMaker.oscog-eem.2760 ===============
>
> Item was changed:
> ----- Method: CogARMCompiler>>concretizeAndCqRR (in category 'generate machine code - concretize') -----
> concretizeAndCqRR
> "Will get inlined into concretizeAt: switch."
> "AND is very important since it's used to mask all sorts of flags in the jit. We take special care to try to find compact ways to make the masks"
> <inline: true>
> | val srcReg dstReg |
> val := operands at: 0.
> srcReg := operands at: 1.
> dstReg := operands at: 2.
> self rotateable8bitBitwiseImmediate: val
> ifTrue:
> [:rot :immediate :invert|
> self machineCodeAt: 0 put: (invert
> ifTrue: [self bics: dstReg rn: srcReg imm: immediate ror: rot]
> ifFalse: [self ands: dstReg rn: srcReg imm: immediate ror: rot]).
> ^4]
> ifFalse:
> [| hb |
> hb := (operands at: 0) highBit.
> "First see if the constant can be made from a simple shift of 0xFFFFFFFF"
> 1 << hb = (val +1) ifTrue: "MVN temp reg, 0, making 0xffffffff"
> [self machineCodeAt: 0 put:(self mvn: ConcreteIPReg imm: 0 ror: 0).
> "Then AND reg, temp reg, lsr #(32-hb)"
> self machineCodeAt: 4 put: (self dataOpType: AndOpcode rd: dstReg rn: srcReg rm: ConcreteIPReg lsr: 32 - hb).
> + ^8]].
> + ^self concretizeDataOperationCwR: AndOpcode R: dstReg!
> - ^8].
> - ^self concretizeDataOperationCwR: AndOpcode R: dstReg]!
>
> Item was added:
> + ----- Method: Float32Array class>>ccg:prolog:expr:index: (in category '*VMMaker-plugin generation') -----
> + ccg: cg prolog: aBlock expr: aString index: anInteger
> +
> + ^cg ccgLoad: aBlock expr: aString asWBFloatPtrFrom: anInteger!
>
> Item was added:
> + ----- Method: Float32Array class>>ccgDeclareCForVar: (in category '*VMMaker-plugin generation') -----
> + ccgDeclareCForVar: aSymbolOrString
> +
> + ^'float *', aSymbolOrString!
>
> Item was removed:
> - ----- Method: FloatArray class>>ccg:prolog:expr:index: (in category '*VMMaker-plugin generation') -----
> - ccg: cg prolog: aBlock expr: aString index: anInteger
> -
> - ^cg ccgLoad: aBlock expr: aString asWBFloatPtrFrom: anInteger!
>
> Item was removed:
> - ----- Method: FloatArray class>>ccgDeclareCForVar: (in category '*VMMaker-plugin generation') -----
> - ccgDeclareCForVar: aSymbolOrString
> -
> - ^'float *', aSymbolOrString!
>
> Item was changed:
> ----- Method: InterpreterProxy>>methodReturnBool: (in category 'stack access') -----
> methodReturnBool: boolean
> "Sets the return value for a method"
> + self deny: self failed.
> self pop: argumentCount+1 thenPushBool: boolean.
> ^0!
>
> Item was changed:
> ----- Method: InterpreterProxy>>methodReturnFloat: (in category 'stack access') -----
> methodReturnFloat: aFloat
> "Sets the return value for a method"
> <var: 'aFloat' type: #double>
> + self deny: self failed.
> self pop: argumentCount+1 thenPushFloat: aFloat.
> ^0!
>
> Item was changed:
> ----- Method: InterpreterProxy>>methodReturnInteger: (in category 'stack access') -----
> methodReturnInteger: integer
> "Sets the return value for a method"
> + self deny: self failed.
> self pop: argumentCount+1 thenPushInteger: integer.
> ^0!
>
> Item was changed:
> ----- Method: InterpreterProxy>>methodReturnReceiver (in category 'stack access') -----
> methodReturnReceiver
> "Sets the return value for a method"
> + self deny: self failed.
> self pop: argumentCount.
> ^0!
>
> Item was changed:
> ----- Method: InterpreterProxy>>methodReturnString: (in category 'stack access') -----
> methodReturnString: aCString
> "Attempt to answer a ByteString for a given C string as the result of a primitive."
> <var: 'aCString' type: #'char *'>
> + self deny: self failed.
> aCString
> ifNil: [primFailCode := PrimErrOperationFailed]
> ifNotNil:
> [(self stringForCString: aCString)
> ifNil: [primFailCode := PrimErrNoMemory]
> ifNotNil: [:result| self pop: argumentCount+1 thenPush: result]].
> ^0!
>
> Item was changed:
> ----- Method: InterpreterProxy>>methodReturnValue: (in category 'stack access') -----
> methodReturnValue: oop
> "Sets the return value for a method"
> + self deny: self failed.
> self pop: argumentCount+1 thenPush: oop.
> ^0!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveCompareString (in category 'primitives') -----
> primitiveCompareString
> "ByteString (class) compare: string1 with: string2 collated: order"
> <export: true>
> | len1 len2 order string1 string2 orderOop string1Oop string2Oop |
>
> <var: 'order' type: #'unsigned char *'>
> <var: 'string1' type: #'unsigned char *'>
> <var: 'string2' type: #'unsigned char *'>
> orderOop := interpreterProxy stackValue: 0.
> string2Oop := interpreterProxy stackValue: 1.
> string1Oop := interpreterProxy stackValue: 2.
> ((interpreterProxy isBytes: orderOop)
> and: [(interpreterProxy isBytes: string2Oop)
> and: [interpreterProxy isBytes: string1Oop]]) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> order := interpreterProxy firstIndexableField: orderOop.
> (interpreterProxy sizeOfSTArrayFromCPrimitive: order) < 256 ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> string1 := interpreterProxy firstIndexableField: string1Oop.
> string2 := interpreterProxy firstIndexableField: string2Oop.
> len1 := interpreterProxy sizeOfSTArrayFromCPrimitive: string1.
> len2 := interpreterProxy sizeOfSTArrayFromCPrimitive: string2.
> + interpreterProxy failed ifTrue: "the sizeOfSTArrayFromCPrimitive:'s fail for e.g. CompiledMethod"
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> 0 to: (len1 min: len2) - 1 do:
> [ :i | | c1 c2 |
> c1 := order at: (string1 at: i).
> c2 := order at: (string2 at: i).
> c1 = c2 ifFalse:
> [^interpreterProxy methodReturnInteger: (c1 < c2 ifTrue: [1] ifFalse: [3])]].
> interpreterProxy methodReturnInteger:
> (len1 = len2 ifTrue: [2] ifFalse: [len1 < len2 ifTrue: [1] ifFalse: [3]])!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveCompressToByteArray (in category 'primitives') -----
> primitiveCompressToByteArray
> "Bitmap compress: bm toByteArray: ba"
> <export: true>
> | bm ba eqBytes i j k lowByte size destSize word |
> <var: 'ba' type: #'unsigned char *'>
> <var: 'bm' type: #'int *'>
> bm := self cCode: [interpreterProxy arrayValueOf: (interpreterProxy stackValue: 1)]
> inSmalltalk: [interpreterProxy
> cCoerce: (interpreterProxy arrayValueOf: (interpreterProxy stackValue: 1))
> to: #'int *'].
> interpreterProxy failed ifTrue: [^nil].
> (interpreterProxy isBytes: (interpreterProxy stackValue: 0)) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> (interpreterProxy isOopImmutable: (interpreterProxy stackValue: 0)) ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrNoModification].
> ba := interpreterProxy firstIndexableField: (interpreterProxy stackValue: 0).
> size := interpreterProxy sizeOfSTArrayFromCPrimitive: bm.
> destSize := interpreterProxy sizeOfSTArrayFromCPrimitive: ba.
> + interpreterProxy failed ifTrue: "the sizeOfSTArrayFromCPrimitive:'s fail for e.g. CompiledMethod"
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> destSize < ((size * 4) + 7 + (size // 1984 * 3)) ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrUnsupported]. "Size may be OK but we don't know, hence fail with unsupported"
> i := self encodeInt: size in: ba at: 0.
> k := 0.
> [k < size] whileTrue:
> [word := bm at: k.
> lowByte := word bitAnd: 255.
> eqBytes := (word >> 8 bitAnd: 255) = lowByte and: [(word >> 16 bitAnd: 255) = lowByte and: [(word >> 24 bitAnd: 255) = lowByte]].
> j := k.
> [j + 1 < size and: [word = (bm at: j + 1)]] whileTrue: [j := j + 1].
> j > k
> ifTrue:
> [eqBytes
> ifTrue:
> [i := self encodeInt: j - k + 1 * 4 + 1 in: ba at: i.
> ba at: i put: lowByte.
> i := i + 1]
> ifFalse:
> [i := self encodeInt: j - k + 1 * 4 + 2 in: ba at: i.
> i := self encodeBytesOf: word in: ba at: i].
> k := j + 1]
> ifFalse:
> [eqBytes
> ifTrue:
> [i := self encodeInt: 1 * 4 + 1 in: ba at: i.
> ba at: i put: lowByte.
> i := i + 1.
> k := k + 1]
> ifFalse:
> [[j + 1 < size and: [(bm at: j) ~= (bm at: j + 1)]] whileTrue: [j := j + 1].
> j + 1 = size ifTrue: [j := j + 1].
> i := self encodeInt: j - k * 4 + 3 in: ba at: i.
> k to: j - 1 by: 1 do: [ :m | i := self encodeBytesOf: (bm at: m) in: ba at: i].
> k := j]]].
> interpreterProxy methodReturnInteger: i!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveConvert8BitSigned (in category 'primitives') -----
> primitiveConvert8BitSigned
> "SampledSound (class) convert8bitSignedFrom: aByteArray to16Bit: aSoundBuffer"
> <export: true>
> | aByteArray aSoundBuffer arraySize byteArrayOop soundBufferOop |
>
> <var: 'aByteArray' type: #'unsigned char *'>
> <var: 'aSoundBuffer' type: #'unsigned short *'>
> byteArrayOop := interpreterProxy stackValue: 1.
> (interpreterProxy isBytes: byteArrayOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> aByteArray := interpreterProxy firstIndexableField: byteArrayOop.
> soundBufferOop := interpreterProxy stackValue: 0.
> + (interpreterProxy isOopImmutable: soundBufferOop) ifTrue:
> + [^interpreterProxy primitiveFailFor: PrimErrNoModification].
> aSoundBuffer := self
> cCode: [interpreterProxy arrayValueOf: soundBufferOop]
> inSmalltalk: [interpreterProxy
> cCoerce: (interpreterProxy arrayValueOf: soundBufferOop)
> to: #'unsigned short *'].
> - interpreterProxy failed ifTrue: [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> - (interpreterProxy isOopImmutable: soundBufferOop) ifTrue:
> - [^interpreterProxy primitiveFailFor: PrimErrNoModification].
> arraySize := interpreterProxy sizeOfSTArrayFromCPrimitive: aByteArray.
> + interpreterProxy failed ifTrue:
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> (interpreterProxy byteSizeOf: soundBufferOop) < (2 * arraySize) ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> 0 to: arraySize - 1 do:
> [ :i | | s |
> s := aByteArray at: i.
> aSoundBuffer
> at: i
> put: (s > 127
> ifTrue: [s - 256 bitShift: 8]
> ifFalse: [s bitShift: 8])].
> interpreterProxy methodReturnReceiver!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveDecompressFromByteArray (in category 'primitives') -----
> primitiveDecompressFromByteArray
> "Bitmap decompress: bm fromByteArray: ba at: index"
> <export: true>
> | bm ba index i anInt code data end k n pastEnd |
> <var: 'ba' type: #'unsigned char *'>
> <var: 'bm' type: #'int *'>
> <var: 'anInt' type: #'unsigned int'>
> <var: 'code' type: #'unsigned int'>
> <var: 'data' type: #'unsigned int'>
> <var: 'n' type: #'unsigned int'>
> bm := self cCode: [interpreterProxy arrayValueOf: (interpreterProxy stackValue: 2)]
> inSmalltalk: [interpreterProxy
> cCoerce: (interpreterProxy arrayValueOf: (interpreterProxy stackValue: 2))
> to: #'int *'].
> (interpreterProxy isOopImmutable: (interpreterProxy stackValue: 2)) ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrNoModification].
> (interpreterProxy isBytes: (interpreterProxy stackValue: 1)) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> ba := interpreterProxy firstIndexableField: (interpreterProxy stackValue: 1).
> index := interpreterProxy stackIntegerValue: 0.
> - interpreterProxy failed ifTrue: [^nil].
> - i := index - 1.
> - k := 0.
> end := interpreterProxy sizeOfSTArrayFromCPrimitive: ba.
> pastEnd := interpreterProxy sizeOfSTArrayFromCPrimitive: bm.
> + interpreterProxy failed ifTrue:
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> + i := index - 1.
> + k := 0.
> [i < end] whileTrue:
> [anInt := ba at: i.
> i := i + 1.
> anInt <= 223 ifFalse:
> [anInt <= 254
> ifTrue:
> [anInt := anInt - 224 * 256 + (ba at: i).
> i := i + 1]
> ifFalse:
> [anInt := 0.
> 1 to: 4 by: 1 do:
> [ :j | anInt := (anInt bitShift: 8) + (ba at: i).
> i := i + 1]]].
> n := anInt >> 2.
> k + n > pastEnd ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadIndex].
> code := anInt bitAnd: 3.
> "code = 0 ifTrue: [nil]."
> code = 1 ifTrue:
> [data := ba at: i.
> i := i + 1.
> data := data bitOr: (data bitShift: 8).
> data := data bitOr: (data bitShift: 16).
> 1 to: n do:
> [ :j |
> bm at: k put: data.
> k := k + 1]].
> code = 2 ifTrue:
> [data := 0.
> 1 to: 4 do:
> [ :j |
> data := (data bitShift: 8) bitOr: (ba at: i).
> i := i + 1].
> 1 to: n do:
> [ :j |
> bm at: k put: data.
> k := k + 1]].
> code = 3 ifTrue:
> [1 to: n do:
> [ :m |
> data := 0.
> 1 to: 4 do:
> [ :j |
> data := (data bitShift: 8) bitOr: (ba at: i).
> i := i + 1].
> bm at: k put: data.
> k := k + 1]]].
> interpreterProxy pop: interpreterProxy methodArgumentCount!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveFindFirstInString (in category 'primitives') -----
> primitiveFindFirstInString
> "ByteString (class) findFirstInString: aString inSet: inclusionMap startingAt: start"
> <export: true>
>
> | aString i inclusionMap stringSize aStringOop inclusionMapOop |
> <var: 'aString' type: #'unsigned char *'>
> <var: 'inclusionMap' type: #'unsigned char *'>
> aStringOop := interpreterProxy stackValue: 2.
> (interpreterProxy isBytes: aStringOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> inclusionMapOop := interpreterProxy stackValue: 1.
> (interpreterProxy isBytes: inclusionMapOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> i := interpreterProxy stackIntegerValue: 0.
> + interpreterProxy failed ifTrue:
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> - interpreterProxy failed ifTrue: [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> i := i - 1. "Convert to 0-based index."
> i < 0 ifTrue: [^interpreterProxy primitiveFailFor: PrimErrBadIndex].
> inclusionMap := interpreterProxy firstIndexableField: inclusionMapOop.
> (interpreterProxy sizeOfSTArrayFromCPrimitive: inclusionMap) ~= 256 ifTrue:
> [^interpreterProxy methodReturnInteger: 0].
> aString := interpreterProxy firstIndexableField: aStringOop.
> stringSize := interpreterProxy sizeOfSTArrayFromCPrimitive: aString.
> + interpreterProxy failed ifTrue: "the sizeOfSTArrayFromCPrimitive:'s fail for e.g. CompiledMethod"
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> [i < stringSize and: [(inclusionMap at: (aString at: i)) = 0]] whileTrue:
> [i := i + 1].
> interpreterProxy methodReturnInteger: (i >= stringSize ifTrue: [0] ifFalse: [i + 1])!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveFindSubstring (in category 'primitives') -----
> primitiveFindSubstring
> "ByteString findSubstring: key in: body startingAt: start matchTable: matchTable"
> <export: true>
>
> + | body key keySize bodySize matchTable start bodyOop keyOop matchTableOop |
> - | body key keySize matchTable start bodyOop keyOop matchTableOop |
> <var: #key type: #'unsigned char *'>
> <var: #body type: #'unsigned char *'>
> <var: #matchTable type: #'unsigned char *'>
> keyOop := interpreterProxy stackValue: 3.
> (interpreterProxy isBytes: keyOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> bodyOop := interpreterProxy stackValue: 2.
> (interpreterProxy isBytes: bodyOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> start := interpreterProxy stackIntegerValue: 1.
> interpreterProxy failed ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> matchTableOop := interpreterProxy stackValue: 0.
> (interpreterProxy isBytes: matchTableOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> matchTable := interpreterProxy firstIndexableField: matchTableOop.
> (interpreterProxy sizeOfSTArrayFromCPrimitive: matchTable) < 256 ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> key := interpreterProxy firstIndexableField: keyOop.
> (keySize := interpreterProxy sizeOfSTArrayFromCPrimitive: key) > 0 ifTrue:
> [keySize := keySize - 1. "adjust for zero relative indexes"
> start := start - 1 max: 0. "adjust for zero relative indexes"
> + body := interpreterProxy firstIndexableField: bodyOop.
> + bodySize := interpreterProxy sizeOfSTArrayFromCPrimitive: body.
> + interpreterProxy failed ifTrue: "the sizeOfSTArrayFromCPrimitive:'s fail for e.g. CompiledMethod"
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> + start to: bodySize - 1 - keySize do:
> - body := interpreterProxy firstIndexableField: bodyOop.
> - start to: (interpreterProxy sizeOfSTArrayFromCPrimitive: body) - 1 - keySize do:
> [ :startIndex | | index |
> index := 0.
> [(matchTable at: (body at: startIndex + index)) = (matchTable at: (key at: index))] whileTrue:
> [index = keySize ifTrue:
> [^interpreterProxy methodReturnInteger: startIndex + 1].
> index := index + 1]]].
> ^interpreterProxy methodReturnInteger: 0!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveIndexOfAsciiInString (in category 'primitives') -----
> primitiveIndexOfAsciiInString
> "ByteString indexOfAscii: anInteger inString: aString startingAt: start"
> <export: true>
>
> + | integerOop startOop anInteger aString start stringSize stringOop |
> - | anInteger aString start stringSize aStringOop |
> <var: #aString type: #'unsigned char *'>
> + integerOop := interpreterProxy stackValue: 2.
> + stringOop := interpreterProxy stackValue: 1.
> + startOop := interpreterProxy stackValue: 0.
> + ((interpreterProxy isIntegerObject: integerOop)
> + and: [(interpreterProxy isIntegerObject: startOop)
> + and: [(interpreterProxy isBytes: stringOop)
> + and: [interpreterProxy isWordsOrBytes: stringOop]]]) ifFalse: "sizeOfSTArrayFromCPrimitive: is defined only for words or bytes"
> - anInteger := interpreterProxy stackIntegerValue: 2.
> - interpreterProxy failed ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> + (start := interpreterProxy integerValueOf: startOop) >= 1 ifFalse:
> + [^interpreterProxy primitiveFailFor: PrimErrBadIndex].
> + anInteger := interpreterProxy integerValueOf: integerOop.
> + aString := interpreterProxy firstIndexableField: stringOop.
> - aStringOop := interpreterProxy stackValue: 1.
> - (interpreterProxy isBytes: aStringOop) ifFalse:
> - [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> - start := interpreterProxy stackIntegerValue: 0.
> - interpreterProxy failed ifTrue:
> - [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> - start >= 1 ifFalse: [^interpreterProxy primitiveFailFor: PrimErrBadIndex].
> - aString := interpreterProxy firstIndexableField: aStringOop.
> stringSize := interpreterProxy sizeOfSTArrayFromCPrimitive: aString.
> start - 1 to: stringSize - 1 do:
> + [:pos |
> - [ :pos |
> (aString at: pos) = anInteger ifTrue:
> [^interpreterProxy methodReturnInteger: pos + 1]].
> ^interpreterProxy methodReturnInteger: 0!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveStringHash (in category 'primitives') -----
> primitiveStringHash
> "ByteArray (class) hashBytes: aByteArray startingWith: speciesHash"
> <export: true>
>
> | aByteArray hash byteArrayOop |
> <var: 'aByteArray' type: #'unsigned char *'>
> <var: 'hash' type: #'unsigned int'>
> hash := interpreterProxy stackIntegerValue: 0.
> interpreterProxy failed ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> byteArrayOop := interpreterProxy stackValue: 1.
> + ((interpreterProxy isBytes: byteArrayOop)
> + and: [interpreterProxy isWordsOrBytes: byteArrayOop]) ifFalse: "filters out CompiledMethods"
> - (interpreterProxy isBytes: byteArrayOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> aByteArray := interpreterProxy firstIndexableField: byteArrayOop.
> 0 to: (interpreterProxy sizeOfSTArrayFromCPrimitive: aByteArray) - 1 do:
> [ :pos |
> hash := hash + (aByteArray at: pos) * 16r19660D ].
> interpreterProxy methodReturnInteger: (hash bitAnd: 16r0FFFFFFF)!
>
> Item was changed:
> ----- Method: MiscPrimitivePlugin>>primitiveTranslateStringWithTable (in category 'primitives') -----
> primitiveTranslateStringWithTable
> "ByteString (class) translate: aString from: start to: stop table: table"
> <export: true>
>
> | aString start stop table aStringOop tableOop |
> <var: #table type: #'unsigned char *'>
> <var: #aString type: #'unsigned char *'>
> aStringOop := interpreterProxy stackValue: 3.
> (interpreterProxy isBytes: aStringOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> (interpreterProxy isOopImmutable: aStringOop) ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrNoModification].
> start := interpreterProxy stackIntegerValue: 2.
> - interpreterProxy failed ifTrue:
> - [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> stop := interpreterProxy stackIntegerValue: 1.
> interpreterProxy failed ifTrue:
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> - [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> tableOop := interpreterProxy stackValue: 0.
> (interpreterProxy isBytes: tableOop) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> aString := interpreterProxy firstIndexableField: aStringOop.
> (start >= 1 and: [stop <= (interpreterProxy sizeOfSTArrayFromCPrimitive: aString)]) ifFalse:
> [^interpreterProxy primitiveFailFor: PrimErrBadIndex].
> table := interpreterProxy firstIndexableField: tableOop.
> (interpreterProxy sizeOfSTArrayFromCPrimitive: table) < 256 ifTrue:
> [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> + interpreterProxy failed ifTrue:
> + [^interpreterProxy primitiveFailFor: PrimErrBadArgument].
> start - 1 to: stop - 1 do: [ :i | aString at: i put: (table at: (aString at: i))].
> interpreterProxy methodReturnReceiver!
>
> Item was added:
> + ----- Method: Spur32BitCoMemoryManager>>followForwardedObjectFields:toDepth: (in category 'forwarding') -----
> + followForwardedObjectFields: objOop toDepth: depth
> + "Follow pointers in the object to depth.
> + Answer if any forwarders were found.
> + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no need since depth is always finite."
> + <api>
> + <inline: false>
> + | found fmt numSlots |
> + found := false.
> + self assert: ((self isPointers: objOop) or: [self isOopCompiledMethod: objOop]).
> + fmt := self formatOf: objOop.
> + numSlots := self numPointerSlotsOf: objOop format: fmt.
> + "It is essential to skip the first field of a method because it may be a
> + reference to a Cog method in the method zone, not a real object at all."
> + ((self isCompiledMethodFormat: fmt)
> + ifTrue: [1]
> + ifFalse: [0])
> + to: numSlots - 1
> + do: [:i| | oop |
> + oop := self fetchPointer: i ofObject: objOop.
> + (self isNonImmediate: oop) ifTrue:
> + [(self isForwarded: oop) ifTrue:
> + [found := true.
> + oop := self followForwarded: oop.
> + self storePointer: i ofObject: objOop withValue: oop].
> + (depth > 0
> + and: [(self hasPointerFields: oop)
> + and: [self followForwardedObjectFields: oop toDepth: depth - 1]]) ifTrue:
> + [found := true]]].
> + ^found!
>
> Item was added:
> + ----- Method: Spur64BitCoMemoryManager>>followForwardedObjectFields:toDepth: (in category 'forwarding') -----
> + followForwardedObjectFields: objOop toDepth: depth
> + "Follow pointers in the object to depth.
> + Answer if any forwarders were found.
> + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no need since depth is always finite."
> + <api>
> + <inline: false>
> + | found fmt numSlots |
> + found := false.
> + self assert: ((self isPointers: objOop) or: [self isOopCompiledMethod: objOop]).
> + fmt := self formatOf: objOop.
> + numSlots := self numPointerSlotsOf: objOop format: fmt.
> + "It is essential to skip the first field of a method because it may be a
> + reference to a Cog method in the method zone, not a real object at all."
> + ((self isCompiledMethodFormat: fmt)
> + ifTrue: [1]
> + ifFalse: [0])
> + to: numSlots - 1
> + do: [:i| | oop |
> + oop := self fetchPointer: i ofObject: objOop.
> + (self isNonImmediate: oop) ifTrue:
> + [(self isForwarded: oop) ifTrue:
> + [found := true.
> + oop := self followForwarded: oop.
> + self storePointer: i ofObject: objOop withValue: oop].
> + (depth > 0
> + and: [(self hasPointerFields: oop)
> + and: [self followForwardedObjectFields: oop toDepth: depth - 1]]) ifTrue:
> + [found := true]]].
> + ^found!
>
> Item was changed:
> ----- Method: SpurMemoryManager>>followForwardedObjectFields:toDepth: (in category 'forwarding') -----
> followForwardedObjectFields: objOop toDepth: depth
> "Follow pointers in the object to depth.
> Answer if any forwarders were found.
> + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no need since depth is always finite."
> - How to avoid cyclic structures?? A temproary mark bit?"
> <api>
> <inline: false>
> + | found numSlots |
> - | oop found |
> found := false.
> self assert: ((self isPointers: objOop) or: [self isOopCompiledMethod: objOop]).
> + numSlots := self numPointerSlotsOf: objOop.
> + 0 to: numSlots - 1 do:
> + [:i| | oop |
> - 0 to: (self numPointerSlotsOf: objOop) - 1 do:
> - [:i|
> oop := self fetchPointer: i ofObject: objOop.
> (self isNonImmediate: oop) ifTrue:
> [(self isForwarded: oop) ifTrue:
> [found := true.
> oop := self followForwarded: oop.
> self storePointer: i ofObject: objOop withValue: oop].
> (depth > 0
> and: [(self hasPointerFields: oop)
> and: [self followForwardedObjectFields: oop toDepth: depth - 1]]) ifTrue:
> [found := true]]].
> ^found!
>
> Item was changed:
> ----- Method: SpurMemoryManager>>numPointerSlotsOf: (in category 'object access') -----
> numPointerSlotsOf: objOop
> "Answer the number of pointer fields in the given object.
> Works with CompiledMethods, as well as ordinary objects."
> <api>
> <inline: true>
> + | fmt |
> - | fmt contextSize numLiterals header |
> fmt := self formatOf: objOop.
> + ^self numPointerSlotsOf: objOop format: fmt!
> - fmt <= self lastPointerFormat ifTrue:
> - [(fmt = self indexablePointersFormat
> - and: [self isContextNonImm: objOop]) ifTrue:
> - ["contexts end at the stack pointer"
> - contextSize := coInterpreter fetchStackPointerOf: objOop.
> - ^CtxtTempFrameStart + contextSize].
> - ^self numSlotsOf: objOop "all pointers"].
> - fmt = self forwardedFormat ifTrue: [^1].
> - fmt < self firstCompiledMethodFormat ifTrue: [^0]. "no pointers"
> -
> - "CompiledMethod: contains both pointers and bytes"
> - header := self methodHeaderOf: objOop.
> - numLiterals := self literalCountOfMethodHeader: header.
> - ^numLiterals + LiteralStart!
>
> Item was added:
> + ----- Method: SpurMemoryManager>>numPointerSlotsOf:format: (in category 'object access') -----
> + numPointerSlotsOf: objOop format: fmt
> + "Answer the number of pointer fields in the given object.
> + Works with CompiledMethods, as well as ordinary objects."
> + <inline: #always>
> + | contextSize numLiterals header |
> + fmt <= self lastPointerFormat ifTrue:
> + [(fmt = self indexablePointersFormat
> + and: [self isContextNonImm: objOop]) ifTrue:
> + ["contexts end at the stack pointer"
> + contextSize := coInterpreter fetchStackPointerOf: objOop.
> + ^CtxtTempFrameStart + contextSize].
> + ^self numSlotsOf: objOop "all pointers"].
> + fmt = self forwardedFormat ifTrue: [^1].
> + fmt < self firstCompiledMethodFormat ifTrue: [^0]. "no pointers"
> +
> + "CompiledMethod: contains both pointers and bytes"
> + header := self methodHeaderOf: objOop.
> + numLiterals := self literalCountOfMethodHeader: header.
> + ^numLiterals + LiteralStart!
>
> Item was changed:
> ----- Method: StackInterpreter>>methodReturnBool: (in category 'plugin primitive support') -----
> methodReturnBool: boolean
> "Sets the return value for a method. In the CoInterpreter we replace the cumbersome
> primResult machinery."
> + self deny: self failed.
> self pop: argumentCount+1 thenPushBool: boolean.
> ^0!
>
> Item was changed:
> ----- Method: StackInterpreter>>methodReturnFloat: (in category 'plugin primitive support') -----
> methodReturnFloat: aFloat
> "Sets the return value for a method."
> <var: 'aFloat' type: #double>
> + self deny: self failed.
> self pop: argumentCount+1 thenPushFloat: aFloat.
> ^0!
>
> Item was changed:
> ----- Method: StackInterpreter>>methodReturnInteger: (in category 'plugin primitive support') -----
> methodReturnInteger: integer
> "Sets the return value for a method. In the CoInterpreter we replace the cumbersome
> primResult machinery."
> + self deny: self failed.
> self pop: argumentCount+1 thenPushInteger: integer.
> ^0!
>
> Item was changed:
> ----- Method: StackInterpreter>>methodReturnReceiver (in category 'plugin primitive support') -----
> methodReturnReceiver
> "Sets the return value for a method"
> + self deny: self failed.
> self pop: argumentCount.
> ^0!
>
> Item was changed:
> ----- Method: StackInterpreter>>methodReturnString: (in category 'plugin primitive support') -----
> methodReturnString: aCString
> "Attempt to answer a ByteString for a given C string as the result of a primitive."
> <var: 'aCString' type: #'char *'>
> + self deny: self failed.
> aCString
> ifNil: [primFailCode := PrimErrOperationFailed]
> ifNotNil:
> [(self stringForCString: aCString)
> ifNil: [primFailCode := PrimErrNoMemory]
> ifNotNil: [:result| self pop: argumentCount+1 thenPush: result]].
> ^0!
>
> Item was changed:
> ----- Method: StackInterpreter>>methodReturnValue: (in category 'plugin primitive support') -----
> methodReturnValue: oop
> "Sets the return value for a method. In the CoInterpreter we replace the cumbersome
> primResult machinery."
> + self deny: self failed.
> self pop: argumentCount+1 thenPush: oop.
> ^0!
>
> Item was changed:
> ----- Method: TAssignmentNode>>bindVariablesIn: (in category 'transformations') -----
> bindVariablesIn: aDictionary
>
> + self setVar: (variable bindVariablesIn: aDictionary)
> + exp: (expression bindVariablesIn: aDictionary)!
> - variable := variable bindVariablesIn: aDictionary.
> - expression := expression bindVariablesIn: aDictionary.!
>
> Item was changed:
> ----- Method: TAssignmentNode>>isSameAs: (in category 'testing') -----
> isSameAs: aTParseNode
> + ^self == aTParseNode
> + or: [aTParseNode isAssignment
> + and: [(variable isSameAs: aTParseNode variable)
> + and: [expression isSameAs: aTParseNode expression]]]!
> - ^aTParseNode isAssignment
> - and: [(variable isSameAs: aTParseNode variable)
> - and: [expression isSameAs: aTParseNode expression]]!
>
> Item was changed:
> ----- Method: TAssignmentNode>>postCopy (in category 'copying') -----
> postCopy
>
> + self setVar: variable copy exp: expression copy!
> - variable := variable copy.
> - expression := expression copy!
>
> Item was changed:
> ----- Method: TAssignmentNode>>replaceNodesIn: (in category 'transformations') -----
> replaceNodesIn: aDictionary
>
> + ^aDictionary
> + at: self
> + ifAbsent:
> + [self setVar: (variable replaceNodesIn: aDictionary)
> + exp: (expression replaceNodesIn: aDictionary)]!
> - ^aDictionary at: self ifAbsent: [
> - variable := variable replaceNodesIn: aDictionary.
> - expression := expression replaceNodesIn: aDictionary.
> - self]!
>
> Item was added:
> + ----- Method: TAssignmentNode>>setVar:exp: (in category 'private') -----
> + setVar: varNode exp: expressionNode
> + "This is a private setter, just for breakpointing..."
> + variable := varNode.
> + expression := expressionNode!
>
> Item was changed:
> ----- Method: TConstantNode>>isSameAs: (in category 'comparing') -----
> isSameAs: aTParseNode
> + ^self == aTParseNode
> + or: [aTParseNode isConstant
> + and: [value class == aTParseNode value class
> + and: [value = aTParseNode value]]]!
> - ^aTParseNode isConstant
> - and: [value class == aTParseNode value class
> - and: [value = aTParseNode value]]!
>
> Item was changed:
> ----- Method: TDefineNode>>isSameAs: (in category 'comparing') -----
> isSameAs: aTParseNode
> + ^self == aTParseNode
> + or: [self class == aTParseNode class
> + and: [value class == aTParseNode value class
> + and: [value = aTParseNode value
> + and: [name = aTParseNode nameOrValue]]]]!
> - ^self class == aTParseNode class
> - and: [value class == aTParseNode value class
> - and: [value = aTParseNode value
> - and: [name = aTParseNode nameOrValue]]]!
>
> Item was changed:
> ----- Method: TMethod>>deny: (in category 'error handling') -----
> deny: aBooleanOrBlock
> - <doNotGenerate>
> aBooleanOrBlock value ifTrue: [AssertionFailure signal: 'Assertion failed']!
>
> Item was changed:
> ----- Method: TMethod>>inlineFunctionCall:in: (in category 'inlining') -----
> inlineFunctionCall: aSendNode in: aCodeGen
> "Answer the body of the called function, substituting the actual
> parameters for the formal argument variables in the method body.
> Assume caller has established that:
> 1. the method arguments are all substitutable nodes, and
> 2. the method to be inlined contains no additional embedded returns."
>
> | sel meth doNotRename argsForInlining substitutionDict |
> + aCodeGen maybeBreakForInlineOf: aSendNode in: self.
> sel := aSendNode selector.
> meth := (aCodeGen methodNamed: sel) copy.
> meth ifNil:
> [^self inlineBuiltin: aSendNode in: aCodeGen].
> doNotRename := Set withAll: args.
> argsForInlining := aSendNode argumentsForInliningCodeGenerator: aCodeGen.
> meth args with: argsForInlining do:
> [ :argName :exprNode |
> exprNode isLeaf ifTrue:
> [doNotRename add: argName]].
> (meth statements size = 2
> and: [meth statements first isSend
> and: [meth statements first selector == #flag:]]) ifTrue:
> [meth statements removeFirst].
> meth renameVarsForInliningInto: self except: doNotRename in: aCodeGen.
> meth renameLabelsForInliningInto: self.
> self addVarsDeclarationsAndLabelsOf: meth except: doNotRename.
> substitutionDict := Dictionary new: meth args size * 2.
> meth args with: argsForInlining do:
> [ :argName :exprNode |
> + (exprNode isVariable and: [exprNode name = argName]) ifFalse:
> + [substitutionDict at: argName put: exprNode].
> - substitutionDict at: argName put: exprNode.
> (doNotRename includes: argName) ifFalse:
> [locals remove: argName]].
> meth parseTree bindVariablesIn: substitutionDict.
> ^meth parseTree endsWithReturn
> ifTrue: [meth parseTree copyWithoutReturn]
> ifFalse: [meth parseTree]!
>
> Item was added:
> + ----- Method: TParseNode>>deny: (in category 'as yet unclassified') -----
> + deny: aBooleanOrBlock
> + aBooleanOrBlock value ifTrue: [AssertionFailure signal: 'Assertion failed']!
>
> Item was changed:
> ----- Method: TParseNode>>isSameAs: (in category 'comparing') -----
> isSameAs: aTParseNode
> "Answer if the ParseTree rooted at this node is the same as aTParseNode.
> By default answer false and have subclasses override as appropriate."
> + ^self == aTParseNode!
> - ^false!
>
> Item was added:
> + ----- Method: TReturnNode>>isSameAs: (in category 'comparing') -----
> + isSameAs: aTParseNode
> + ^self == aTParseNode
> + or: [aTParseNode isReturn
> + and: [expression isSameAs: aTParseNode expression]]!
>
> Item was changed:
> ----- Method: TSendNode>>isSameAs: (in category 'comparing') -----
> isSameAs: aTParseNode
> + self == aTParseNode ifTrue: [^true].
> (aTParseNode isSend
> and: [selector == aTParseNode selector
> and: [receiver isSameAs: aTParseNode receiver]]) ifFalse:
> [^false].
> arguments with: aTParseNode args do:
> [:a :b|
> (a isSameAs: b) ifFalse:
> [^false]].
> ^true!
>
> Item was changed:
> ----- Method: TStmtListNode>>bindVariablesIn: (in category 'transformations') -----
> bindVariablesIn: aDictionary
>
> + aDictionary notEmpty ifTrue:
> + [statements := statements collect: [:s| s bindVariablesIn: aDictionary]]!
> - statements := statements collect: [ :s | s bindVariablesIn: aDictionary ].!
>
> Item was changed:
> ----- Method: TStmtListNode>>isSameAs: (in category 'testing') -----
> isSameAs: aTParseNode
> + self == aTParseNode ifTrue: [^true].
> (aTParseNode isStmtList
> and: [statements size = aTParseNode statements size]) ifFalse:
> [^false].
> statements with: aTParseNode statements do:
> [:mine :theirs|
> (mine isSameAs: theirs) ifFalse:
> [^false]].
> ^true!
>
> Item was changed:
> ----- Method: TVariableNode>>isSameAs: (in category 'comparing') -----
> isSameAs: aTParseNode
> + ^self == aTParseNode
> + or: [aTParseNode isVariable
> + and: [name = aTParseNode name]]!
> - ^aTParseNode isVariable
> - and: [name = aTParseNode name]!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>canReturnInRegistersStructOfSize: (in category 'marshalling-struct') -----
> - ----- Method: ThreadedFFIPlugin>>canReturnInRegistersStructOfSize: (in category 'marshalling') -----
> canReturnInRegistersStructOfSize: returnStructSize
> "Answer if a struct result of a given size can be returned via registers or not.
> Size is a necessary condition, but it might not be a sufficient condition.
> For example, SysV X64 also require that struct fields be properly aligned."
> ^self subclassResponsibility!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>ffiPushSignedLongLongOop:in: (in category 'marshalling') -----
> - ----- Method: ThreadedFFIPlugin>>ffiPushSignedLongLongOop:in: (in category 'callout support') -----
> ffiPushSignedLongLongOop: oop in: calloutState
> <var: #calloutState type: #'CalloutState *'>
> "Push a longlong type (e.g., a 64bit integer).
> Note: Coercions from float are *not* supported."
> | value |
> <var: #value type: #sqLong>
> (oop = interpreterProxy nilObject
> or: [oop = interpreterProxy falseObject])
> ifTrue:[value := 0] ifFalse:
> [oop = interpreterProxy trueObject
> ifTrue:[value := 1] ifFalse:
> [value := interpreterProxy signed64BitValueOf: oop.
> interpreterProxy failed ifTrue:
> [^FFIErrorCoercionFailed]]].
> ^self ffiPushSignedLongLong: value in: calloutState!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>ffiPushStructure:ofSize:typeSpec:ofLength:in: (in category 'marshalling-struct') -----
> - ----- Method: ThreadedFFIPlugin>>ffiPushStructure:ofSize:typeSpec:ofLength:in: (in category 'marshalling') -----
> ffiPushStructure: pointer ofSize: structSize typeSpec: argSpec ofLength: argSpecSize in: calloutState
> <var: #pointer type: #'void *'>
> <var: #argSpec type: #'sqInt *'>
> <var: #calloutState type: #'CalloutState *'>
> <inline: true>
> self subclassResponsibility!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>ffiPushStructureContentsOf:in: (in category 'marshalling-struct') -----
> - ----- Method: ThreadedFFIPlugin>>ffiPushStructureContentsOf:in: (in category 'callout support') -----
> ffiPushStructureContentsOf: oop in: calloutState
> <var: #calloutState type: #'CalloutState *'>
> "Push the contents of the given external structure"
> | ptrClass ptrAddress |
> <inline: true>
> ptrClass := interpreterProxy fetchClassOf: oop.
> ptrClass = interpreterProxy classExternalAddress ifTrue: "ExternalAddress is bytes"
> [ptrAddress := (interpreterProxy fetchPointer: 0 ofObject: oop) asVoidPointer.
> "There is no way we can make sure the structure is valid.
> But we can at least check for attempts to pass pointers to ST memory."
> (interpreterProxy isInMemory: ptrAddress) ifTrue:
> [^FFIErrorInvalidPointer].
> ^self ffiPushStructure: ptrAddress
> ofSize: (calloutState ffiArgHeader bitAnd: FFIStructSizeMask)
> typeSpec: calloutState ffiArgSpec
> ofLength: calloutState ffiArgSpecSize
> in: calloutState].
> ptrClass = interpreterProxy classByteArray ifTrue:
> ["The following is a somewhat pessimistic test but I like being sure..."
> (interpreterProxy byteSizeOf: oop) = (calloutState ffiArgHeader bitAnd: FFIStructSizeMask)
> ifFalse:[^FFIErrorStructSize].
> ptrAddress := interpreterProxy firstIndexableField: oop.
> (calloutState ffiArgHeader anyMask: FFIFlagPointer) ifFalse:
> "Since this involves passing the address of the first indexable field we need to fail
> the call if it is threaded and the object is young, since it may move during the call."
> [self cppIf: COGMTVM ifTrue:
> [((calloutState callFlags anyMask: FFICallFlagThreaded)
> and: [interpreterProxy isYoung: oop]) ifTrue:
> [^PrimErrObjectMayMove negated]].
> ^self ffiPushStructure: ptrAddress
> ofSize: (calloutState ffiArgHeader bitAnd: FFIStructSizeMask)
> typeSpec: calloutState ffiArgSpec
> ofLength: calloutState ffiArgSpecSize
> in: calloutState].
> "If FFIFlagPointer + FFIFlagStructure is set use ffiPushPointer on the contents"
> (calloutState ffiArgHeader bitAnd: FFIStructSizeMask) = BytesPerWord ifFalse:
> [^FFIErrorStructSize].
> ptrAddress := (interpreterProxy fetchPointer: 0 ofObject: oop) asVoidPointer.
> (interpreterProxy isInMemory: ptrAddress) ifTrue:
> [^FFIErrorInvalidPointer].
> ^self ffiPushPointer: ptrAddress in: calloutState].
> ^FFIErrorBadArg!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>ffiPushUnsignedLongLongOop:in: (in category 'marshalling') -----
> - ----- Method: ThreadedFFIPlugin>>ffiPushUnsignedLongLongOop:in: (in category 'callout support') -----
> ffiPushUnsignedLongLongOop: oop in: calloutState
> <var: #calloutState type: #'CalloutState *'>
> "Push an unsigned longlong type (e.g., a 64bit integer).
> Note: Coercions from float are *not* supported."
> | value |
> <var: #value type: #usqLong>
> (oop = interpreterProxy nilObject
> or: [oop = interpreterProxy falseObject])
> ifTrue:[value := 0] ifFalse:
> [oop = interpreterProxy trueObject
> ifTrue:[value := 1] ifFalse:
> [value := interpreterProxy positive64BitValueOf: oop.
> interpreterProxy failed ifTrue:
> [^FFIErrorCoercionFailed]]].
> ^self ffiPushUnsignedLongLong: value in: calloutState!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>ffiPushVoid:in: (in category 'marshalling') -----
> - ----- Method: ThreadedFFIPlugin>>ffiPushVoid:in: (in category 'callout support') -----
> ffiPushVoid: ignored in: calloutState
> <var: #calloutState type: #'CalloutState *'>
> "This is a fallback in case somebody tries to pass a 'void' value.
> We could simply ignore the argument but I think it's better to let
> the caller know what he did"
> ^FFIErrorAttemptToPassVoid!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>nonRegisterStructReturnIsViaImplicitFirstArgument (in category 'marshalling-struct') -----
> - ----- Method: ThreadedFFIPlugin>>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!
>
> Item was changed:
> + ----- Method: ThreadedFFIPlugin>>returnStructInRegisters: (in category 'marshalling-struct') -----
> - ----- Method: ThreadedFFIPlugin>>returnStructInRegisters: (in category 'marshalling') -----
> returnStructInRegisters: calloutState
> "Answer if struct result is returned in registers or not.
> Use the OS specific encoding stored in structReturnType.
> Since it is OS dependent, leave the responsibility to subclass"
> <var: #calloutState type: #'CalloutState *'>
> ^self subclassResponsibility!
>
> Item was changed:
> ----- Method: ThreadedX64SysVFFIPlugin>>registerType:ForUnionSpecs:OfLength:StartingAt:ByteOffset:EightbyteOffset: (in category 'marshalling') -----
> registerType: initialRegisterType ForUnionSpecs: specs OfLength: specSize StartingAt: indexPtr ByteOffset: byteOffset EightbyteOffset: eightbyteOffset
> "Answer with a number characterizing the register type for passing a union of size <= 16 bytes.
> On input, the index points to the structure header (the one with FFIFlagStructure + structSize)
> On output, the index points to the structure trailer (the FFIFlagStructure)."
>
> <var: #specs type: #'unsigned int*'>
> <var: #indexPtr type: #'unsigned int*'>
> - <var: #subIndex type: #'unsigned int'>
> <inline: false>
> | registerType spec atomic isInt recurse subLevel |
> registerType := initialRegisterType.
> [indexPtr at: 0 put: (indexPtr at: 0) + 1.
> subLevel := 0.
> (indexPtr at: 0) < specSize]
> whileTrue:
> [spec := specs at: (indexPtr at: 0).
> isInt := false.
> recurse := false.
> spec = FFIFlagStructure "this marks end of structure/union"
> ifTrue:
> [subLevel = 0 ifTrue: [^registerType].
> subLevel := subLevel - 1]
> ifFalse:
> [(spec anyMask: FFIFlagPointer)
> ifTrue:
> [isInt := true]
> ifFalse:
> [(spec bitAnd: FFIFlagStructure + FFIFlagAtomic)
> caseOf:
> {[FFIFlagStructure] ->
> [recurse := (self isUnionSpec: specs OfLength: specSize StartingAt: (indexPtr at: 0))not.
> recurse ifFalse: [subLevel := subLevel + 1]].
> [FFIFlagAtomic] ->
> [atomic := self atomicTypeOf: spec.
> isInt := (atomic >> 1) ~= (FFITypeSingleFloat >> 1)]}
> otherwise: ["invalid spec" ^-1]].
> isInt
> ifTrue:
> ["If this eightbyte contains an int field, then we must use an int register"
> registerType := registerType bitOr: 1 << eightbyteOffset].
> recurse ifTrue:
> ["struct in union require a recursive form, because we handle byteOffset/eightbyteOffset differently"
> registerType := self
> registerType: registerType
> ForStructSpecs: specs
> OfLength: specSize
> StartingAt: indexPtr
> ByteOffset: byteOffset
> EightbyteOffset: eightbyteOffset]]].
> self assert: subLevel = 0.
> ^registerType!
>
> Item was changed:
> ----- Method: ThreadedX64SysVFFIPlugin>>registerTypeForStructSpecs:OfLength: (in category 'marshalling') -----
> registerTypeForStructSpecs: specs OfLength: specSize
> "Answer with a number characterizing the register type for passing a struct of size <= 16 bytes.
> The bit at offset i of registerType is set to 1 if eightbyte at offset i is a int register (RAX ...)
> The bit at offset 2 indicates if there is a single eightbyte (struct size <= 8)
> * 2r00 for float float (XMM0 XMM1)
> * 2r01 for int float (RAX XMM0)
> * 2r10 for float int (XMM0 RAX)
> * 2r11 for int int (RAX RDX)
> * 2r100 for float (XMM0)
> * 2r101 for int (RAX)
> * 2r110 INVALID (not aligned)
> Beware, the bits must be read from right to left for decoding register type.
> Note: this method reconstructs the struct layout according to X64 alignment rules.
> Therefore, it will not work for packed struct or other exotic alignment."
>
> <var: #specs type: #'unsigned int*'>
> - <var: #subIndex type: #'unsigned int'>
> <inline: false>
> | index byteSize registerType |
> index := 0.
> byteSize := (specs at: index) bitAnd: FFIStructSizeMask.
> byteSize > 16 ifTrue: [^2r110].
> (self checkAlignmentOfStructSpec: specs OfLength: specSize StartingAt: index)
> ifFalse: [^2r110].
> registerType := byteSize <= 8 ifTrue: [2r100] ifFalse: [0].
> ^(self isUnionSpec: specs OfLength: specSize StartingAt: 0)
> ifTrue: [ self
> registerType: registerType
> ForUnionSpecs: specs
> OfLength: specSize
> StartingAt: (self addressOf: index)
> ByteOffset: 0
> EightbyteOffset: 0 ]
> ifFalse: [ self
> registerType: registerType
> ForStructSpecs: specs
> OfLength: specSize
> StartingAt: (self addressOf: index)
> ByteOffset: 0
> EightbyteOffset: 0 ]!
>
> Item was changed:
> ----- Method: VMMaker class>>generateVMPlugins (in category 'configurations') -----
> generateVMPlugins
> ^VMMaker
> generatePluginsTo: self sourceTree, '/src'
> options: #()
> platformDir: self sourceTree, '/platforms'
> including:#(ADPCMCodecPlugin AsynchFilePlugin
> BalloonEnginePlugin B3DAcceleratorPlugin B3DEnginePlugin BMPReadWriterPlugin BitBltSimulation
> BochsIA32Plugin BochsX64Plugin GdbARMv6Plugin GdbARMv8Plugin
> CameraPlugin CroquetPlugin DeflatePlugin DropPlugin
> + "Cryptography Plugins:" DESPlugin DSAPlugin MD5Plugin
> - "Cryptography Plugins:" DESPlugin DSAPlugin MD5Plugin SHA256Plugin
> "FT2Plugin" FFTPlugin FileCopyPlugin FilePlugin FileAttributesPlugin Float64ArrayPlugin FloatArrayPlugin FloatMathPlugin
> GeniePlugin HostWindowPlugin IA32ABIPlugin ImmX11Plugin InternetConfigPlugin
> JPEGReadWriter2Plugin JPEGReaderPlugin JoystickTabletPlugin KlattSynthesizerPlugin
> LargeIntegersPlugin LocalePlugin MIDIPlugin MacMenubarPlugin Matrix2x3Plugin
> MiscPrimitivePlugin Mpeg3Plugin QuicktimePlugin RePlugin
> ScratchPlugin SecurityPlugin SerialPlugin SocketPlugin
> SoundCodecPlugin SoundGenerationPlugin SoundPlugin SqueakSSLPlugin StarSqueakPlugin
> ThreadedFFIPlugin ThreadedARM32FFIPlugin ThreadedARM64FFIPlugin ThreadedIA32FFIPlugin
> ThreadedX64SysVFFIPlugin ThreadedX64Win64FFIPlugin
> UnicodePlugin UnixAioPlugin UUIDPlugin UnixOSProcessPlugin
> Win32OSProcessPlugin VMProfileLinuxSupportPlugin VMProfileMacSupportPlugin WeDoPlugin
> XDisplayControlPlugin)!