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

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

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

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

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

Name: VMMaker.oscog-eem.2432
Author: eem
Time: 24 August 2018, 1:18:16.336121 pm
UUID: 7b14d114-0e04-4e46-b8a7-4b5e6d87f5fe
Ancestors: VMMaker.oscog-eem.2431

Finish the VMMaker side of the failing of FFI callouts on exceptions.

Have the ThreadedFFIPlugin always call disownVM: and ownVM:, providing a flag that states if this is a threaded callout or not, and another flag that states that this is an FFI call.

Provide ffiExceptionResponse as a global variable the VM command line argument processors can set.  By default this is 0 and whether an FFI exception is caught or not depends on there being an error code in the FFI callout.  If there is one any exception will be caught; if there isn't a crash will occur as always.  If ffiExceptionResponse is < 0 FFI exceptions will never be caught and never delivered as primitive failures (which will be selected by -nofailonffiexception).  If ffiExceptionResponse is > 0 then FFI exceptions will always be caught and always delivered as primitive failures (which will be selected by -failonffiexception).

primitiveFailForFFIException:at: now fails if
- in an FFI call as indicated by DisownVMForFFICall being set in inFFIFlags
- ffiExceptionResponse > 0 or ffiExceptionResponse = 0 and newMethod has an error code

So it should always be called from the platform exception handlers such as sigsegv on Unix platforms.

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

Item was changed:
  ----- Method: CoInterpreter class>>writeVMHeaderTo:bytesPerWord:generator: (in category 'translation') -----
  writeVMHeaderTo: aStream bytesPerWord: bytesPerWord generator: aCCodeGenerator
  super writeVMHeaderTo: aStream bytesPerWord: bytesPerWord generator: aCCodeGenerator.
  aCCodeGenerator
  putDefineOf: #COGVM as: 1 on: aStream;
  putConditionalDefineOf: #COGMTVM as: 0 comment: nil on: aStream.
+ aStream cr!
- aStream cr.
- "This constant is a hack for the MT VM on SqueakV3.  Eventually it will disappear.
- But having it here rather than in CoInterpreterMT means that the interp.h header
- doesn't get regenerated every time the sources are, which is a good thing."
- aCCodeGenerator
- putDefineOf: #DisownVMLockOutFullGC as: DisownVMLockOutFullGC on: aStream!

Item was changed:
  ----- Method: CoInterpreterMT class>>initializeMiscConstants (in category 'initialization') -----
  initializeMiscConstants
 
  super initializeMiscConstants.
 
  "N.B. some of these DisownFlags are replicated in platforms/Cross/vm/sqVirtualMachine.h.
  Hence they should always be initialized."
+ DisownVMForProcessorRelinquish := 8.
- DisownVMLockOutFullGC := 1.
- DisownVMForProcessorRelinquish := 2.
 
  (InitializationOptions at: #COGMTVM ifAbsent: [false]) == false ifTrue:
  [^self].
 
  COGMTVM := true.
 
  ReturnToThreadSchedulingLoop := 2 "setjmp/longjmp code."!

Item was changed:
  ----- Method: CoInterpreterMT>>disownVM: (in category 'vm scheduling') -----
  disownVM: flags
  "Release the VM to other threads and answer the current thread's index.
  Currently valid flags:
+ DisownVMLockOutFullGC - prevent fullGCs while this thread disowns the VM
+ DisownVMForFFICall - informs the VM that it is entering an FFI call
+ DisownVMForThreading - informs the VM that it is entering an FFI call etc during which threading should be permitted
- DisownVMLockOutFullGC - prevent fullGCs while this thread disowns the VM.
  OwnVMForeignThreadFlag - indicates lowest-level entry from a foreign thread
  - not to be used explicitly by clients
  - only set by ownVMFromUnidentifiedThread
  VMAlreadyOwnedHenceDoNotDisown
  - indicates an ownVM from a callback was made when
   the vm was still owned.
  - not to be used explicitly by clients
  - only set by ownVMFromUnidentifiedThread
 
  This is the entry-point for plugins and primitives that wish to release the VM while
  performing some operation that may potentially block, and for callbacks returning
  back to some blocking operation.  If this thread does not reclaim the VM before-
  hand then when the next heartbeat occurs the thread manager will schedule a
  thread to acquire the VM which may start running the VM in place of this thread.
 
  N.B. Most of the state needed to resume after preemption is set in preemptDisowningThread."
  <api>
  <inline: false>
  | vmThread result |
  <var: #vmThread type: #'CogVMThread *'>
  self assert: self successful.
  cogit recordEventTrace ifTrue:
  [self recordTrace: TraceDisownVM thing: (objectMemory integerObjectOf: flags) source: 0].
  processHasThreadId ifFalse:
  [willNotThreadWarnCount < 10 ifTrue:
  [self print: 'warning: VM parameter 48 indicates Process doesn''t have threadId; VM will not thread'; cr.
  willNotThreadWarnCount := willNotThreadWarnCount + 1]].
  vmThread := cogThreadManager currentVMThread.
  (flags anyMask: VMAlreadyOwnedHenceDoNotDisown) ifTrue:
  [disowningVMThread := vmThread.
  vmThread state: CTMUnavailable.
  ^0].
  self cCode: ''
  inSmalltalk:
  [| range |
  range := self cStackRangeForThreadIndex: vmThread index.
  self assert: (range includes: cogit getCStackPointer).
  self assert: (range includes: cogit getCFramePointer)].
  (flags anyMask: DisownVMForProcessorRelinquish) ifTrue:
  [| proc |
  (proc := objectMemory splObj: foreignCallbackProcessSlot) ~= objectMemory nilObject ifTrue:
  [foreignCallbackPriority := self quickFetchInteger: PriorityIndex ofObject: proc].
  relinquishing := true.
  self sqLowLevelMFence].
  (flags anyMask: DisownVMLockOutFullGC) ifTrue:
  [objectMemory incrementFullGCLock].
  (noThreadingOfGUIThread and: [self inGUIThread]) ifTrue:
  [^vmThread index
  + LockGUIThreadFlag
  + (activeProcessAffined ifTrue: [0] ifFalse: [ProcessUnaffinedOnDisown])
  + (flags << DisownFlagsShift)].
  disownCount := disownCount + 1.
  disowningVMThread := vmThread.
  "self cr; cr; print: 'disownVM  Csp: '; printHex: vmThread cStackPointer; cr.
  (0 to: 16 by: 4) do:
  [:offset|
  self print: ' *(esp+'; printNum: offset; print: ': '; printHex: (stackPages longAt: cogit processor sp + offset); cr].
  cogit processor printIntegerRegistersOn: Transcript."
 
  "OwnVMForeignThreadFlag indicates lowest-level of entry by a foreign
  thread. If that's where we are then release the vmThread.  Otherwise
  indicate the vmThread is off doing something outside of the VM."
  (flags anyMask: OwnVMForeignThreadFlag)
  ifTrue:
  ["I don't think this is quite right.  Josh's use case is creating some foreign thread and then registering
  it with the VM. That's not the same as binding a process to a foreign thread given that the foreign
  callback process is about to terminate anyway (it is returning from a callback here).  So do we need
  an additional concept, that of a vmThread being either of the set known to the VM or floating?"
  self flag: 'issue with registering foreign threads with the VM'.
  (self isBoundProcess: self activeProcess) ifFalse:
  [cogThreadManager unregisterVMThread: vmThread]]
  ifFalse: [vmThread state: CTMUnavailable].
  result := vmThread index
  + (activeProcessAffined ifTrue: [0] ifFalse: [ProcessUnaffinedOnDisown])
  + (flags << DisownFlagsShift).
  cogThreadManager releaseVM.
  ^result!

Item was changed:
  VMClass subclass: #InterpreterPrimitives
+ instanceVariableNames: 'objectMemory messageSelector argumentCount newMethod primFailCode osErrorCode exceptionPC inFFIFlags profileMethod profileProcess profileSemaphore nextProfileTick preemptionYields newFinalization sHEAFn ffiExceptionResponse'
- instanceVariableNames: 'objectMemory messageSelector argumentCount newMethod primFailCode osErrorCode exceptionPC profileMethod profileProcess profileSemaphore nextProfileTick preemptionYields newFinalization sHEAFn'
  classVariableNames: 'CrossedX EndOfRun MillisecondClockMask'
  poolDictionaries: 'VMBasicConstants VMBytecodeConstants VMMethodCacheConstants VMObjectIndices VMSqueakClassIndices VMStackFrameOffsets'
  category: 'VMMaker-Interpreter'!
 
+ !InterpreterPrimitives commentStamp: 'eem 8/24/2018 11:05' prior: 0!
- !InterpreterPrimitives commentStamp: 'eem 12/7/2017 18:44' prior: 0!
  InterpreterPrimitives implements most of the VM's core primitives.  It is the root of the interpreter hierarchy so as to share the core primitives amongst the varioius interpreters.
 
  Instance Variables
+ argumentCount <Integer>
+ ffiExceptionResponse <Integer>
+ inFFIFlags <Integer>
+ messageSelector <Integer>
+ newMethod <Integer>
+ nextProfileTick <Integer>
+ objectMemory <ObjectMemory> (simulation only)
+ preemptionYields <Boolean>
+ primFailCode <Integer>
+ osErrorCode <Integer>
+ profileMethod <Integer>
+ profileProcess <Integer>
+ profileSemaphore <Integer>
- argumentCount: <Integer>
- messageSelector: <Integer>
- newMethod: <Integer>
- nextProfileTick: <Integer>
- objectMemory: <ObjectMemory> (simulation only)
- preemptionYields: <Boolean>
- primFailCode: <Integer>
- osErrorCode: <Integer>
- profileMethod: <Integer>
- profileProcess: <Integer>
- profileSemaphore: <Integer>
  secHasEnvironmentAccess <Integer>
 
  argumentCount
  - the number of arguments of the current message
 
+ ffiExceptionResponse
+ - controls system response to exceptions during FFI calls.  See primitiveFailForFFIException:at:
+
+ inFFIFlags
+ - flags recording currently only whether the system is in an FFI call
+
  messageSelector
  - the oop of the selector of the current message
 
  newMethod
  - the oop of the result of looking up the current message
 
  nextProfileTick
  - the millisecond clock value of the next profile tick (if profiling is in effect)
 
  objectMemory
  - the memory manager and garbage collector that manages the heap
 
  preemptionYields
  - a boolean controlling the process primitives.  If true (old, incorrect, blue-book semantics) a preempted process is sent to the back of its run-queue.  If false, a process preempted by a higher-priority process is put back at the head of its run queue, hence preserving cooperative scheduling within priorities.
 
  primFailCode
  - primitive success/failure flag, 0 for success, otherwise the reason code for failure
 
  osErrorCode
  - a 64-bit value settable by external primitives conveying arbitrary error codes from the operating system and/or system libraries
 
  profileMethod
  - the oop of the method at the time nextProfileTick was reached
 
  profileProcess
  - the oop of the activeProcess at the time nextProfileTick was reached
 
  profileSemaphore
  - the oop of the semaphore to signal when nextProfileTick is reached
 
  secHasEnvironmentAccess
  - the function to call to check if access to the envronment should be granted to primitiveGetenv
  !

Item was changed:
  ----- Method: InterpreterPrimitives class>>declareCVarsIn: (in category 'C translation') -----
  declareCVarsIn: aCCodeGen
  aCCodeGen
  var: 'osErrorCode' type: #sqLong;
+ var: 'exceptionPC' type: #usqInt;
  var: 'sHEAFn' declareC: 'int (*sHEAFn)() = 0' "the hasEnvironmentAccess function"!

Item was added:
+ ----- Method: InterpreterPrimitives>>initialize (in category 'initialization') -----
+ initialize
+ "Here we can initialize the variables C initializes to zero.  #initialize methods do /not/ get translated."
+ argumentCount := primFailCode := nextProfileTick := osErrorCode := exceptionPC := inFFIFlags := ffiExceptionResponse := 0.
+ newFinalization := false!

Item was changed:
  ----- Method: InterpreterPrimitives>>primitiveFailForFFIException:at: (in category 'primitive support') -----
  primitiveFailForFFIException: exceptionCode at: pc
+ <var: 'exceptionCode' type: #usqLong>
+ <var: 'pc' type: #usqInt>
+ "Set PrimErrOSError primitive failure and associated osErrorCode. Under
+ control of the ffiExceptionResponse flag, if in a primitive with an error code
+ and the inFFIFlags indicate we're in an FFI call, then fail the primitive.
+ ffiExceptionResponse < 0 never fail
+ ffiExceptionResponse = 0 fail if method has a primitive error code (default)
+ ffiExceptionResponse > 0 always fail"
- <var: 'exceptionCode' type: #sqLong>
- "Set PrimErrOSError primitive failure and associated osErrorCode."
  <api>
+ ((inFFIFlags noMask: DisownVMForFFICall) "i.e. not in an FFI call"
+ or: [ffiExceptionResponse < 0]) ifTrue: "i.e. never fail"
+ [^self].
+ osErrorCode := self cCoerceSimple: exceptionCode to: #sqLong.
- osErrorCode := exceptionCode.
  exceptionPC := pc.
+ primFailCode := PrimErrFFIException.
+ (ffiExceptionResponse > 0 "always fail..."
+ or: [(objectMemory isOopCompiledMethod: newMethod)
+ and: [self methodUsesPrimitiveErrorCode: newMethod]]) ifTrue:
+ [self activateFailingPrimitiveMethod]!
- ^primFailCode := PrimErrFFIException!

Item was changed:
  ----- Method: StackInterpreter class>>initializeMiscConstants (in category 'initialization') -----
  initializeMiscConstants
 
  super initializeMiscConstants.
  STACKVM := true.
 
  "These flags function to identify a GC operation, or
  to specify what operations the leak checker should be run for."
  GCModeFull := 1. "stop-the-world global GC"
  GCModeNewSpace := 2. "Spur's scavenge, or V3's incremental"
  GCModeIncremental := 4. "incremental global gc (Dijkstra tri-colour marking); as yet unimplemented"
  GCModeBecome := 8. "v3 post-become sweeping/Spur forwarding"
  GCModeImageSegment := 16. "just a flag for leak checking image segments"
  GCModeFreeSpace := 32. "just a flag for leak checking free space; Spur only"
  GCCheckPrimCall := 64. "just a flag for leak checking external primitive calls"
 
  StackPageTraceInvalid := -1.
  StackPageUnreached := 0.
  StackPageReachedButUntraced := 1.
  StackPageTraced := 2.
 
  DumpStackOnLowSpace := 0.
  MillisecondClockMask := 16r1FFFFFFF.
  "Note: The external primitive table should actually be dynamically sized but for the sake of inferior platforms (e.g., Mac :-) who cannot allocate memory in any reasonable way, we keep it static (and cross our fingers...)"
  MaxExternalPrimitiveTableSize := 4096. "entries"
 
  MaxJumpBuf := 32. "max. callback depth"
  FailImbalancedPrimitives := InitializationOptions at: #FailImbalancedPrimitives ifAbsentPut: [true].
  EnforceAccessControl := InitializationOptions at: #EnforceAccessControl ifAbsent: [true].
 
+ ReturnToInterpreter := 1. "setjmp/longjmp code."
+
+ "N.B. some of these DisownFlags are replicated in platforms/Cross/vm/sqVirtualMachine.h.
+ Hence they should always be initialized."
+ DisownVMLockOutFullGC := 1.
+ DisownVMForFFICall := 2.
+ DisownVMForThreading := 4
+ !
- ReturnToInterpreter := 1 "setjmp/longjmp code."!

Item was changed:
  ----- Method: StackInterpreter class>>mustBeGlobal: (in category 'translation') -----
  mustBeGlobal: var
  "Answer if a variable must be global and exported.  Used for inst vars that are accessed from VM support code."
 
  ^(super mustBeGlobal: var)
    or: [(self objectMemoryClass mustBeGlobal: var)
    or: [(#('interpreterProxy' 'interpreterVersion' 'inIOProcessEvents'
  'deferDisplayUpdates' 'extraVMMemory'
  'showSurfaceFn' 'displayBits' 'displayWidth' 'displayHeight' 'displayDepth'
  'desiredNumStackPages' 'desiredEdenBytes'
  'breakLookupClassTag' 'breakSelector' 'breakSelectorLength' 'sendTrace' 'checkAllocFiller' 'checkedPluginName'
+ 'reenterInterpreter' 'suppressHeartbeatFlag' 'ffiExceptionResponse'
- 'reenterInterpreter' 'suppressHeartbeatFlag'
  'debugCallbackInvokes' 'debugCallbackPath' 'debugCallbackReturns') includes: var)
    or: [ "This allows slow machines to define bytecodeSetSelector as 0
  to avoid the interpretation overhead."
  MULTIPLEBYTECODESETS not and: [var = 'bytecodeSetSelector']]]]!

Item was changed:
  ----- Method: StackInterpreter class>>writeVMHeaderTo:bytesPerWord:generator: (in category 'translation') -----
  writeVMHeaderTo: aStream bytesPerWord: bytesPerWord generator: aCCodeGenerator
  super writeVMHeaderTo: aStream bytesPerWord: bytesPerWord generator: aCCodeGenerator.
  SistaVM ifTrue:
  [aCCodeGenerator putDefineOf: #SistaVM as: 1 on: aStream].
  NewspeakVM ifTrue:
  [aCCodeGenerator putDefineOf: #NewspeakVM as: 1 on: aStream].
  MULTIPLEBYTECODESETS ifTrue:
  [aCCodeGenerator putDefineOf: #MULTIPLEBYTECODESETS as: 1 on: aStream].
  IMMUTABILITY ifTrue:
  [aCCodeGenerator
  putConditionalDefineOf: #IMMUTABILITY
  as: 1
  comment: 'Allow this to be overridden on the compiler command line'
  on: aStream].
  SistaVM | NewspeakVM | MULTIPLEBYTECODESETS | IMMUTABILITY ifTrue:
  [aStream cr].
  aCCodeGenerator putDefineOf: #STACKVM as: 1 on: aStream.
  (InitializationOptions at: #SpurObjectMemory ifAbsent: false) ifTrue:
+ [aCCodeGenerator putDefineOf: #SPURVM as: 1 on: aStream].
+
+ aCCodeGenerator
+ putDefineOf: #DisownVMLockOutFullGC as: DisownVMLockOutFullGC on: aStream;
+ putDefineOf: #DisownVMForFFICall as: DisownVMForFFICall on: aStream;
+ putDefineOf: #DisownVMForThreading as: DisownVMForThreading on: aStream!
- [aCCodeGenerator putDefineOf: #SPURVM as: 1 on: aStream]!

Item was changed:
  ----- Method: StackInterpreter>>cloneOSErrorObj:numSlots: (in category 'message sending') -----
  cloneOSErrorObj: errObj numSlots: numSlots
  "If errObj is a pointer object with at least two slots, then answer a clone
   of the error object with the second slot set to the value of osErrorCode,
   and if an PrimErrFFIException, then the third slow with the exceptionPC."
  | clone |
  <inline: true>
  clone := objectMemory hasSpurMemoryManagerAPI
  ifTrue: [objectMemory
  eeInstantiateSmallClassIndex: (objectMemory classIndexOf: errObj)
  format: objectMemory nonIndexablePointerFormat
  numSlots: numSlots]
  ifFalse: [objectMemory
  eeInstantiateSmallClass: (objectMemory fetchClassOfNonImm: errObj)
  numSlots: numSlots].
  0 to: numSlots - 1 do:
  [:i| objectMemory
  storePointerUnchecked: i
  ofObject: clone
  withValue: (objectMemory fetchPointer: i ofObject: errObj)].
  (numSlots > 2
  and: [primFailCode = PrimErrFFIException])
  ifTrue:
  [objectMemory
  storePointerUnchecked: 1
  ofObject: clone
+ withValue: (self positive64BitIntegerFor: (self cCoerceSimple: osErrorCode to: #usqLong));
- withValue: (self positive64BitIntegerFor: osErrorCode);
  storePointerUnchecked: 2
  ofObject: clone
+ withValue: (self positiveMachineIntegerFor: exceptionPC)]
- withValue: (self positive64BitIntegerFor: exceptionPC)]
  ifFalse:
  [objectMemory
  storePointerUnchecked: 1
  ofObject: clone
  withValue: (self signed64BitIntegerFor: osErrorCode)].
  ^clone!

Item was changed:
  ----- Method: StackInterpreter>>disownVM: (in category 'vm scheduling') -----
  disownVM: flags
  <api>
  <inline: false>
  "Release the VM to other threads and answer the current thread's index.
+ Currently valid flags for the non-threaded VM are:
+ DisownVMLockOutFullGC - prevent fullGCs while this thread disowns the VM
+ DisownVMForFFICall - informs the VM that it is entering an FFI call
 
  This is the entry-point for plugins and primitives that wish to release the VM while
  performing some operation that may potentially block, and for callbacks returning
  back to some blocking operation.  While this exists for the threaded FFI VM we use
  it to reset newMethod and the argumentCount after a callback."
  self assert: ((objectMemory isIntegerObject: flags)
  and: [(objectMemory integerValueOf: flags)
  between: 0
  and: (self argumentCountOfMethodHeader: -1)]).
  self assert: primFailCode = 0.
+
+ "If DisownVMForFFICall this is from the FFI plugin and we're making a callout; remember the fact."
+ (flags anyMask: DisownVMForFFICall) ifTrue:
+ [self assert: ((objectMemory isOopCompiledMethod: newMethod)
+ and: [(self argumentCountOf: newMethod) = argumentCount]).
+ inFFIFlags := DisownVMForFFICall.
+ ^flags].
+
+ "Otherwise this is a callback return; restore argumentCount and newMethod as per the ownVM: on callback."
  argumentCount := objectMemory integerValueOf: flags.
  newMethod := self popStack.
  self assert: ((objectMemory isOopCompiledMethod: newMethod)
  and: [(self argumentCountOf: newMethod) = argumentCount]).
  ^0!

Item was changed:
  ----- Method: StackInterpreter>>initialize (in category 'initialization') -----
  initialize
  "Here we can initialize the variables C initializes to zero.  #initialize methods do /not/ get translated."
+ super initialize.
  checkAllocFiller := false. "must precede initializeObjectMemory:"
- primFailCode := 0.
  stackLimit := 0. "This is also the initialization flag for the stack system."
  stackPage := overflowedPage := 0.
  extraFramesToMoveOnOverflow := 0.
  bytecodeSetSelector := 0.
  highestRunnableProcessPriority := 0.
- nextProfileTick := 0.
  nextPollUsecs := 0.
  nextWakeupUsecs := 0.
  tempOop := tempOop2 := theUnknownShort := 0.
  interruptPending := false.
  inIOProcessEvents := 0.
  fullScreenFlag := 0.
  deferDisplayUpdates := false.
  displayBits := displayWidth := displayHeight := displayDepth := 0.
  pendingFinalizationSignals := statPendingFinalizationSignals := 0.
  globalSessionID := 0.
  jmpDepth := 0.
  longRunningPrimitiveStartUsecs := longRunningPrimitiveStopUsecs := 0.
  maxExtSemTabSizeSet := false.
  debugCallbackInvokes := debugCallbackPath := debugCallbackReturns := 0.
  statForceInterruptCheck := statStackOverflow := statCheckForEvents :=
  statProcessSwitch := statIOProcessEvents := statStackPageDivorce :=
  statIdleUsecs := 0!

Item was added:
+ ----- Method: StackInterpreter>>methodUsesPrimitiveErrorCode: (in category 'primitive support') -----
+ methodUsesPrimitiveErrorCode: aMethodObj
+ "Answer if aMethodObj contains a primitive and uses the primitive error code."
+ <inline: true>
+ | methodHeader |
+ methodHeader := objectMemory methodHeaderOf: aMethodObj.
+ ^(self primitiveIndexOfMethod: aMethodObj header: methodHeader) > 0
+  and: [(self longStoreBytecodeForHeader: methodHeader)
+ = (objectMemory
+ fetchByte: (self startPCOfMethod: aMethodObj) + (self sizeOfCallPrimitiveBytecode: methodHeader)
+ ofObject: aMethodObj)]!

Item was changed:
  ----- Method: StackInterpreter>>ownVM: (in category 'vm scheduling') -----
  ownVM: threadIndexAndFlags
  <api>
  <inline: false>
  "This is the entry-point for plugins and primitives that wish to reacquire the VM after having
  released it via disownVM or callbacks that want to acquire it without knowing their ownership
  status.  While this exists for the threaded FFI VM we use it to reset newMethod and the
  argumentCount after a callback.
 
- Answer the argumentCount encoded as a SmallInteger if the current thread is the VM thread.
  Answer -1 if the current thread is unknown to the VM and fails to take ownership."
  <var: 'amInVMThread' declareC: 'extern sqInt amInVMThread(void)'>
  self amInVMThread ifFalse:
  [^-1].
  self assert: primFailCode = 0.
  self assert: ((objectMemory isOopCompiledMethod: newMethod)
  and: [(self argumentCountOf: newMethod) = argumentCount]).
+
+ "If DisownVMForFFICall this is from the FFI plugin and we're returning from a callout."
+ (threadIndexAndFlags anyMask: DisownVMForFFICall) ifTrue:
+ [inFFIFlags := 0.
+ ^0].
+
+ "Otherwise this is a callback; stash newMethod on the stack and argumentCount in the flags
+ we'll get back when the calback calls disownVM:."
  self push: newMethod.
  ^objectMemory integerObjectOf: argumentCount!

Item was changed:
  ----- Method: ThreadedARMFFIPlugin>>ffiCalloutTo:SpecOnStack:in: (in category 'callout support') -----
  ffiCalloutTo: procAddr SpecOnStack: specOnStack in: calloutState
  <var: #procAddr type: #'void *'>
  <var: #calloutState type: #'CalloutState *'>
  <var: #loadFloatRegs declareC: 'extern void loadFloatRegs(double, double, double, double, double, double, double, double)'>
  "Go out, call this guy and create the return value.  This *must* be inlined because of
  the alloca of the outgoing stack frame in ffiCall:WithFlags:NumArgs:Args:AndTypes:"
  | myThreadIndex atomicType floatRet intRet |
  <var: #floatRet type: #double>
  <var: #intRet type: #usqLong>
  <inline: true>
+ myThreadIndex := interpreterProxy disownVM: (self disownFlagsFor: calloutState).
- self maybeDisownVM: calloutState threadIndexInto: [:threadIndex| myThreadIndex := threadIndex].
 
  calloutState floatRegisterIndex > 0 ifTrue:
  [self
  load: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 0)) to: 'double *') at: 0)
  Flo: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 2)) to: 'double *') at: 0)
  a: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 4)) to: 'double *') at: 0)
  t: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 6)) to: 'double *') at: 0)
  R: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 8)) to: 'double *') at: 0)
  e: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 10)) to: 'double *') at: 0)
  g: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 12)) to: 'double *') at: 0)
  s: ((self cCoerceSimple: (self addressOf: (calloutState floatRegisters at: 14)) to: 'double *') at: 0)].
 
  (self allocaLiesSoSetSpBeforeCall or: [self mustAlignStack]) ifTrue:
  [self setsp: calloutState argVector].
 
  atomicType := self atomicTypeOf: calloutState ffiRetHeader.
  (atomicType >> 1) = (FFITypeSingleFloat >> 1) ifTrue:
  [atomicType = FFITypeSingleFloat
  ifTrue:
  [floatRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'float (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)]
  ifFalse: "atomicType = FFITypeDoubleFloat"
  [floatRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'double (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)].
 
  "undo any callee argument pops because it may confuse stack management with the alloca."
  (self isCalleePopsConvention: calloutState callFlags) ifTrue:
  [self setsp: calloutState argVector].
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  ^interpreterProxy floatObjectOf: floatRet].
 
  intRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'usqIntptr_t (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3).
 
  "undo any callee argument pops because it may confuse stack management with the alloca."
  (self isCalleePopsConvention: calloutState callFlags) ifTrue:
  [self setsp: calloutState argVector].
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  (calloutState ffiRetHeader anyMask: FFIFlagPointer+FFIFlagStructure) ifTrue:
  ["Note: Order is important here since FFIFlagPointer + FFIFlagStructure is used to represent
  'typedef void* VoidPointer' and VoidPointer must be returned as pointer *not* as struct."
  (calloutState ffiRetHeader anyMask: FFIFlagPointer) ifTrue:
  [^self ffiReturnPointer: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
  ^self ffiReturnStruct: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
 
  ^self ffiCreateIntegralResultOop: intRet ofAtomicType: atomicType in: calloutState!

Item was added:
+ ----- Method: ThreadedFFIPlugin>>disownFlagsFor: (in category 'primitive support') -----
+ disownFlagsFor: calloutState
+ <var: #calloutState type: #'CalloutState *'>
+ <inline: #always>
+ ^self cppIf: COGMTVM
+ ifTrue: [(calloutState callFlags anyMask: FFICallFlagThreaded)
+ ifTrue: [DisownVMForFFICall+DisownVMForThreading]
+ ifFalse: [DisownVMForFFICall]]
+ ifFalse: [DisownVMForFFICall]!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>dispatchFunctionPointer: (in category 'callout support') -----
  dispatchFunctionPointer: aFunctionPointer
  "In C aFunctionPointer is void (*aFunctionPointer)()"
  <cmacro: '(aFunctionPointer) (aFunctionPointer)()'>
  "To write the FFI call failure code we simulate invoking the production VM's
  fatal exception handlers (sigsegv on Unix, squeakExceptionHandler on WIN32, et al)."
  ^[self perform: aFunctionPointer]
  on: Error
  do: [:ex|
  interpreterProxy
+ primitiveFailForFFIException: PrimErrFFIException
+ at: aFunctionPointer asInteger.
+ ex pass "NOTREACHED if VM is handling FFI exceptions"]!
- primitiveFailForFFIException: PrimErrFFIException at: aFunctionPointer asInteger;
- activateFailingPrimitiveMethod]!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>dispatchFunctionPointer:with:with:with:with: (in category 'callout support') -----
  dispatchFunctionPointer: aFunctionPointer with: int1 with: int2 with: int3 with: int4
  "In C aFunctionPointer is void (*aFunctionPointer)(int, int, int, int)"
  <cmacro: '(aFunctionPointer, int1, int2, int3, int4) (aFunctionPointer)(int1, int2, int3, int4)'>
  "To write the FFI call failure code we simulate invoking the production VM's
  fatal exception handlers (sigsegv on Unix, squeakExceptionHandler on WIN32, et al)."
  ^[self perform: aFunctionPointer
  with: int1
  with: int2
  with: int3
  with: int4]
  on: Error
  do: [:ex|
  interpreterProxy
+ primitiveFailForFFIException: PrimErrFFIException
+ at: aFunctionPointer asInteger.
+ ex pass "NOTREACHED if VM is handling FFI exceptions"]!
- primitiveFailForFFIException: PrimErrFFIException at: aFunctionPointer asInteger;
- activateFailingPrimitiveMethod]!

Item was changed:
  ----- Method: ThreadedFFIPlugin>>dispatchFunctionPointer:with:with:with:with:with:with: (in category 'callout support') -----
  dispatchFunctionPointer: aFunctionPointer with: int1 with: int2 with: int3 with: int4 with: int5 with: int6
  "In C aFunctionPointer is void (*aFunctionPointer)(int, int, int, int, int, int)"
+ <cmacro: '(aFunctionPointer, int1, int2, int3, int4, int5, int6) (aFunctionPointer)(int1, int2, int3, int4, int5, int6)'>
- <cmacro: '(aFunctionPointer, int1, int2, int3, int4) (aFunctionPointer)(int1, int2, int3, int4, int5, int6)'>
  "To write the FFI call failure code we simulate invoking the production VM's
  fatal exception handlers (sigsegv on Unix, squeakExceptionHandler on WIN32, et al)."
  ^[self perform: aFunctionPointer
  with: int1
  with: int2
  with: int3
  with: int4
  with: int5
  with: int6]
  on: Error
  do: [:ex|
  interpreterProxy
+ primitiveFailForFFIException: PrimErrFFIException
+ at: aFunctionPointer asInteger.
+ ex pass "NOTREACHED if VM is handling FFI exceptions"]!
- primitiveFailForFFIException: PrimErrFFIException at: aFunctionPointer asInteger;
- activateFailingPrimitiveMethod]!

Item was removed:
- ----- Method: ThreadedFFIPlugin>>maybeDisownVM:threadIndexInto: (in category 'primitive support') -----
- maybeDisownVM: calloutState threadIndexInto: aBlock
- <var: #calloutState type: #'CalloutState *'>
- <inline: #always>
- self cppIf: COGMTVM
- ifTrue:
- [(calloutState callFlags anyMask: FFICallFlagThreaded) ifTrue:
- [aBlock value: (interpreterProxy disownVM: 0)]]!

Item was removed:
- ----- Method: ThreadedFFIPlugin>>maybeOwnVM:threadIndex: (in category 'primitive support') -----
- maybeOwnVM: calloutState threadIndex: myThreadIndex
- <var: #calloutState type: #'CalloutState *'>
- <inline: #always>
- self cppIf: COGMTVM ifTrue:
- [(calloutState callFlags anyMask: FFICallFlagThreaded) ifTrue:
- [interpreterProxy ownVM: myThreadIndex]]!

Item was changed:
  ----- Method: ThreadedIA32FFIPlugin>>ffiCalloutTo:SpecOnStack:in: (in category 'callout support') -----
  ffiCalloutTo: procAddr SpecOnStack: specOnStack in: calloutState
  <var: #procAddr type: #'void *'>
  <var: #calloutState type: #'CalloutState *'>
  "Go out, call this guy and create the return value.  This *must* be inlined because of
  the alloca of the outgoing stack frame in ffiCall:WithFlags:NumArgs:Args:AndTypes:"
  | myThreadIndex atomicType floatRet intRet |
  <var: #floatRet type: #double>
  <var: #intRet type: #usqLong>
  <inline: true>
+ myThreadIndex := interpreterProxy disownVM: (self disownFlagsFor: calloutState).
- self maybeDisownVM: calloutState threadIndexInto: [:threadIndex| myThreadIndex := threadIndex].
 
  (self allocaLiesSoSetSpBeforeCall or: [self mustAlignStack]) ifTrue:
  [self setsp: calloutState argVector].
 
  atomicType := self atomicTypeOf: calloutState ffiRetHeader.
  (atomicType >> 1) = (FFITypeSingleFloat >> 1) ifTrue:
  [floatRet := self dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'double (*)()').
 
  "undo any callee argument pops because it may confuse stack management with the alloca."
  (self isCalleePopsConvention: calloutState callFlags) ifTrue:
  [self setsp: calloutState argVector].
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  ^interpreterProxy floatObjectOf: floatRet].
 
  intRet := self dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'usqLong (*)()').
 
  "undo any callee argument pops because it may confuse stack management with the alloca."
  (self isCalleePopsConvention: calloutState callFlags) ifTrue:
  [self setsp: calloutState argVector].
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  (calloutState ffiRetHeader anyMask: FFIFlagPointer+FFIFlagStructure) ifTrue:
  ["Note: Order is important here since FFIFlagPointer + FFIFlagStructure is used to represent
  'typedef void* VoidPointer' and VoidPointer must be returned as pointer *not* as struct."
  (calloutState ffiRetHeader anyMask: FFIFlagPointer) ifTrue:
  [^self ffiReturnPointer: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
  ^self ffiReturnStruct: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
 
  ^self ffiCreateIntegralResultOop: intRet ofAtomicType: atomicType in: calloutState!

Item was changed:
  ----- Method: ThreadedX64SysVFFIPlugin>>ffiCalloutTo:SpecOnStack:in: (in category 'callout support') -----
  ffiCalloutTo: procAddr SpecOnStack: specOnStack in: calloutState
  <var: #procAddr type: #'void *'>
  <var: #calloutState type: #'CalloutState *'>
  <var: #loadFloatRegs declareC: 'extern void loadFloatRegs(double, double, double, double, double, double, double, double)'>
  "Go out, call this guy and create the return value.  This *must* be inlined because of
  the alloca of the outgoing stack frame in ffiCall:WithFlags:NumArgs:Args:AndTypes:"
  | myThreadIndex atomicType floatRet intRet |
  <var: #floatRet type: #double>
  <var: #intRet type: #SixteenByteReturn>
  <inline: true>
+ myThreadIndex := interpreterProxy disownVM: (self disownFlagsFor: calloutState).
- self maybeDisownVM: calloutState threadIndexInto: [:threadIndex| myThreadIndex := threadIndex].
 
  calloutState floatRegisterIndex > 0 ifTrue:
  [self
  load: (calloutState floatRegisters at: 0)
  Flo: (calloutState floatRegisters at: 1)
  a: (calloutState floatRegisters at: 2)
  t: (calloutState floatRegisters at: 3)
  R: (calloutState floatRegisters at: 4)
  e: (calloutState floatRegisters at: 5)
  g: (calloutState floatRegisters at: 6)
  s: (calloutState floatRegisters at: 7)].
 
  (self allocaLiesSoSetSpBeforeCall or: [self mustAlignStack]) ifTrue:
  [self setsp: calloutState argVector].
 
  atomicType := self atomicTypeOf: calloutState ffiRetHeader.
  (atomicType >> 1) = (FFITypeSingleFloat >> 1) ifTrue:
  [atomicType = FFITypeSingleFloat
  ifTrue:
  [floatRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'float (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)
  with: (calloutState integerRegisters at: 4)
  with: (calloutState integerRegisters at: 5)]
  ifFalse: "atomicType = FFITypeDoubleFloat"
  [floatRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'double (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)
  with: (calloutState integerRegisters at: 4)
  with: (calloutState integerRegisters at: 5)].
 
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  ^interpreterProxy floatObjectOf: floatRet].
 
  intRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'SixteenByteReturn (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)
  with: (calloutState integerRegisters at: 4)
  with: (calloutState integerRegisters at: 5).
 
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  (calloutState ffiRetHeader anyMask: FFIFlagPointer+FFIFlagStructure) ifTrue:
  ["Note: Order is important here since FFIFlagPointer + FFIFlagStructure is used to represent
  'typedef void* VoidPointer' and VoidPointer must be returned as pointer *not* as struct."
  (calloutState ffiRetHeader anyMask: FFIFlagPointer) ifTrue:
  [^self ffiReturnPointer: intRet a ofType: (self ffiReturnType: specOnStack) in: calloutState].
  ^self ffiReturnStruct: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
 
  ^self ffiCreateIntegralResultOop: intRet a ofAtomicType: atomicType in: calloutState!

Item was changed:
  ----- Method: ThreadedX64Win64FFIPlugin>>ffiCalloutTo:SpecOnStack:in: (in category 'callout support') -----
  ffiCalloutTo: procAddr SpecOnStack: specOnStack in: calloutState
  <var: #procAddr type: #'void *'>
  <var: #calloutState type: #'CalloutState *'>
  <var: #loadFloatRegs declareC: 'extern void loadFloatRegs(double, double, double, double)'>
  "Go out, call this guy and create the return value.  This *must* be inlined because of
  the alloca of the outgoing stack frame in ffiCall:WithFlags:NumArgs:Args:AndTypes:"
  | myThreadIndex atomicType floatRet intRet |
  <var: #floatRet type: #double>
  <var: #intRet type: #usqLong>
  <inline: true>
+ myThreadIndex := interpreterProxy disownVM: (self disownFlagsFor: calloutState).
- self maybeDisownVM: calloutState threadIndexInto: [:threadIndex| myThreadIndex := threadIndex].
 
  calloutState floatRegisterSignature > 0 ifTrue:
  [self
  load: (calloutState floatRegisters at: 0)
  Flo: (calloutState floatRegisters at: 1)
  atR: (calloutState floatRegisters at: 2)
  egs: (calloutState floatRegisters at: 3)].
 
  (self allocaLiesSoSetSpBeforeCall or: [self mustAlignStack]) ifTrue:
  [self setsp: calloutState argVector].
 
  atomicType := self atomicTypeOf: calloutState ffiRetHeader.
  (atomicType >> 1) = (FFITypeSingleFloat >> 1) ifTrue:
  [atomicType = FFITypeSingleFloat
  ifTrue:
  [floatRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'float (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)]
  ifFalse: "atomicType = FFITypeDoubleFloat"
  [floatRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'double (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3)].
 
  "undo any callee argument pops because it may confuse stack management with the alloca."
  (self isCalleePopsConvention: calloutState callFlags) ifTrue:
  [self setsp: calloutState argVector].
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  ^interpreterProxy floatObjectOf: floatRet].
 
  intRet := self
  dispatchFunctionPointer: (self cCoerceSimple: procAddr to: 'usqIntptr_t (*)(sqIntptr_t, sqIntptr_t, sqIntptr_t, sqIntptr_t)')
  with: (calloutState integerRegisters at: 0)
  with: (calloutState integerRegisters at: 1)
  with: (calloutState integerRegisters at: 2)
  with: (calloutState integerRegisters at: 3).
 
  "undo any callee argument pops because it may confuse stack management with the alloca."
  (self isCalleePopsConvention: calloutState callFlags) ifTrue:
  [self setsp: calloutState argVector].
+ interpreterProxy ownVM: myThreadIndex.
- self maybeOwnVM: calloutState threadIndex: myThreadIndex.
 
  (calloutState ffiRetHeader anyMask: FFIFlagPointer+FFIFlagStructure) ifTrue:
  ["Note: Order is important here since FFIFlagPointer + FFIFlagStructure is used to represent
  'typedef void* VoidPointer' and VoidPointer must be returned as pointer *not* as struct."
  (calloutState ffiRetHeader anyMask: FFIFlagPointer) ifTrue:
  [^self ffiReturnPointer: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
  ^self ffiReturnStruct: intRet ofType: (self ffiReturnType: specOnStack) in: calloutState].
 
  ^self ffiCreateIntegralResultOop: intRet ofAtomicType: atomicType in: calloutState!

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