VM Maker: VMMaker.oscog-eem.2570.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.2570.mcz

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

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

Name: VMMaker.oscog-eem.2570
Author: eem
Time: 10 October 2019, 5:25:36.463979 pm
UUID: b61e294a-cb2a-4d9a-9e7e-8cc17676c920
Ancestors: VMMaker.oscog-eem.2569

Cog:
Refactor to make CFramePointer and CStackPointer private variables of CoInterpreter.  This needs associated C changes to isCFramePointerInUse (changes are on deck in my repository), which now takes pointers to the two variables instead of referencing them directly.  The result is that CFramePointer and CStackPointer are accessed via VarBaseReg on relevant platforms.  This nearly halves the size of the generated trampolines/enilopmarts on x86_64.

Fix a reg arg order overwrite problem with the ceDirectedSuperSend?Args trampolines on ARM32.

Simulation:
Eliminate use of the near address hack for CFramePointer and CStackPointer on X64 so that simulation is faithful to the new scheme above.

Slang: Fix a bug in inferring the type of addressOf:[put:].

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

Item was changed:
  ----- Method: CCodeGenerator>>returnTypeForSend:in:ifNil: (in category 'type inference') -----
  returnTypeForSend: sendNode in: aTMethod ifNil: typeIfNil
  "Answer the return type for a send.  Unbound sends default to typeIfNil.
  Methods with types as yet unknown have a type determined either by the
  kernelReturnTypes or the table below, or, if they are in neither set, then nil.
  The inferred type should match as closely as possible the C type of
  generated expessions so that inlining would not change the expression.
  If there is a method for sel but its return type is as yet unknown it mustn't
  be defaulted, since on a subsequent pass its type may be computable."
  | sel methodOrNil |
  methodOrNil := self anyMethodNamed: (sel := sendNode selector).
  (methodOrNil notNil and: [methodOrNil returnType notNil]) ifTrue:
  [^self baseTypeForType: methodOrNil returnType].
  ^kernelReturnTypes
  at: sel
  ifAbsent:
  [sel
  caseOf: {
  [#integerValueOf:] -> [#sqInt].
  [#isIntegerObject:] -> [#int].
  [#negated] -> [self promoteArithmeticTypes: (sendNode receiver typeFrom: self in: aTMethod) and: #int].
  [#+] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#-] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#*] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#/] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#//] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#\\] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#rem:] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#quo:] -> [self typeForArithmetic: sendNode in: aTMethod].
  "C99 Sec Bitwise shift operators ... 3 Sematics ...
  The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand..."
  [#>>] -> [sendNode receiver typeFrom: self in: aTMethod].
  [#<<] -> [sendNode receiver typeFrom: self in: aTMethod].
+ [#addressOf:] -> [(sendNode args first typeFrom: self in: aTMethod)
- [#addressOf:] -> [(sendNode receiver typeFrom: self in: aTMethod)
  ifNil: [#sqInt]
+ ifNotNil: [:type| type, (type last isSeparator ifTrue: ['*'] ifFalse: [' *'])]].
+ [#addressOf:put:] -> [(sendNode args first typeFrom: self in: aTMethod)
+ ifNil: [#sqInt]
+ ifNotNil: [:type| type, (type last isSeparator ifTrue: ['*'] ifFalse: [' *'])]].
- ifNotNil: [:type| type, (type last isLetter ifTrue: [' *'] ifFalse: ['*'])]].
  [#at:] -> [self typeForDereference: sendNode in: aTMethod].
  [#bitAnd:] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#bitOr:] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#bitXor:] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#bitClear:] -> [self typeForArithmetic: sendNode in: aTMethod].
  [#bitInvert32] -> [#'unsigned int'].
  [#bitInvert64] -> [self promoteArithmeticTypes: (sendNode receiver typeFrom: self in: aTMethod) and: #int].
  [#byteSwap32] -> [#'unsigned int'].
  [#byteSwap64] -> [#'unsigned long long'].
  [#byteSwapped32IfBigEndian:] -> [#'unsigned int'].
  [#byteSwapped64IfBigEndian:] -> [#'unsigned long long'].
  [#=] -> [#int].
  [#~=] -> [#int].
  [#==] -> [#int].
  [#~~] -> [#int].
  [#<] -> [#int].
  [#<=] -> [#int].
  [#>] -> [#int].
  [#>=] -> [#int].
  [#between:and:] -> [#int].
  [#anyMask:] -> [#int].
  [#allMask:] -> [#int].
  [#noMask:] -> [#int].
  [#isNil] -> [#int].
  [#notNil] -> [#int].
  [#&] -> [#int].
  [#|] -> [#int].
  [#not] -> [#int].
  [#asFloat] -> [#double].
  [#atan] -> [#double].
  [#exp] -> [#double].
  [#log] -> [#double].
  [#sin] -> [#double].
  [#sqrt] -> [#double].
  [#asLong] -> [#long].
  [#asInteger] -> [#sqInt].
  [#asIntegerPtr] -> [#'sqIntptr_t'].
  [#asUnsignedInteger] -> [#usqInt].
  [#asUnsignedIntegerPtr]-> [#'usqIntptr_t'].
  [#asUnsignedLong] -> [#'unsigned long'].
  [#asUnsignedLongLong] -> [#'unsigned long long'].
  [#asVoidPointer] -> [#'void *'].
  [#signedIntToLong] -> [#usqInt]. "c.f. generateSignedIntToLong:on:indent:"
  [#signedIntToShort] -> [#usqInt]. "c.f. generateSignedIntToShort:on:indent:"
  [#cCoerce:to:] -> [self conventionalTypeForType: sendNode args last value].
  [#cCoerceSimple:to:] -> [self conventionalTypeForType: sendNode args last value].
  [#sizeof:] -> [#'usqIntptr_t']. "Technically it's a size_t but it matches on target architectures so far..."
  [#ifTrue:ifFalse:] -> [self typeForConditional: sendNode in: aTMethod].
  [#ifFalse:ifTrue:] -> [self typeForConditional: sendNode in: aTMethod].
  [#ifTrue:] -> [self typeForConditional: sendNode in: aTMethod].
  [#ifFalse:] -> [self typeForConditional: sendNode in: aTMethod].
  [#and:] -> [#sqInt].
  [#or:] -> [#sqInt].
  [#caseOf:] -> [self typeFor: sendNode args first in: aTMethod] }
  otherwise: "If there /is/ a method for sel but its return type is as yet unknown it /mustn't/ be defaulted,
  since on a subsequent pass its type may be computable.  Only default unbound selectors."
  [methodOrNil ifNotNil: [nil] ifNil: [typeIfNil]]]!

Item was changed:
  StackInterpreterPrimitives subclass: #CoInterpreter
+ instanceVariableNames: 'cogit cogMethodZone gcMode cogCodeSize desiredCogCodeSize heapBase lastCoggableInterpretedBlockMethod deferSmash deferredSmash primTraceLog primTraceLogIndex traceLog traceLogIndex traceSources cogCompiledCodeCompactionCalledFor statCodeCompactionCount statCodeCompactionUsecs lastUncoggableInterpretedBlockMethod flagInterpretedMethods maxLiteralCountForCompile minBackwardJumpCountForCompile CFramePointer CStackPointer'
- instanceVariableNames: 'cogit cogMethodZone gcMode cogCodeSize desiredCogCodeSize heapBase lastCoggableInterpretedBlockMethod deferSmash deferredSmash primTraceLog primTraceLogIndex traceLog traceLogIndex traceSources cogCompiledCodeCompactionCalledFor statCodeCompactionCount statCodeCompactionUsecs lastUncoggableInterpretedBlockMethod flagInterpretedMethods maxLiteralCountForCompile minBackwardJumpCountForCompile'
  classVariableNames: 'CSCallbackEnter CSCallbackLeave CSCheckEvents CSEnterCriticalSection CSExitCriticalSection CSOwnVM CSResume CSSignal CSSuspend CSSwitchIfNeccessary CSThreadBind CSThreadSchedulingLoop CSWait CSYield HasBeenReturnedFromMCPC HasBeenReturnedFromMCPCOop MFMethodFlagFrameIsMarkedFlag MinBackwardJumpCountForCompile PrimNumberHashMultiply PrimTraceLogSize RumpCStackSize TraceBlockActivation TraceBlockCreation TraceBufferSize TraceCodeCompaction TraceContextSwitch TraceDisownVM TraceFullGC TraceIncrementalGC TraceIsFromInterpreter TraceIsFromMachineCode TraceOwnVM TracePreemptDisowningThread TracePrimitiveFailure TracePrimitiveRetry TraceSources TraceStackOverflow TraceThreadSwitch TraceVMCallback TraceVMCallbackReturn'
  poolDictionaries: 'CogMethodConstants VMStackFrameOffsets'
  category: 'VMMaker-JIT'!
 
+ !CoInterpreter commentStamp: 'eem 10/10/2019 09:08' prior: 0!
- !CoInterpreter commentStamp: 'eem 12/7/2017 11:19' prior: 0!
  I am a variant of the StackInterpreter that can co-exist with the Cog JIT.  I interpret unjitted methods, either because they have been found for the first time or because they are judged to be too big to JIT.  See CogMethod class's comment for method interoperability.
 
  cogCodeSize
  - the current size of the machine code zone
 
  cogCompiledCodeCompactionCalledFor
  - a variable set when the machine code zone runs out of space, causing a machine code zone compaction at the next available opportunity
 
  cogMethodZone
  - the manager for the machine code zone (instance of CogMethodZone)
 
  cogit
  - the JIT (co-jit) (instance of SimpleStackBasedCogit, StackToRegisterMappoingCogit, etc)
 
  deferSmash
  - a flag causing deferral of smashes of the stackLimit around the call of functionSymbol (for assert checks)
 
  deferredSmash
  - a flag noting deferral of smashes of the stackLimit around the call of functionSymbol (for assert checks)
 
  desiredCogCodeSize
  - the desred size of the machine code zone, set at startup or via primitiveVMParameter to be written at snapshot time
 
  flagInterpretedMethods
  - true if methods that are interpreted shoudl have their flag bit set (used to identity methods that are interpreted because they're unjittable for some reason)
 
  gcMode
  - the variable holding the gcMode, used to inform the cogit of how to scan the machine code zone for oops on GC
 
  heapBase
  - the address in memory of the base of the objectMemory's heap, which is immediately above the machine code zone
 
  lastCoggableInterpretedBlockMethod
  - a variable used to invoke the cogit for a block mehtod being invoked repeatedly in the interpreter
 
  lastUncoggableInterpretedBlockMethod
  - a variable used to avoid invoking the cogit for an unjittable method encountered on block evaluation
 
  maxLiteralCountForCompile
  - the variable controlling which methods to jit.  methods with a literal count above this value will not be jitted (on the grounds that large methods are typically used for initialization, and take up a lot of space in the code zone)
 
  minBackwardJumpCountForCompile
  - the variable controlling when to attempt to jit a method being interpreted.  If as many backward jumps as this occur, the current method will be jitted
 
  primTraceLog
  - a small array implementing a crcular buffer logging the last N primitive invocations, GCs, code compactions, etc used for crash reporting
 
  primTraceLogIndex
  - the index into primTraceLog of the next entry
 
  reenterInterpreter
  - the jmpbuf used to jmp back into the interpreter when transitioning from machine code to the interpreter
 
  statCodeCompactionCount
  - the count of machine code zone compactions
 
  statCodeCompactionUsecs
  - the total microseconds spent in machine code zone compactions
 
  traceLog
  - a log of various events, used in debugging
 
  traceLogIndex
  - the index into traceLog of the next entry
 
  traceSources
+ - the names associated with the codes of events in traceLog
+
+ CFramePointer
+ - if in use, the value of the C frame pointer on most recent entry to the interpreter after start-up or a callback.  Used to establish the C stack when calling the run-time from generated machine code.
+
+ CStackPointer
+ - the value of the C stack pointer on most recent entry to the interpreter after start-up or a callback.  Used to establish the C stack when calling the run-time from generated machine code.!
- - the names associated with the codes of events in traceLog!

Item was changed:
  ----- Method: CoInterpreter class>>declareCVarsIn: (in category 'translation') -----
  declareCVarsIn: aCCodeGenerator
  "Override to avoid repeating StackInterpreter's declarations and add our own extensions"
  self class == thisContext methodClass ifFalse: [^self]. "Don't duplicate decls in subclasses"
  aCCodeGenerator
  addHeaderFile:'"sqCogStackAlignment.h"';
  addHeaderFile:'"cogmethod.h"'.
  NewspeakVM ifTrue:
  [aCCodeGenerator addHeaderFile:'"nssendcache.h"'].
  aCCodeGenerator
  addHeaderFile: (aCCodeGenerator vmClass isThreadedVM
  ifTrue: ['"cointerpmt.h"']
  ifFalse: ['"cointerp.h"']);
  addHeaderFile:'"cogit.h"'.
  aCCodeGenerator vmClass
  declareInterpreterVersionIn: aCCodeGenerator
  defaultName: aCCodeGenerator interpreterVersion.
  aCCodeGenerator
  var: #heapBase type: #usqInt;
  var: #statCodeCompactionUsecs type: #usqLong;
  var: #maxLiteralCountForCompile
  declareC: 'sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* ', MaxLiteralCountForCompile printString, ' */';
  var: #minBackwardJumpCountForCompile
  declareC: 'sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* ', MinBackwardJumpCountForCompile printString, ' */'.
  aCCodeGenerator removeVariable: 'atCache'. "Way too much trouble than it's worth in the Cog VM"
  aCCodeGenerator
  var: #primTraceLogIndex type: #'unsigned char';
  var: #primTraceLog declareC: 'sqInt primTraceLog[256]';
  var: #traceLog
  declareC: 'sqInt traceLog[TraceBufferSize /* ', TraceBufferSize printString, ' */]';
+ var: #traceSources type: #'char *' array: TraceSources.
+ aCCodeGenerator
+ var: #CFramePointer type: #'usqIntptr_t';
+ var: #CStackPointer type: #'usqIntptr_t'!
- var: #traceSources type: #'char *' array: TraceSources!

Item was added:
+ ----- Method: CoInterpreter>>cFramePointerAddress (in category 'cog jit support') -----
+ cFramePointerAddress
+ <api>
+ ^self cCode: [self addressOf: CFramePointer] inSmalltalk: [self error: 'Use Cogit''s version  when simulating']!

Item was added:
+ ----- Method: CoInterpreter>>cStackPointerAddress (in category 'cog jit support') -----
+ cStackPointerAddress
+ <api>
+ ^self cCode: [self addressOf: CStackPointer] inSmalltalk: [self error: 'Use Cogit''s version  when simulating']!

Item was changed:
  ----- Method: CoInterpreter>>callbackEnter: (in category 'callback support') -----
  callbackEnter: callbackID
  "Re-enter the interpreter for executing a callback"
  | currentCStackPointer currentCFramePointer savedReenterInterpreter
   wasInMachineCode calledFromMachineCode |
  <volatile>
  <export: true>
  <var: #currentCStackPointer type: #'void *'>
  <var: #currentCFramePointer type: #'void *'>
  <var: #callbackID type: #'sqInt *'>
  <var: #savedReenterInterpreter type: #'jmp_buf'>
 
  "For now, do not allow a callback unless we're in a primitiveResponse"
  (self asserta: primitiveFunctionPointer ~= 0) ifFalse:
  [^false].
 
  self assert: primFailCode = 0.
 
  "Check if we've exceeded the callback depth"
  (self asserta: jmpDepth < MaxJumpBuf) ifFalse:
  [^false].
  jmpDepth := jmpDepth + 1.
 
  wasInMachineCode := self isMachineCodeFrame: framePointer.
  calledFromMachineCode := instructionPointer <= objectMemory startOfMemory.
 
  "Suspend the currently active process"
  suspendedCallbacks at: jmpDepth put: self activeProcess.
  "We need to preserve newMethod explicitly since it is not activated yet
  and therefore no context has been created for it. If the caller primitive
  for any reason decides to fail we need to make sure we execute the correct
  method and not the one 'last used' in the call back"
  suspendedMethods at: jmpDepth put: newMethod.
  self flag: 'need to debug this properly.  Conceptually it is the right thing to do but it crashes in practice'.
  false
  ifTrue:
  ["Signal external semaphores since a signalSemaphoreWithIndex: request may
   have been issued immediately prior to this callback before the VM has any
   chance to do a signalExternalSemaphores in checkForEventsMayContextSwitch:"
  self signalExternalSemaphores.
  "If no process is awakened by signalExternalSemaphores then transfer
   to the highest priority runnable one."
  (suspendedCallbacks at: jmpDepth) = self activeProcess ifTrue:
  [self transferTo: self wakeHighestPriority from: CSCallbackLeave]]
  ifFalse:
  [self transferTo: self wakeHighestPriority from: CSCallbackLeave].
 
  "Typically, invoking the callback means that some semaphore has been
  signaled to indicate the callback. Force an interrupt check as soon as possible."
  self forceInterruptCheck.
 
  "Save the previous CStackPointers and interpreter entry jmp_buf."
+ currentCStackPointer := CStackPointer.
+ currentCFramePointer := CFramePointer.
- currentCStackPointer := cogit getCStackPointer.
- currentCFramePointer := cogit getCFramePointer.
  self memcpy: savedReenterInterpreter asVoidPointer
  _: reenterInterpreter
  _: (self sizeof: #'jmp_buf').
  cogit assertCStackWellAligned.
  (self setjmp: (jmpBuf at: jmpDepth)) = 0 ifTrue: "Fill in callbackID"
  [callbackID at: 0 put: jmpDepth.
  self enterSmalltalkExecutive.
  self assert: false "NOTREACHED"].
 
  "Restore the previous CStackPointers and interpreter entry jmp_buf."
+ self setCFramePointer: currentCFramePointer setCStackPointer: currentCStackPointer.
- cogit setCStackPointer: currentCStackPointer.
- cogit setCFramePointer: currentCFramePointer.
  self memcpy: reenterInterpreter
  _: (self cCoerceSimple: savedReenterInterpreter to: #'void *')
  _: (self sizeof: #'jmp_buf').
 
  "Transfer back to the previous process so that caller can push result"
  self putToSleep: self activeProcess yieldingIf: preemptionYields.
  self transferTo: (suspendedCallbacks at: jmpDepth) from: CSCallbackLeave.
  newMethod := suspendedMethods at: jmpDepth. "see comment above"
  argumentCount := self argumentCountOf: newMethod.
  self assert: wasInMachineCode = (self isMachineCodeFrame: framePointer).
  calledFromMachineCode
  ifTrue:
  [instructionPointer asUnsignedInteger >= objectMemory startOfMemory ifTrue:
  [self iframeSavedIP: framePointer put: instructionPointer.
  instructionPointer := cogit ceReturnToInterpreterPC]]
  ifFalse:
  ["Even if the context was flushed to the heap and rebuilt in transferTo:from:
   above it will remain an interpreted frame because the context's pc would
   remain a bytecode pc.  So the instructionPointer must also be a bytecode pc."
  self assert: (self isMachineCodeFrame: framePointer) not.
  self assert: instructionPointer > objectMemory startOfMemory].
  self assert: primFailCode = 0.
  jmpDepth := jmpDepth-1.
  ^true!

Item was added:
+ ----- Method: CoInterpreter>>checkIfCFramePointerInUse (in category 'cog jit support') -----
+ checkIfCFramePointerInUse
+ <api>
+ "Necessary because we very much want CStackPointer and CFramePointer to be static/private and grouped
+ with other interpreter variables which will hence be accessed via VarBaseReg on platforms trhat have one."
+ ^self isCFramePointerInUse: (self addressOf: CFramePointer) _: (self addressOf: CStackPointer)!

Item was changed:
  ----- Method: CoInterpreter>>divorceSomeFramesWithMachineCodePrimitiveMethod (in category 'frame access') -----
  divorceSomeFramesWithMachineCodePrimitiveMethod
  "Divorce at most one frame (since the divorce may cause the containing
  page to be split) and answer whether a frame was divorced."
- <var: #cogMethod type: #'CogMethod *'>
  | divorcedSome |
  <var: #aPage type: #'StackPage *'>
  divorcedSome := false.
  0 to: numStackPages - 1 do:
  [:i| | aPage |
  aPage := stackPages stackPageAt: i.
  (stackPages isFree: aPage) ifFalse:
  ["this to avoid assert in externalDivorceFrame:andContext:"
  stackPages markStackPageMostRecentlyUsed: stackPage.
  (self divorceAMachineCodeFrameWithMachineCodePrimitiveMethodIn: aPage) ifTrue:
  [divorcedSome := true]]].
  ^divorcedSome!

Item was added:
+ ----- Method: CoInterpreter>>getCFramePointer (in category 'callback support') -----
+ getCFramePointer
+ <doNotGenerate>
+ ^CFramePointer!

Item was added:
+ ----- Method: CoInterpreter>>getCStackPointer (in category 'callback support') -----
+ getCStackPointer
+ <doNotGenerate>
+ ^CStackPointer!

Item was added:
+ ----- Method: CoInterpreter>>isCFramePointerInUse:_: (in category 'cog jit support') -----
+ isCFramePointerInUse: cFrmPtrPtr _: cStkPtrPtr
+ <var: 'cFrmPtrPtr' type: 'usqIntptr_t *'>
+ <var: 'cStkPtrPtr' type: 'usqIntptr_t *'>
+ <doNotGenerate>
+ "This must be implemented externally, e.g. in sqPlatMain.c."
+ ^self class initializationOptions at: #isCFramePointerInUse ifAbsent: [true]!

Item was changed:
  ----- Method: CoInterpreter>>restoreCStackStateForCallbackContext: (in category 'callback support') -----
  restoreCStackStateForCallbackContext: vmCallbackContext
  <var: #vmCallbackContext type: #'VMCallbackContext *'>
+ self setCFramePointer: vmCallbackContext savedCFramePointer setCStackPointer: vmCallbackContext savedCStackPointer.
- cogit
- setCStackPointer: vmCallbackContext savedCStackPointer;
- setCFramePointer: vmCallbackContext savedCFramePointer.
  self memcpy: reenterInterpreter
  _: vmCallbackContext savedReenterInterpreter asVoidPointer
  _: (self sizeof: #'jmp_buf')!

Item was changed:
  ----- Method: CoInterpreter>>saveCStackStateForCallbackContext: (in category 'callback support') -----
  saveCStackStateForCallbackContext: vmCallbackContext
  <var: #vmCallbackContext type: #'VMCallbackContext *'>
  vmCallbackContext
+ savedCStackPointer: CStackPointer;
+ savedCFramePointer: CFramePointer.
- savedCStackPointer: cogit getCStackPointer;
- savedCFramePointer: cogit getCFramePointer.
  super saveCStackStateForCallbackContext: vmCallbackContext!

Item was added:
+ ----- Method: CoInterpreter>>setCFramePointer:setCStackPointer: (in category 'callback support') -----
+ setCFramePointer: cFramePointer setCStackPointer: cStackPointer
+ <inline: #always>
+ CStackPointer := cStackPointer.
+ CFramePointer := cFramePointer.
+ self cCode: [] inSmalltalk:
+ [objectMemory
+ longAt: self inMemoryCFramePointerAddress
+ put: cFramePointer;
+ longAt: self inMemoryCStackPointerAddress
+ put: cStackPointer]!

Item was changed:
  ----- Method: CoInterpreterMT>>assertCStackPointersBelongToCurrentThread (in category 'simulation') -----
  assertCStackPointersBelongToCurrentThread
  <doNotGenerate>
  | range |
  range := self cStackRangeForCurrentThread.
+ self assert: ((range includes: CStackPointer)
+ and: [range includes: CFramePointer])!
- self assert: ((range includes: cogit getCStackPointer)
- and: [range includes: cogit getCFramePointer])!

Item was changed:
  ----- Method: CoInterpreterMT>>assertCStackPointersBelongToCurrentVMOwner (in category 'simulation') -----
  assertCStackPointersBelongToCurrentVMOwner
  <doNotGenerate>
  | range |
  range := self cStackRangeForCurrentVMOwner.
  self assert: (range notNil "VM is owned"
+ and: [(range includes: CStackPointer)
+ and: [range includes: CFramePointer]])!
- and: [(range includes: cogit getCStackPointer)
- and: [range includes: cogit getCFramePointer]])!

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
  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: CStackPointer).
+ self assert: (range includes: CFramePointer)].
- 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:
  ----- Method: CoInterpreterMT>>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.  This call will block until the VM is owned by the current thread or an error occurs.
  The argument should be the value answered by disownVM, or 0 for callbacks that don't know
  if they have disowned or not.  This is both an optimization to avoid having to query thread-
  local storage for the current thread's index (since it can easily keep it in some local variable),
  and a record of when an unbound process becomes affined to a thread for the dynamic
  extent of some operation.
 
  Answer 0 if the current thread is known to the VM.
  Answer 1 if the current thread is unknown to the VM and takes ownership.
  Answer -1 if the current thread is unknown to the VM and fails to take ownership."
  | threadIndex flags vmThread myProc activeProc sched |
  <var: #vmThread type: #'CogVMThread *'>
  threadIndexAndFlags = 0 ifTrue:
  [^self ownVMFromUnidentifiedThread].
  threadIndex := threadIndexAndFlags bitAnd: ThreadIdMask.
  flags := threadIndexAndFlags >> DisownFlagsShift.
  (flags anyMask: DisownVMForProcessorRelinquish) ifTrue:
  [relinquishing := false.
  self sqLowLevelMFence].
  (threadIndexAndFlags anyMask: LockGUIThreadFlag) ifTrue:
  [self assert: (noThreadingOfGUIThread and: [self inGUIThread]).
  self assert: disowningVMThread = nil.
  (flags anyMask: DisownVMLockOutFullGC) ifTrue:
  [objectMemory decrementFullGCLock].
  cogit recordEventTrace ifTrue:
  [self recordTrace: TraceOwnVM thing: ConstZero source: 0].
  ^0].
 
  vmThread := cogThreadManager acquireVMFor: threadIndex.
  disownCount := disownCount - 1.
 
  (flags anyMask: DisownVMLockOutFullGC) ifTrue:
  [objectMemory decrementFullGCLock].
  disowningVMThread notNil ifTrue:
  [vmThread = disowningVMThread ifTrue:
  [self cCode: ''
  inSmalltalk:
  [| range |
  range := self cStackRangeForThreadIndex: threadIndex.
+ self assert: (range includes: CStackPointer).
+ self assert: (range includes: CFramePointer)].
- self assert: (range includes: cogit getCStackPointer).
- self assert: (range includes: cogit getCFramePointer)].
  self assert: self successful.
  self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject.
  disowningVMThread := nil.
  cogit recordEventTrace ifTrue:
  [self recordTrace: TraceOwnVM thing: ConstOne source: 0].
  ^0].  "if not preempted we're done."
  self preemptDisowningThread].
  "We've been preempted; we must restore state and update the threadId
  in our process, and may have to put the active process to sleep."
  sched := self schedulerPointer.
  activeProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
  (threadIndexAndFlags anyMask: OwnVMForeignThreadFlag)
  ifTrue:
  [self assert: foreignCallbackProcessSlot == ForeignCallbackProcess.
  myProc := objectMemory splObj: foreignCallbackProcessSlot.
  self assert: myProc ~= objectMemory nilObject.
  objectMemory splObj: foreignCallbackProcessSlot put: objectMemory nilObject]
  ifFalse: [myProc := cogThreadManager popAWOLProcess: vmThread].
  self assert: activeProc ~= myProc.
  (activeProc ~= objectMemory nilObject
  and: [(objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject]) ifTrue:
  [self putToSleep: activeProc yieldingIf: preemptionYields].
  self assert: (objectMemory fetchPointer: MyListIndex ofObject: myProc) = (objectMemory splObj: ProcessInExternalCodeTag).
  objectMemory
  storePointer: ActiveProcessIndex ofObject: sched withValue: myProc;
  storePointerUnchecked: MyListIndex ofObject: myProc withValue: objectMemory nilObject.
  "Only unaffine if the process was affined at this level and did not become bound in the interim."
  ((threadIndexAndFlags anyMask: ProcessUnaffinedOnDisown)
  and: [(self isBoundProcess: myProc) not]) ifTrue:
  [self setOwnerIndexOfProcess: myProc to: 0 bind: false].
  self initPrimCall.
  self externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc.
  "If this primitive is called from machine code maintain the invariant that the return pc
  of an interpreter callee calling a machine code caller is ceReturnToInterpreterPC."
  (vmThread inMachineCode
  and: [instructionPointer >= objectMemory startOfMemory]) ifTrue:
  [self iframeSavedIP: framePointer put: instructionPointer.
  instructionPointer := cogit ceReturnToInterpreterPC].
  newMethod := vmThread newMethodOrNull.
  argumentCount := vmThread argumentCount.
  self cCode:
  [self memcpy: reenterInterpreter
  _: vmThread reenterInterpreter
  _: (self sizeof: #'jmp_buf')]
  inSmalltalk:
  [reenterInterpreter := vmThread reenterInterpreter].
  vmThread newMethodOrNull: nil.
  self cCode: ''
  inSmalltalk:
  [| range |
  range := self cStackRangeForThreadIndex: threadIndex.
  self assert: (range includes: vmThread cStackPointer).
  self assert: (range includes: vmThread cFramePointer)].
+ self setCFramePointer: vmThread cFramePointer setCStackPointer: vmThread cStackPointer.
- cogit setCStackPointer: vmThread cStackPointer.
- cogit setCFramePointer: vmThread cFramePointer.
  self assert: newMethod ~~ nil.
  cogit recordEventTrace ifTrue:
  [self recordTrace: TraceOwnVM thing: ConstTwo source: 0].
  ^threadIndexAndFlags bitAnd: OwnVMForeignThreadFlag!

Item was changed:
  ----- Method: CoInterpreterMT>>preemptDisowningThread (in category 'vm scheduling') -----
  preemptDisowningThread
  "Set the relevant state for disowningVMThread so that it can resume after
  being preempted and set disowningVMThread to nil to indicate preemption.
 
  N.B.  This should only be sent from checkPreemptionOfDisowningThread.
 
  There are essentially four things to do.
  a) save the VM's notion of the current C stack pointers; these are pointers
  into a thread's stack and must be saved and restored in thread switch.
  b) save the VM's notion of the current Smalltalk execution point.  This is
  simply the suspend half of a process switch that saves the current context
  in the current process.
  c) add the process to the thread's set of AWOL processes so that the scheduler
  won't try to run the process while the thread has disowned the VM.
  d) save the in-primitive VM state, newMethod and argumentCount
 
  ownVM: will restore the VM context as of disownVM: from the above when it
  finds it has been preempted."
 
  | activeProc activeContext preemptedThread |
  <var: #preemptedThread type: #'CogVMThread *'>
  <inline: false>
  self assert: disowningVMThread notNil.
  self assert: (disowningVMThread state = CTMUnavailable
  or: [disowningVMThread state = CTMWantingOwnership]).
  self cCode: ''
  inSmalltalk:
  [| range |
  range := self cStackRangeForThreadIndex: disowningVMThread index.
+ self assert: (range includes: CStackPointer).
+ self assert: (range includes: CFramePointer)].
- self assert: (range includes: cogit getCStackPointer).
- self assert: (range includes: cogit getCFramePointer)].
  cogit recordEventTrace ifTrue:
  [self recordTrace: TracePreemptDisowningThread
  thing: (objectMemory integerObjectOf: disowningVMThread index)
  source: 0].
+ disowningVMThread cStackPointer: CStackPointer.
+ disowningVMThread cFramePointer: CFramePointer.
- disowningVMThread cStackPointer: cogit getCStackPointer.
- disowningVMThread cFramePointer: cogit getCFramePointer.
  activeProc := self activeProcess.
  self assert: (objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject.
  objectMemory
  storePointer: MyListIndex
  ofObject: activeProc
  withValue: (objectMemory splObj: ProcessInExternalCodeTag).
  "The instructionPointer must be pushed because the convention for inactive stack pages is that the
  instructionPointer is top of stack.  We need to know if this primitive is called from machine code
  because the invariant that the return pc of an interpreter callee calling a machine code caller is
  ceReturnToInterpreterPC must be maintained."
  self push: instructionPointer.
  self externalWriteBackHeadFramePointers.
  activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
  objectMemory
  storePointer: SuspendedContextIndex
  ofObject: activeProc
  withValue: activeContext.
  "Since pushing the awol process may realloc disowningVMThread we need to reassign.
  But since we're going to nil disowningVMThread anyway we can assign to a local."
  preemptedThread := cogThreadManager pushAWOLProcess: activeProc on: disowningVMThread.
  disowningVMThread := nil.
  preemptedThread priority: (self quickFetchInteger: PriorityIndex ofObject: activeProc).
  (self ownerIndexOfProcess: activeProc) = 0
  ifTrue: [self setOwnerIndexOfProcess: activeProc to: preemptedThread index bind: false]
  ifFalse: [self assert: (self ownerIndexOfProcess: activeProc) = preemptedThread index].
  preemptedThread
  newMethodOrNull: newMethod;
  argumentCount: argumentCount;
  inMachineCode: instructionPointer asUnsignedInteger <= objectMemory startOfMemory.
  self cCode:
  [self memcpy: preemptedThread reenterInterpreter
  _: reenterInterpreter
  _: (self sizeof: #'jmp_buf')]
  inSmalltalk:
  [preemptedThread reenterInterpreter: reenterInterpreter]!

Item was changed:
  ----- Method: CoInterpreterMT>>primitiveRelinquishProcessor (in category 'I/O primitives') -----
  primitiveRelinquishProcessor
  "Relinquish the processor for up to the given number of microseconds.
  The exact behavior of this primitive is platform dependent.
  Override to check for waiting threads."
 
  | microSecs threadIndexAndFlags currentCStackPointer currentCFramePointer savedReenterInterpreter |
  <var: #currentCStackPointer type: #'void *'>
  <var: #currentCFramePointer type: #'void *'>
  <var: #savedReenterInterpreter type: #'jmp_buf'>
  microSecs := self stackTop.
  (objectMemory isIntegerObject: microSecs) ifFalse:
  [^self primitiveFail].
  self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject.
  self assert: relinquishing not.
  "DO NOT allow relinquishing the processor while we are profiling since this
  may skew the time base for our measures (it may reduce processor speed etc).
  Instead we go full speed, therefore measuring the precise time we spend in the
  inner idle loop as a busy loop."
  nextProfileTick = 0 ifTrue:
  "Presumably we have nothing to do; this primitive is typically called from the
  background process. So we should /not/ try and activate any threads in the
  pool; they will waste cycles finding there is no runnable process, and will
  cause a VM abort if no runnable process is found.  But we /do/ want to allow
  FFI calls that have completed, or callbacks a chance to get into the VM; they
  do have something to do.  DisownVMForProcessorRelinquish indicates this."
+ [currentCStackPointer := CStackPointer.
+ currentCFramePointer := CFramePointer.
- [currentCStackPointer := cogit getCStackPointer.
- currentCFramePointer := cogit getCFramePointer.
  self cCode:
  [self memcpy: savedReenterInterpreter asVoidPointer
  _: reenterInterpreter
  _: (self sizeof: #'jmp_buf')].
  threadIndexAndFlags := self disownVM: DisownVMForProcessorRelinquish.
  self assert: relinquishing.
  self ioRelinquishProcessorForMicroseconds: (objectMemory integerValueOf: microSecs).
  self assert: relinquishing.
  self ownVM: threadIndexAndFlags.
  self assert: relinquishing not.
  self assert: cogThreadManager currentVMThread state = CTMAssignableOrInVM.
+ self assert: currentCStackPointer = CStackPointer.
+ self assert: currentCFramePointer = CFramePointer.
- self assert: currentCStackPointer = cogit getCStackPointer.
- self assert: currentCFramePointer = cogit getCFramePointer.
  self cCode:
  [self assert: (self mem: (self cCoerceSimple: savedReenterInterpreter to: #'void *')
  cm: reenterInterpreter
  p: (self sizeof: #'jmp_buf')) = 0]].
  self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject.
  self pop: 1  "microSecs; leave rcvr on stack"!

Item was changed:
  ----- Method: CoInterpreterMT>>returnToSchedulingLoopAndReleaseVMOrWakeThread:source: (in category 'process primitive support') -----
  returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: source
  | savedReenterInterpreter |
  <var: #savedReenterInterpreter type: #'jmp_buf'>
  <var: #vmThread type: #'CogVMThread *'>
  <inline: false>
  self cCode:
  [self flag: 'this is just for debugging.  Note the current C stack pointers'.
  cogThreadManager currentVMThread
+ cStackPointer: CStackPointer;
+ cFramePointer: CFramePointer]
- cStackPointer: cogit getCStackPointer;
- cFramePointer: cogit getCFramePointer]
  inSmalltalk:
  [| range |
  range := self cStackRangeForThreadIndex: cogThreadManager getVMOwner.
+ self assert: (range includes: CStackPointer).
+ self assert: (range includes: CFramePointer)].
- self assert: (range includes: cogit getCStackPointer).
- self assert: (range includes: cogit getCFramePointer)].
  "We must use a copy of reenterInterpreter since we're giving up the VM to another vmThread."
  self cCode:
  [self memcpy: savedReenterInterpreter asVoidPointer
  _: reenterInterpreter
  _: (self sizeof: #'jmp_buf')]
  inSmalltalk:
  [savedReenterInterpreter := reenterInterpreter].
  self recordThreadSwitchTo: (vmThread ifNotNil: [vmThread index] ifNil: [0]) source: source.
  vmThread
  ifNotNil: [cogThreadManager wakeVMThreadFor: vmThread index]
  ifNil: [cogThreadManager releaseVM].
  "2 implies returning to the threadSchedulingLoop."
  self siglong: savedReenterInterpreter jmp: ReturnToThreadSchedulingLoop!

Item was changed:
  ----- Method: CogARMCompiler>>genMarshallNArgs:arg:arg:arg:arg: (in category 'abi') -----
  genMarshallNArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3
  "Generate the code to pass up to four arguments in a C run-time call.  Hack: each argument is
  either a negative number, which encodes a constant, or a non-negative number, that of a register.
 
  Run-time calls have no more than four arguments, so chosen so that on ARM, where in its C ABI the
  first four integer arguments are passed in registers, all arguments can be passed in registers.  We
  defer to the back end to generate this code not so much that the back end knows whether it uses
  the stack or registers to pass arguments (it does, but...). In fact we defer for an extremely evil reason.
  Doing so allows the x64 (where up to 6 args are passed) to assign the register arguments in an order
+ that allows some of the argument registers to be used for specific abstract registers, specifically
- that allows some of the argument registers to be used for specific abstract  registers, specifically
  ReceiverResultReg and ClassReg.  This is evil, evil, evil, but also it's really nice to keep using the old
  register assignments the original author has grown accustomed to."
  <inline: true>
  numArgs = 0 ifTrue: [^self].
+ "Avoid arg regs being overwritten before they are read."
+ numArgs > 1 ifTrue:
+ [((cogit isTrampolineArgConstant: regOrConst1) not
+   and: [regOrConst1 = CArg0Reg]) ifTrue:
+ [cogit MoveR: regOrConst1 R: Extra0Reg.
+ ^self genMarshallNArgs: numArgs arg: regOrConst0 arg: Extra0Reg arg: regOrConst2 arg: regOrConst3].
+ numArgs > 2 ifTrue:
+ [((cogit isTrampolineArgConstant: regOrConst2) not
+   and: [regOrConst2 = CArg0Reg or: [regOrConst2 = CArg1Reg]]) ifTrue:
+ [cogit MoveR: regOrConst2 R: Extra1Reg.
+ ^self genMarshallNArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: Extra1Reg arg: regOrConst3].
+ numArgs > 3 ifTrue:
+ [((cogit isTrampolineArgConstant: regOrConst3) not
+   and: [regOrConst3 = CArg0Reg or: [regOrConst3 = CArg1Reg or: [regOrConst3 = CArg2Reg]]]) ifTrue:
+ [cogit MoveR: regOrConst3 R: Extra2Reg.
+ ^self genMarshallNArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: Extra2Reg]]]].
  (cogit isTrampolineArgConstant: regOrConst0)
  ifTrue: [cogit MoveCq: (cogit trampolineArgValue: regOrConst0) R: CArg0Reg]
  ifFalse: [cogit MoveR: regOrConst0 R: CArg0Reg].
  numArgs = 1 ifTrue: [^self].
  (cogit isTrampolineArgConstant: regOrConst1)
  ifTrue: [cogit MoveCq: (cogit trampolineArgValue: regOrConst1) R: CArg1Reg]
  ifFalse: [cogit MoveR: regOrConst1 R: CArg1Reg].
  numArgs = 2 ifTrue: [^self].
  (cogit isTrampolineArgConstant: regOrConst2)
  ifTrue: [cogit MoveCq: (cogit trampolineArgValue: regOrConst2) R: CArg2Reg]
  ifFalse: [cogit MoveR: regOrConst2 R: CArg2Reg].
  numArgs = 3 ifTrue: [^self].
  (cogit isTrampolineArgConstant: regOrConst3)
  ifTrue: [cogit MoveCq: (cogit trampolineArgValue: regOrConst3) R: CArg3Reg]
  ifFalse: [cogit MoveR: regOrConst3 R: CArg3Reg]!

Item was removed:
- ----- Method: CogInLineLiteralsX64Compiler>>wantsNearAddressFor: (in category 'simulation') -----
- wantsNearAddressFor: anObject
- "A hack hook to allow x64 to address CStackPointer and CFramePointer relative to VarBaseReg.
- With this regime we do _not_ want to access via VarBasereg but instead test the
- MoveAwR & MoveRAw hack fetch/storing through RAX."
- <doNotGenerate>
- ^false!

Item was changed:
  ----- Method: CogObjectRepresentationForSpur>>genGetClassObjectOf:into:scratchReg:mayBeAForwarder: (in category 'compile abstract instructions') -----
+ genGetClassObjectOf: instReg into: destReg scratchReg: scratchReg mayBeAForwarder: mayBeForwarder
- genGetClassObjectOf: instReg into: destReg scratchReg: scratchReg mayBeAForwarder: mayBeAForwarder
  "Fetch the instance's class into destReg.  If the instance is not the receiver and is forwarded, follow forwarding."
  | jumpIsImm jumpNotForwarded loop |
  <var: #jumpIsImm type: #'AbstractInstruction *'>
  <var: #jumpNotForwarded type: #'AbstractInstruction *'>
  <var: #loop type: #'AbstractInstruction *'>
  (instReg = destReg or: [instReg = scratchReg or: [destReg = scratchReg]]) ifTrue:
  [^BadRegisterSet].
  loop := cogit MoveR: instReg R: scratchReg.
  cogit AndCq: objectMemory tagMask R: scratchReg.
  jumpIsImm := cogit JumpNonZero: 0.
  self flag: #endianness.
  "Get least significant half of header word in destReg"
  cogit MoveMw: 0 r: instReg R: scratchReg.
  "mask off class index"
  cogit AndCq: objectMemory classIndexMask R: scratchReg.
+ mayBeForwarder ifTrue:
- mayBeAForwarder ifTrue:
  ["if it is forwarded..."
  cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: scratchReg.
  jumpNotForwarded := cogit JumpNonZero: 0.
  "...follow the forwarding pointer and loop to fetch its classIndex"
  cogit MoveMw: objectMemory baseHeaderSize r: instReg R: instReg.
  cogit Jump: loop.
  jumpNotForwarded jmpTarget: cogit Label].
  jumpIsImm jmpTarget:
  (cogit MoveR: scratchReg R: destReg).
  scratchReg = TempReg
  ifTrue:
  [cogit PushR: instReg.
  self genGetClassObjectOfClassIndex: destReg into: instReg scratchReg: TempReg.
  cogit MoveR: instReg R: destReg.
  cogit PopR: instReg]
  ifFalse:
  [self genGetClassObjectOfClassIndex: destReg into: scratchReg scratchReg: TempReg.
  cogit MoveR: scratchReg R: destReg].
  ^0!

Item was changed:
  ----- Method: CogVMSimulator>>utilitiesMenu: (in category 'UI') -----
  utilitiesMenu: aMenuMorph
  aMenuMorph
  add: 'toggle transcript' action: #toggleTranscript;
  add: 'clone VM' action: #cloneSimulationWindow;
  addLine;
  add: 'print ext head frame' action: #printExternalHeadFrame;
  add: 'print int head frame' action: #printHeadFrame;
  add: 'print mc/cog head frame' action: [self printFrame: cogit processor fp WithSP: cogit processor sp];
  add: 'short print ext head frame & callers' action: [self shortPrintFrameAndCallers: framePointer];
  add: 'short print int head frame & callers' action: [self shortPrintFrameAndCallers: localFP];
  add: 'short print mc/cog head frame & callers' action: [self shortPrintFrameAndCallers: cogit processor fp];
  add: 'long print ext head frame & callers' action: [self printFrameAndCallers: framePointer SP: stackPointer];
  add: 'long print int head frame & callers' action: [self printFrameAndCallers: localFP SP: localSP];
  add: 'long print mc/cog head frame & callers' action: [self printFrameAndCallers: cogit processor fp SP: cogit processor sp];
  add: 'print frame...' action: [(self promptHex: 'print frame') ifNotNil: [:fp| self printFrame: fp]];
  add: 'print call stack' action: #printCallStack;
  add: 'print stack call stack' action: #printStackCallStack;
  add: 'print stack call stack of...' action: [(self promptHex: 'frame') ifNotNil: [:fp| self printStackCallStackOf: fp]];
  add: 'print call stack of...' action: [(self promptHex: 'context or process oop') ifNotNil: [:obj| self printCallStackOf: obj]];
  add: 'print call stack of frame...' action: [(self promptHex: 'frame') ifNotNil: [:fp| self printCallStackFP: fp]];
  add: 'print all stacks' action: #printAllStacks;
  add: 'write back local ptrs' action: [stackPointer := localSP. framePointer := localFP. instructionPointer := localIP.
  self writeBackHeadFramePointers];
  add: 'write back mc ptrs' action: [stackPointer := cogit processor sp. framePointer := cogit processor fp. instructionPointer := cogit processor pc.
  self externalWriteBackHeadFramePointers];
  addLine;
+ add: 'print rump C stack' action: [objectMemory printMemoryFrom: cogit processor sp to: CFramePointer];
- add: 'print rump C stack' action: [objectMemory printMemoryFrom: cogit processor sp to: cogit getCStackPointer];
  add: 'print registers' action: [cogit processor printRegistersOn: transcript];
  add: 'print register map' action: [cogit printRegisterMapOn: transcript];
  add: 'disassemble method/trampoline...' action: [(self promptHex: 'pc') ifNotNil: [:pc| cogit disassembleCodeAt: pc]];
  add: 'disassemble method/trampoline at pc' action:
  [cogit disassembleCodeAt: (((cogit codeEntryFor: cogit processor pc) isNil
   and: [(cogit methodZone methodFor: cogit processor pc) = 0])
  ifTrue: [instructionPointer]
  ifFalse: [cogit processor pc])];
  add: 'disassemble ext head frame method' action: [cogit disassembleMethod: (self frameMethod: framePointer)];
  add: 'print oop...' action: [(self promptHex: 'print oop') ifNotNil: [:oop| self printOop: oop]];
  add: 'long print oop...' action: [(self promptHex: 'print oop') ifNotNil: [:oop| self longPrintOop: oop]];
  add: 'print context...' action: [(self promptHex: 'print context') ifNotNil: [:oop| self printContext: oop]];
  add: 'symbolic method...' action: [(self promptHex: 'method bytecodes') ifNotNil: [:oop| self symbolicMethod: oop]];
  addLine;
  add: 'inspect object memory' target: objectMemory action: #inspect;
  add: 'run leak checker' action: [Cursor execute showWhile: [self runLeakChecker]];
  add: 'inspect cointerpreter' action: #inspect;
  add: 'inspect cogit' target: cogit action: #inspect;
  add: 'inspect method zone' target: cogit methodZone action: #inspect.
  self isThreadedVM ifTrue:
  [aMenuMorph add: 'inspect thread manager' target: self threadManager action: #inspect].
  aMenuMorph
  addLine;
  add: 'print cog methods' target: cogMethodZone action: #printCogMethods;
  add: 'print cog methods with prim...' action:
  [(self promptNum: 'prim index') ifNotNil: [:pix| cogMethodZone printCogMethodsWithPrimitive: pix]];
  add: 'print cog methods with selector...' action:
  [|s| s := UIManager default request: 'selector'.
  s notEmpty ifTrue:
  [s = 'nil' ifTrue: [s := nil].
  cogMethodZone methodsDo:
  [:m|
  (s ifNil: [m selector = objectMemory nilObject]
  ifNotNil: [(objectMemory numBytesOf: m selector) = s size
  and: [(self strncmp: s
  _: (m selector + objectMemory baseHeaderSize)
  _: (objectMemory numBytesOf: m selector)) = 0]]) ifTrue:
  [cogit printCogMethod: m]]]];
  add: 'print cog methods with method...' action:
  [(self promptHex: 'method') ifNotNil: [:methodOop| cogMethodZone printCogMethodsWithMethod: methodOop]];
  add: 'print cog method for...' action: [(self promptHex: 'pc') ifNotNil: [:pc| cogit printCogMethodFor: pc]];
  add: 'print cog method header for...' action: [(self promptHex: 'pc') ifNotNil: [:pc| cogit printCogMethodHeaderFor: pc]];
  add: 'print trampoline table' target: cogit action: #printTrampolineTable;
  add: 'print prim trace log' action: #dumpPrimTraceLog;
  add: 'report recent instructions' target: cogit action: #reportLastNInstructions;
  add: (cogit printRegisters
  ifTrue: ['no print registers each instruction']
  ifFalse: ['print registers each instruction'])
  action: [cogit printRegisters: cogit printRegisters not];
  add: (cogit printInstructions
  ifTrue: ['no print instructions each instruction']
  ifFalse: ['print instructions each instruction'])
  action: [cogit printInstructions: cogit printInstructions not];
  addLine;
  add: (cogit singleStep
  ifTrue: ['no single step']
  ifFalse: ['single step'])
  action: [cogit singleStep: cogit singleStep not];
  add: 'click step' action: [cogit setClickStepBreakBlock];
  add: 'set break pc', cogit breakPC menuPrompt, '...-ve to disable or remove' action: [cogit promptForBreakPC];
  add: 'set break count...' action: [|s| s := UIManager default request: 'break count (dec)'.
  s notEmpty ifTrue: [breakCount := Integer readFrom: s readStream]];
  add: 'set break selector...' action: [|s| s := UIManager default request: 'break selector (MNU:foo for MNU)'.
  s notEmpty ifTrue:
  [(s size > 4 and: [s beginsWith: 'MNU:'])
  ifTrue: [self setBreakMNUSelector: (s allButFirst: 4)]
  ifFalse: [self setBreakSelector: s]]];
  add: 'set break block...' action: [|s| s := UIManager default request: 'break block' initialAnswer: '[:theCogit| false]'.
  s notEmpty ifTrue: [self setBreakBlockFromString: s]];
  add: 'set cogit break method...' action: [(self promptHex: 'cogit breakMethod') ifNotNil: [:bm| cogit setBreakMethod: bm]];
  add: (printBytecodeAtEachStep
  ifTrue: ['no print bytecode each bytecode']
  ifFalse: ['print bytecode each bytecode'])
  action: [self ensureDebugAtEachStepBlock.
  printBytecodeAtEachStep := printBytecodeAtEachStep not];
  add: (printFrameAtEachStep
  ifTrue: ['no print frame each bytecode']
  ifFalse: ['print frame each bytecode'])
  action: [self ensureDebugAtEachStepBlock.
  printFrameAtEachStep := printFrameAtEachStep not].
  ^aMenuMorph!

Item was changed:
  CogClass subclass: #Cogit
+ instanceVariableNames: 'coInterpreter objectMemory objectRepresentation processor threadManager methodZone methodZoneBase codeBase minValidCallAddress lastNInstructions simulatedAddresses simulatedTrampolines simulatedVariableGetters simulatedVariableSetters printRegisters printInstructions compilationTrace clickConfirm breakPC breakBlock singleStep guardPageSize traceFlags traceStores breakMethod methodObj enumeratingCogMethod methodHeader initialPC endPC methodOrBlockNumArgs inBlock needsFrame hasYoungReferent hasMovableLiteral primitiveIndex backEnd literalsManager postCompileHook methodLabel stackCheckLabel blockEntryLabel blockEntryNoContextSwitch blockNoContextSwitchOffset stackOverflowCall sendMiss missOffset entryPointMask checkedEntryAlignment uncheckedEntryAlignment cmEntryOffset entry cmNoCheckEntryOffset noCheckEntry fullBlockEntry cbEntryOffset fullBlockNoContextSwitchEntry cbNoSwitchEntryOffset picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 firstCPICCaseOffs
 et cPICCaseSize cPICEndSize closedPICSize openPICSize fixups abstractOpcodes generatorTable byte0 byte1 byte2 byte3 bytecodePC bytecodeSetOffset opcodeIndex numAbstractOpcodes blockStarts blockCount labelCounter cStackAlignment expectedSPAlignment expectedFPAlignment codeModified maxLitIndex ceMethodAbortTrampoline cePICAbortTrampoline ceCheckForInterruptTrampoline ceCPICMissTrampoline ceReturnToInterpreterTrampoline ceBaseFrameReturnTrampoline ceReapAndResetErrorCodeTrampoline ceSendMustBeBooleanAddTrueTrampoline ceSendMustBeBooleanAddFalseTrampoline ceCannotResumeTrampoline ceEnterCogCodePopReceiverReg ceCallCogCodePopReceiverReg ceCallCogCodePopReceiverAndClassRegs cePrimReturnEnterCogCode cePrimReturnEnterCogCodeProfiling ceNonLocalReturnTrampoline ceFetchContextInstVarTrampoline ceStoreContextInstVarTrampoline ceEnclosingObjectTrampoline ceFlushICache ceCheckFeaturesFunction ceTraceLinkedSendTrampoline ceTraceBlockActivationTrampoline ceTraceStoreTrampoline ceGetFP ceGetSP ceCa
 ptureCStackPointers ordinarySendTrampolines superSendTrampolines directedSuperSendTrampolines directedSuperBindingSendTrampolines dynamicSuperSendTrampolines outerSendTrampolines selfSendTrampolines firstSend lastSend realCEEnterCogCodePopReceiverReg realCECallCogCodePopReceiverReg realCECallCogCodePopReceiverAndClassRegs trampolineTableIndex trampolineAddresses objectReferencesInRuntime runtimeObjectRefIndex cFramePointerInUse debugPrimCallStackOffset ceTryLockVMOwner ceUnlockVMOwner extA extB numExtB tempOop numIRCs indexOfIRC theIRCs receiverTags implicitReceiverSendTrampolines cogMethodSurrogateClass cogBlockMethodSurrogateClass nsSendCacheSurrogateClass cPICPrototype cPICEndOfCodeOffset cPICEndOfCodeLabel ceMallocTrampoline ceFreeTrampoline ceFFICalloutTrampoline debugBytecodePointers debugOpcodeIndices disassemblingMethod cogConstituentIndex directedSendUsesBinding ceCheckLZCNTFunction'
- instanceVariableNames: 'coInterpreter objectMemory objectRepresentation processor threadManager methodZone methodZoneBase codeBase minValidCallAddress lastNInstructions simulatedAddresses simulatedTrampolines simulatedVariableGetters simulatedVariableSetters printRegisters printInstructions compilationTrace clickConfirm breakPC breakBlock singleStep guardPageSize traceFlags traceStores breakMethod methodObj enumeratingCogMethod methodHeader initialPC endPC methodOrBlockNumArgs inBlock needsFrame hasYoungReferent hasMovableLiteral primitiveIndex backEnd literalsManager postCompileHook methodLabel stackCheckLabel blockEntryLabel blockEntryNoContextSwitch blockNoContextSwitchOffset stackOverflowCall sendMiss missOffset entryPointMask checkedEntryAlignment uncheckedEntryAlignment cmEntryOffset entry cmNoCheckEntryOffset noCheckEntry fullBlockEntry cbEntryOffset fullBlockNoContextSwitchEntry cbNoSwitchEntryOffset picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 firstCPICCaseOffs
 et cPICCaseSize cPICEndSize closedPICSize openPICSize fixups abstractOpcodes generatorTable byte0 byte1 byte2 byte3 bytecodePC bytecodeSetOffset opcodeIndex numAbstractOpcodes blockStarts blockCount labelCounter cStackAlignment expectedSPAlignment expectedFPAlignment codeModified maxLitIndex ceMethodAbortTrampoline cePICAbortTrampoline ceCheckForInterruptTrampoline ceCPICMissTrampoline ceReturnToInterpreterTrampoline ceBaseFrameReturnTrampoline ceReapAndResetErrorCodeTrampoline ceSendMustBeBooleanAddTrueTrampoline ceSendMustBeBooleanAddFalseTrampoline ceCannotResumeTrampoline ceEnterCogCodePopReceiverReg ceCallCogCodePopReceiverReg ceCallCogCodePopReceiverAndClassRegs cePrimReturnEnterCogCode cePrimReturnEnterCogCodeProfiling ceNonLocalReturnTrampoline ceFetchContextInstVarTrampoline ceStoreContextInstVarTrampoline ceEnclosingObjectTrampoline ceFlushICache ceCheckFeaturesFunction ceTraceLinkedSendTrampoline ceTraceBlockActivationTrampoline ceTraceStoreTrampoline ceGetFP ceGetSP ceCa
 ptureCStackPointers ordinarySendTrampolines superSendTrampolines directedSuperSendTrampolines directedSuperBindingSendTrampolines dynamicSuperSendTrampolines outerSendTrampolines selfSendTrampolines firstSend lastSend realCEEnterCogCodePopReceiverReg realCECallCogCodePopReceiverReg realCECallCogCodePopReceiverAndClassRegs trampolineTableIndex trampolineAddresses objectReferencesInRuntime runtimeObjectRefIndex cFramePointerInUse debugPrimCallStackOffset ceTryLockVMOwner ceUnlockVMOwner extA extB numExtB tempOop numIRCs indexOfIRC theIRCs receiverTags implicitReceiverSendTrampolines cogMethodSurrogateClass cogBlockMethodSurrogateClass nsSendCacheSurrogateClass CStackPointer CFramePointer cPICPrototype cPICEndOfCodeOffset cPICEndOfCodeLabel ceMallocTrampoline ceFreeTrampoline ceFFICalloutTrampoline debugBytecodePointers debugOpcodeIndices disassemblingMethod cogConstituentIndex directedSendUsesBinding ceCheckLZCNTFunction'
  classVariableNames: 'AltBlockCreationBytecodeSize AltFirstSpecialSelector AltNSSendIsPCAnnotated AltNumSpecialSelectors AnnotationConstantNames AnnotationShift AnnotationsWithBytecodePCs BlockCreationBytecodeSize Debug DisplacementMask DisplacementX2N EagerInstructionDecoration FirstAnnotation FirstSpecialSelector HasBytecodePC IsAbsPCReference IsAnnotationExtension IsDirectedSuperBindingSend IsDirectedSuperSend IsDisplacementX2N IsNSDynamicSuperSend IsNSImplicitReceiverSend IsNSSelfSend IsNSSendCall IsObjectReference IsRelativeCall IsSendCall IsSuperSend MapEnd MaxCPICCases MaxCompiledPrimitiveIndex MaxStackAllocSize MaxX2NDisplacement NSCClassTagIndex NSCEnclosingObjectIndex NSCNumArgsIndex NSCSelectorIndex NSCTargetIndex NSSendIsPCAnnotated NumObjRefsInRuntime NumOopsPerNSC NumSpecialSelectors NumTrampolines ProcessorClass RRRName'
  poolDictionaries: 'CogAbstractRegisters CogCompilationConstants CogMethodConstants CogRTLOpcodes VMBasicConstants VMBytecodeConstants VMObjectIndices VMStackFrameOffsets'
  category: 'VMMaker-JIT'!
  Cogit class
  instanceVariableNames: 'generatorTable primitiveTable'!
 
+ !Cogit commentStamp: 'eem 10/10/2019 09:40' prior: 0!
- !Cogit commentStamp: 'eem 2/25/2017 17:53' prior: 0!
  I am the code generator for the Cog VM.  My job is to produce machine code versions of methods for faster execution and to manage inline caches for faster send performance.  I can be tested in the current image using my class-side in-image compilation facilities.  e.g. try
 
  StackToRegisterMappingCogit genAndDis: (Integer >> #benchFib)
 
  I have concrete subclasses that implement different levels of optimization:
  SimpleStackBasedCogit is the simplest code generator.
 
  StackToRegisterMappingCogit is the current production code generator  It defers pushing operands
  to the stack until necessary and implements a register-based calling convention for low-arity sends.
 
  SistaCogit is an experimental code generator with support for counting
  conditional branches, intended to support adaptive optimization.
 
  RegisterAllocatingCogit is an experimental code generator with support for allocating temporary variables
  to registers. It is inended to serve as the superclass to SistaCogit once it is working.
 
  SistaRegisterAllocatingCogit and SistaCogitClone are temporary classes that allow testing a clone of
  SistaCogit that inherits from RegisterAllocatingCogit.  Once things work these will be merged and
  will replace SistaCogit.
 
  coInterpreter <CoInterpreterSimulator>
  the VM's interpreter with which I cooperate
  methodZoneManager <CogMethodZoneManager>
  the manager of the machine code zone
  objectRepresentation <CogObjectRepresentation>
  the object used to generate object accesses
  processor <BochsIA32Alien|?>
  the simulator that executes the IA32/x86 machine code I generate when simulating execution in Smalltalk
  simulatedTrampolines <Dictionary of Integer -> MessageSend>
  the dictionary mapping trap jump addresses to run-time routines used to warp from simulated machine code in to the Smalltalk run-time.
  simulatedVariableGetters <Dictionary of Integer -> MessageSend>
  the dictionary mapping trap read addresses to variables in run-time objects used to allow simulated machine code to read variables in the Smalltalk run-time.
  simulatedVariableSetters <Dictionary of Integer -> MessageSend>
  the dictionary mapping trap write addresses to variables in run-time objects used to allow simulated machine code to write variables in the Smalltalk run-time.
  printRegisters printInstructions clickConfirm <Boolean>
  flags controlling debug printing and code simulation
  breakPC <Integer>
  machine code pc breakpoint
- cFramePointer cStackPointer <Integer>
- the variables representing the C stack & frame pointers, which must change on FFI callback and return
  selectorOop <sqInt>
  the oop of the methodObj being compiled
  methodObj <sqInt>
  the bytecode method being compiled
  initialPC endPC <Integer>
  the start and end pcs of the methodObj being compiled
  methodOrBlockNumArgs <Integer>
  argument count of current method or block being compiled
  needsFrame <Boolean>
  whether methodObj or block needs a frame to execute
  primitiveIndex <Integer>
  primitive index of current method being compiled
  methodLabel <CogAbstractOpcode>
  label for the method header
  blockEntryLabel <CogAbstractOpcode>
  label for the start of the block dispatch code
  stackOverflowCall <CogAbstractOpcode>
  label for the call of ceStackOverflow in the method prolog
  sendMissCall <CogAbstractOpcode>
  label for the call of ceSICMiss in the method prolog
  entryOffset <Integer>
  offset of method entry code from start (header) of method
  entry <CogAbstractOpcode>
  label for the first instruction of the method entry code
  noCheckEntryOffset <Integer>
  offset of the start of a method proper (after the method entry code) from start (header) of method
  noCheckEntry <CogAbstractOpcode>
  label for the first instruction of start of a method proper
  fixups <Array of <AbstractOpcode Label | nil>>
  the labels for forward jumps that will be fixed up when reaching the relevant bytecode.  fixups has one element per byte in methodObj's bytecode; initialPC maps to fixups[0].
  abstractOpcodes <Array of <AbstractOpcode>>
  the code generated when compiling methodObj
  byte0 byte1 byte2 byte3 <Integer>
  individual bytes of current bytecode being compiled in methodObj
  bytecodePointer <Integer>
  bytecode pc (same as Smalltalk) of the current bytecode being compiled
  opcodeIndex <Integer>
  the index of the next free entry in abstractOpcodes (this code is translated into C where OrderedCollection et al do not exist)
  numAbstractOpcodes <Integer>
  the number of elements in abstractOpcocdes
  blockStarts <Array of <BlockStart>>
  the starts of blocks in the current method
  blockCount
  the index into blockStarts as they are being noted, and hence eventually the total number of blocks in the current method
  labelCounter <Integer>
  a nicety for numbering labels not needed in the production system but probably not expensive enough to worry about
  ceStackOverflowTrampoline <Integer>
  ceSend0ArgsTrampoline <Integer>
  ceSend1ArgsTrampoline <Integer>
  ceSend2ArgsTrampoline <Integer>
  ceSendNArgsTrampoline <Integer>
  ceSendSuper0ArgsTrampoline <Integer>
  ceSendSuper1ArgsTrampoline <Integer>
  ceSendSuper2ArgsTrampoline <Integer>
  ceSendSuperNArgsTrampoline <Integer>
  ceSICMissTrampoline <Integer>
  ceCPICMissTrampoline <Integer>
  ceStoreCheckTrampoline <Integer>
  ceReturnToInterpreterTrampoline <Integer>
  ceBaseFrameReturnTrampoline <Integer>
  ceSendMustBeBooleanTrampoline <Integer>
  ceClosureCopyTrampoline <Integer>
  the various trampolines (system-call-like jumps from machine code to the run-time).
  See Cogit>>generateTrampolines for the mapping from trampoline to run-time
  routine and then read the run-time routine for a funcitonal description.
  ceEnterCogCodePopReceiverReg <Integer>
  the enilopmart (jump from run-time to machine-code)
  methodZoneBase <Integer>
  !
  Cogit class
  instanceVariableNames: 'generatorTable primitiveTable'!

Item was changed:
  ----- Method: Cogit class>>declareCVarsIn: (in category 'translation') -----
  declareCVarsIn: aCCodeGenerator
  #( 'coInterpreter' 'objectMemory' 'methodZone' 'objectRepresentation'
  'cogBlockMethodSurrogateClass' 'cogMethodSurrogateClass' 'nsSendCacheSurrogateClass'
  'threadManager' 'processor' 'lastNInstructions' 'simulatedAddresses'
  'simulatedTrampolines' 'simulatedVariableGetters' 'simulatedVariableSetters'
  'printRegisters' 'printInstructions' 'clickConfirm' 'singleStep') do:
  [:simulationVariableNotNeededForRealVM|
  aCCodeGenerator removeVariable: simulationVariableNotNeededForRealVM].
  NewspeakVM ifFalse:
  [#( 'selfSendTrampolines' 'dynamicSuperSendTrampolines'
  'implicitReceiverSendTrampolines' 'outerSendTrampolines'
  'ceEnclosingObjectTrampoline' 'numIRCs' 'indexOfIRC' 'theIRCs') do:
  [:variableNotNeededInNormalVM|
  aCCodeGenerator removeVariable: variableNotNeededInNormalVM]].
  aCCodeGenerator removeConstant: #COGMTVM. "this should be defined at compile time"
  aCCodeGenerator
  addHeaderFile:'<stddef.h>'; "for e.g. offsetof"
  addHeaderFile:'"sqCogStackAlignment.h"';
  addHeaderFile:'"dispdbg.h"'; "must precede cointerp.h & cogit.h otherwise NoDbgRegParms gets screwed up"
  addHeaderFile:'"cogmethod.h"'.
  NewspeakVM ifTrue:
  [aCCodeGenerator addHeaderFile:'"nssendcache.h"'].
  aCCodeGenerator
  addHeaderFile:'#if COGMTVM';
  addHeaderFile:'"cointerpmt.h"';
  addHeaderFile:'#else';
  addHeaderFile:'"cointerp.h"';
  addHeaderFile:'#endif';
  addHeaderFile:'"cogit.h"'.
  aCCodeGenerator
  var: #ceGetFP
  declareC: 'usqIntptr_t (*ceGetFP)(void)';
  var: #ceGetSP
  declareC: 'usqIntptr_t (*ceGetSP)(void)';
  var: #ceCaptureCStackPointers
  declareC: 'void (*ceCaptureCStackPointers)(void)';
  var: #ceEnterCogCodePopReceiverReg
  declareC: 'void (*ceEnterCogCodePopReceiverReg)(void)';
  var: #realCEEnterCogCodePopReceiverReg
  declareC: 'void (*realCEEnterCogCodePopReceiverReg)(void)';
  var: #ceCallCogCodePopReceiverReg
  declareC: 'void (*ceCallCogCodePopReceiverReg)(void)';
  var: #realCECallCogCodePopReceiverReg
  declareC: 'void (*realCECallCogCodePopReceiverReg)(void)';
  var: #ceCallCogCodePopReceiverAndClassRegs
  declareC: 'void (*ceCallCogCodePopReceiverAndClassRegs)(void)';
  var: #realCECallCogCodePopReceiverAndClassRegs
  declareC: 'void (*realCECallCogCodePopReceiverAndClassRegs)(void)';
  var: #ceFlushICache
  declareC: 'static void (*ceFlushICache)(usqIntptr_t from, usqIntptr_t to)';
  var: #ceCheckFeaturesFunction
  declareC: 'static usqIntptr_t (*ceCheckFeaturesFunction)(void)';
  var: #ceCheckLZCNTFunction
  declareC: 'static usqIntptr_t (*ceCheckLZCNTFunction)(void)';
  var: #ceTryLockVMOwner
  declareC: 'usqIntptr_t (*ceTryLockVMOwner)(void)';
  var: #ceUnlockVMOwner
  declareC: 'void (*ceUnlockVMOwner)(void)';
  var: #postCompileHook
  declareC: 'void (*postCompileHook)(CogMethod *)';
  var: #openPICList declareC: 'CogMethod *openPICList = 0';
  var: #maxMethodBefore type: #'CogBlockMethod *';
  var: 'enumeratingCogMethod' type: #'CogMethod *'.
  aCCodeGenerator
  declareVar: 'aMethodLabel' type: #'AbstractInstruction'; "Has to come lexicographically before backEnd & methodLabel"
  var: #backEnd declareC: 'AbstractInstruction * const backEnd = &aMethodLabel';
  var: #methodLabel declareC: 'AbstractInstruction * const methodLabel = &aMethodLabel'.
  self declareC: #(abstractOpcodes stackCheckLabel
  blockEntryLabel blockEntryNoContextSwitch
  stackOverflowCall sendMiss
  entry noCheckEntry selfSendEntry dynSuperEntry
  fullBlockNoContextSwitchEntry fullBlockEntry
  picMNUAbort picInterpretAbort  endCPICCase0 endCPICCase1 cPICEndOfCodeLabel)
  as: #'AbstractInstruction *'
  in: aCCodeGenerator.
  aCCodeGenerator
  declareVar: #blockStarts type: #'BlockStart *';
  declareVar: #fixups type: #'BytecodeFixup *'.
  aCCodeGenerator
  var: #ordinarySendTrampolines
  declareC: 'sqInt ordinarySendTrampolines[NumSendTrampolines]';
  var: #superSendTrampolines
  declareC: 'sqInt superSendTrampolines[NumSendTrampolines]'.
  BytecodeSetHasDirectedSuperSend ifTrue:
  [aCCodeGenerator
  var: #directedSuperSendTrampolines
  declareC: 'sqInt directedSuperSendTrampolines[NumSendTrampolines]';
  var: #directedSuperBindingSendTrampolines
  declareC: 'sqInt directedSuperBindingSendTrampolines[NumSendTrampolines]'].
  NewspeakVM ifTrue:
  [aCCodeGenerator
  var: #selfSendTrampolines
  declareC: 'sqInt selfSendTrampolines[NumSendTrampolines]';
  var: #dynamicSuperSendTrampolines
  declareC: 'sqInt dynamicSuperSendTrampolines[NumSendTrampolines]';
  var: #implicitReceiverSendTrampolines
  declareC: 'sqInt implicitReceiverSendTrampolines[NumSendTrampolines]';
  var: #outerSendTrampolines
  declareC: 'sqInt outerSendTrampolines[NumSendTrampolines]'].
  aCCodeGenerator
  var: #trampolineAddresses
  declareC: 'static char *trampolineAddresses[NumTrampolines*2]';
  var: #objectReferencesInRuntime
  declareC: 'static usqInt objectReferencesInRuntime[NumObjRefsInRuntime+1]';
  var: #labelCounter
  type: #int;
  var: #traceFlags
  declareC: 'int traceFlags = 8 /* prim trace log on by default */';
  var: #cStackAlignment
  declareC: 'const int cStackAlignment = STACK_ALIGN_BYTES'.
  aCCodeGenerator
- declareVar: #CFramePointer type: #'void *';
- declareVar: #CStackPointer type: #'void *';
  declareVar: #minValidCallAddress type: #'usqIntptr_t';
  declareVar: #debugPrimCallStackOffset type: #'usqIntptr_t'.
  aCCodeGenerator vmClass generatorTable ifNotNil:
  [:bytecodeGenTable|
  aCCodeGenerator
  var: #generatorTable
  declareC: 'static BytecodeDescriptor generatorTable[', bytecodeGenTable size printString, ']',
  (self tableInitializerFor: bytecodeGenTable
  in: aCCodeGenerator)].
  "In C the abstract opcode names clash with the Smalltak generator syntactic sugar.
  Most of the syntactic sugar is inlined, but alas some remains.  Rename the syntactic
  sugar to avoid the clash."
  (self organization listAtCategoryNamed: #'abstract instructions') do:
  [:s|
  aCCodeGenerator addSelectorTranslation: s to: 'g', (aCCodeGenerator cFunctionNameFor: s)].
  aCCodeGenerator addSelectorTranslation: #halt: to: 'haltmsg'!

Item was changed:
  ----- Method: Cogit>>cFramePointerAddress (in category 'trampoline support') -----
  cFramePointerAddress
+ "Real VM's version is in CoInterpreter"
+ <doNotGenerate>
- <cmacro: '() ((usqIntptr_t)&CFramePointer)'>
  ^(backEnd wantsNearAddressFor: #CFramePointer)
  ifTrue: [self simulatedReadWriteVariableAddress: #getCFramePointer in: self]
  ifFalse: [coInterpreter inMemoryCFramePointerAddress]!

Item was changed:
  ----- Method: Cogit>>cStackPointerAddress (in category 'trampoline support') -----
  cStackPointerAddress
+ "Real VM's version is in CoInterpreter"
+ <doNotGenerate>
- <cmacro: '() ((usqIntptr_t)&CStackPointer)'>
  ^(backEnd wantsNearAddressFor: #CStackPointer)
  ifTrue: [self simulatedReadWriteVariableAddress: #getCStackPointer in: self]
  ifFalse: [coInterpreter inMemoryCStackPointerAddress]!

Item was changed:
  ----- Method: Cogit>>ceCaptureCStackPointers (in category 'jit - api') -----
  ceCaptureCStackPointers
  <api: 'extern void (*ceCaptureCStackPointers)()'>
  <doNotGenerate>
  | range |
  coInterpreter isThreadedVM ifFalse:
  [^self].
  thisContext sender selector == #generateStackPointerCapture ifTrue:
  [^self].
  range := coInterpreter cStackRangeForThreadIndex: coInterpreter threadManager getVMOwner.
  self assert: (range notNil "VM is owned"
  and: [(range includes: processor sp)
  and: [range includes: processor fp]]).
+ coInterpreter setCFramePointer: processor fp setCStackPointer: processor sp!
- self setCStackPointer: processor sp.
- self setCFramePointer: processor fp!

Item was changed:
  ----- Method: Cogit>>generateStackPointerCapture (in category 'initialization') -----
  generateStackPointerCapture
  "Generate a routine ceCaptureCStackPointers that will capture the C stack pointer,
  and, if it is in use, the C frame pointer.  These are used in trampolines to call
  run-time routines in the interpreter from machine-code."
 
  | oldMethodZoneBase oldTrampolineTableIndex |
  cFramePointerInUse := false. "For the benefit of the following assert, assume the minimum at first."
  self assertCStackWellAligned.
  oldMethodZoneBase := methodZoneBase.
  oldTrampolineTableIndex := trampolineTableIndex.
  self generateCaptureCStackPointers: true.
  self perform: #ceCaptureCStackPointers.
+ (cFramePointerInUse := coInterpreter checkIfCFramePointerInUse) ifFalse:
- (cFramePointerInUse := self isCFramePointerInUse) ifFalse:
  [methodZoneBase := oldMethodZoneBase.
  trampolineTableIndex := oldTrampolineTableIndex.
  self generateCaptureCStackPointers: false].
+ self assertCStackWellAligned!
- self assertCStackWellAligned.!

Item was removed:
- ----- Method: Cogit>>getCFramePointer (in category 'accessing') -----
- getCFramePointer
- <api>
- <cmacro: '() CFramePointer'>
- "and in the simulator we use..."
- ^(backEnd wantsNearAddressFor: #CFramePointer)
- ifTrue: [CFramePointer]
- ifFalse: [(objectMemory longAt: coInterpreter inMemoryCFramePointerAddress) asVoidPointer]!

Item was removed:
- ----- Method: Cogit>>getCStackPointer (in category 'accessing') -----
- getCStackPointer
- <api>
- <cmacro: '() CStackPointer'>
- "and in the simulator we use..."
- ^(backEnd wantsNearAddressFor: #CStackPointer)
- ifTrue: [CStackPointer]
- ifFalse: [(objectMemory longAt: coInterpreter inMemoryCStackPointerAddress) asVoidPointer]!

Item was changed:
  ----- Method: Cogit>>initializeProcessor (in category 'initialization') -----
  initializeProcessor
  "Initialize the simulation processor, arranging that its initial stack is somewhere on the rump C stack."
  <doNotGenerate>
  guardPageSize := self class guardPageSize.
  lastNInstructions := OrderedCollection new.
  processor initializeStackFor: self.
  self initializeProcessorStack: coInterpreter rumpCStackAddress.
+ coInterpreter setCFramePointer: processor fp setCStackPointer: processor sp.
- self setCFramePointer: processor fp.
- self setCStackPointer: processor sp.
  threadManager ifNotNil:
  [processor := MultiProcessor for: processor coInterpreter: coInterpreter]!

Item was removed:
- ----- Method: Cogit>>isCFramePointerInUse (in category 'initialization') -----
- isCFramePointerInUse
- <doNotGenerate>
- "This should be implemented externally, e.g. in sqPlatMain.c."
- ^true!

Item was removed:
- ----- Method: Cogit>>setCFramePointer: (in category 'accessing') -----
- setCFramePointer: aFramePointer
- <api>
- <cmacro: '(theFP) (CFramePointer = (void *)(theFP))'>
- "and in the simulator we use..."
- ^(backEnd wantsNearAddressFor: #CFramePointer)
- ifTrue: [CFramePointer := aFramePointer]
- ifFalse: [(objectMemory
- longAt: coInterpreter inMemoryCFramePointerAddress
- put: aFramePointer) asVoidPointer]!

Item was removed:
- ----- Method: Cogit>>setCStackPointer: (in category 'accessing') -----
- setCStackPointer: aStackPointer
- <api>
- <cmacro: '(theSP) (CStackPointer = (void *)(theSP))'>
- "and in the simulator we use..."
- ^(backEnd wantsNearAddressFor: #CStackPointer)
- ifTrue: [CStackPointer := aStackPointer]
- ifFalse: [(objectMemory
- longAt: coInterpreter inMemoryCStackPointerAddress
- put: aStackPointer) asVoidPointer]!

Item was changed:
  ----- Method: Cogit>>shortcutTrampoline:to: (in category 'simulation only') -----
  shortcutTrampoline: aProcessorSimulationTrap to: aBlock
  <doNotGenerate>
+ "As a simulation performance hack for debugging trampolines such as ceTraceLinkedSend: allow the entire
+ trampoline to be implemented by this method instead of simulating all of the code for the trampoline."
  backEnd hasLinkRegister ifTrue:
  [processor pushWord: processor lr in: coInterpreter memory].
  processor
  simulateLeafCallOf: aProcessorSimulationTrap address
  nextpc: aProcessorSimulationTrap nextpc
  memory: coInterpreter memory.
  coInterpreter
  stackPointer: processor sp;
  framePointer: processor fp.
  processor
+ sp: coInterpreter getCStackPointer;
+ fp: coInterpreter getCFramePointer.
- sp: self getCStackPointer;
- fp: self getCFramePointer.
  aBlock value.
  processor
  sp: coInterpreter stackPointer;
  fp: coInterpreter framePointer;
  simulateLeafReturnIn: coInterpreter memory.
  backEnd hasLinkRegister ifTrue:
  [processor lr: (processor popWordIn: coInterpreter memory)]!