<div dir="ltr"><div dir="ltr"><div dir="ltr">Hi Ken,</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Mar 14, 2019 at 2:09 PM <<a href="mailto:ken.dickey@whidbey.com">ken.dickey@whidbey.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"> Greetings,<br>
<br>
I have been making small progress in getting Pharo's FFI unit tests to <br>
pass on arm64.<br>
<br>
Built from recent<br>
   OpenSmalltalk opensmalltalk-vm build.linux64ARMv8/pharo.stack.spur<br>
<br>
I get a pharo vm whose UI is pretty slow (no JIT so far), so current <br>
progress is slow.<br>
<br>
I am looking at #FFICallbackThunk>initializeARM32 but I don't yet <br>
understand quite what this code is trying to do, so have not made proper <br>
changes for arm64.<br>
<br>
Anybody care to join in or advise me on this?<br>
<br>
Note that I am neither a Pharo nor a UnifiedFFI user, so I may be making <br>
really dumb mistakes at this point (i.e. _you_ could help me out here!  <br>
8^).<br></blockquote><div><br></div><div>Oops!  I should have directed your attention to the comment for Callback that I wrote back in September of 2016:</div><div><br></div><div>Callbacks encapsulate callbacks from the outside world.  They allow Smalltalk blocks to be evaluated and answer their results to external (e.g. C) callees.  Callbacks are created with signature:block:, e.g.</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>cb := Callback</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                 </span>signature:  #(int (*)(const void *, const void *))</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                    </span>block: [ :arg1 :arg2 | ((arg1 doubleAt: 1) - (arg2 doubleAt: 1)) sign].</div><div><br></div><div>and passed through the FFI by passing their pointer, e.g.</div><div><br></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>self qui: data ck: data size so: 8 rt: cb pointer</div><div><br></div><div>When the callback is made, the system arranges that the block is invoked with the arguments as defined by the signature, and the result of the block passed back, again as defined by the signature.  See methods in the signatures protocol in subclasses of Callback for signature methods that decode the C stack and registers to invoke a callback with parsed arguments.  See Callback>>valueInContext: and subclass implementations for the evaluation of the signature method that invokes the callback block with correctly parsed arguments.  See Alien class>>invokeCallbackContext: for the entry-point for callbacks into the system from the VM.</div><div><br></div><div>Instance Variables:</div><div>block <BlockClosure> - The Smalltalk code to be run in response to external code invoking the callback.</div><div>thunk <FFICallbackThunk> - the wrapper around the machine-code thunk that initiates the callback and whose address should be passed to C</div><div>evaluator <Symbol> - the selector of the marshalling method to use; see methods in the signatures protocol in subclasses of Callback.</div><div>numEvaluatorArgs <Integer> - the arity of evaluator</div><div>argsProxyClass <Alien subclass> - legacy; unused; the wrapper around the thunk's incoming stack pointer, used to extract arguments from the stack.</div><div><br></div><div>Class Variables:</div><div>ThunkToCallbackMap <Dictionary of: thunkAddress <Integer> -> callback <Callback>> - used to lookup the Callback associated with a specific thunk address on callback.  See FFICallbackThunk.</div><div>ABI <String> - the name of the current ABI</div><div><br></div><div>Class Instance Variables</div><div>concreteClass <Callback subclass> - the concrete class for callbacks on the current platform, or nil if one doesn't yet exist.</div><div><br></div><div>Implementation:</div><div>The way that it works is in two parts</div><div>- on callback the VM passes up a pointer to a structure from which all arguments, stacked and in registers (because the VM has copied any register args into the struct) can be accessed, and through which the result can be returned.</div><div>- the image level provides marshalling methods that match the signature in the callback.  Marshalling methods belong in concrete subclasses, one subclass for each ABI.</div><div><br></div><div>So e.g. with a callback of</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">               </span>Callback</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                       </span>signature:  #(int (*)(const void *, const void *))</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                    </span>block: [ :arg1 :arg2 | ((arg1 doubleAt: 1) - (arg2 doubleAt: 1)) sign]</div><div>the marshalling methods are in one of Callback's concrete subclasses signatures protocol, for example</div><div><br></div><div>CallbackForIA32>>voidstarvoidstarRetint: callbackContext sp: spAlien</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span><signature: #(int (*)(const void *, const void *))></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>^callbackContext wordResult:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">           </span>(block</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                 </span>value: (Alien forPointer: (spAlien unsignedLongAt: 1))</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                 </span>value: (Alien forPointer: (spAlien unsignedLongAt: 5)))</div><div><br></div><div>where spAlien is an Alien pointing to a VMCallbackContext32.</div><div><br></div><div>For ARM support, where there the first four integer arguments are passed in registers, we can use</div><div><br></div><div>CallbackForARM32>>voidstarvoidstarRetint: callbackContext regs: regsAlien</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span><signature: #(int (*)(const void *, const void *))></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>^callbackContext wordResult:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">           </span>(block</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                 </span>value: (Alien forPointer: (regsAlien unsignedLongAt: 1))</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                       </span>value: (Alien forPointer: (regsAlien unsignedLongAt: 5)))</div><div><br></div><div>The selector of the method doesn't matter, providing it doesn't conflict with any other, except for the number of arguments.  What's important is the pragma which defines the signature and the ABI for which this is a valid marshalling method.  Support for callee pop callbacks (Pascal calling convention such as the Win32 stdcall: convention) are supported using the <calleepops: N> pragma which specifies how many bytes to pop.</div><div><br></div><div>When a callback is instantiated, Callback introspects to find the marshalling method that matches the signature for the current ABI.  If one doesn't already exist you can write one.  Hopefully we'll write an ABI compiler that will automatically generate these marshalling methods according to the platform's ABI, but for now its a manual process.; at least it's open and flexible.  When the callback is invoked the evaluator is performed with the current callbackContext and pointer(s) to the arguments.  There is a 32-bit and a 64-bit callback context, and it can have a stack pointer, integer register args and floating point register args, so it's general enough for any callback.</div><div><br></div><div>To pass back the result, a value is assigned into the struct via the accessor in the marshalling method and control returns to teh point where teh callback comes in, and this uses a primitive to return.  Inside the callbackContext is a jmpbuf from a setjmp.  The primitive longjmp's back to the entry point in the VM which extracts the result and the code for the kind of result and returns.  See Callback class>>invokeCallbackContext:</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Thanks much in advance,<br>
-KenD<br>
</blockquote></div><div><br></div><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div></div></div>