VM Maker: VMMaker.oscog-eem.2761.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

VM Maker: VMMaker.oscog-eem.2761.mcz

commits-2
 
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.

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)!

Reply | Threaded
Open this post in threaded view
|

Re: VM Maker: VMMaker.oscog-eem.2761.mcz

Levente Uzonyi
 
Hi Eliot,

On Wed, 24 Jun 2020, [hidden email] 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)!
Reply | Threaded
Open this post in threaded view
|

Re: VM Maker: VMMaker.oscog-eem.2761.mcz

Eliot Miranda-2
 
Hi Levente,

    I'll get #1 done by the end of the day

On Wed, Jun 24, 2020 at 12:05 PM Levente Uzonyi <[hidden email]> wrote:
 
Hi Eliot,

On Wed, 24 Jun 2020, [hidden email] 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)!


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: VM Maker: VMMaker.oscog-eem.2761.mcz

Eliot Miranda-2
In reply to this post by Levente Uzonyi
 


   FYI...

On Wed, Jun 24, 2020 at 12:05 PM Levente Uzonyi <[hidden email]> wrote:
 
Hi Eliot,

On Wed, 24 Jun 2020, [hidden email] 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.

Here's how to do #1.

1. check out the opensmalltalk-vm repository (or update your copy)
2. build a VMMaker image using opensmalltalk-vm/image/buildspurtrunkvmmaker64image.sh (or update your copy)
3. open the Source Generation workspace to find the source generaiton doits.
4. evaluate VMMaker generateVMPlugins.
5. in opensmalltalk-vm commit using scripts/gitci -u

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)!


--
_,,,^..^,,,_
best, Eliot