[fpc-devel] OSX: Setting up parameters to objc_msgSend()

David Jenkins david at scootersoftware.com
Thu Oct 11 21:41:25 CEST 2018

This is specifically about objectivce C bridge.  Hopefully this is the 
right forum.

I am seeing a problem with how BOOL values are passed to objc_msgSend.  
The example is a call to NSMenuItem.setEnable(BOOL) from the 
lcl/interfaces/cocoa/cocoawsmenus.pas file.  Here is the code:

class function TCocoaWSMenuItem.SetEnable(const AMenuItem: TMenuItem;
   const Enabled: boolean): boolean;
   Result:=Assigned(AMenuItem) and (AMenuItem.Handle<>0);
   if not Result then Exit;
   NSMenuItem(AMenuItem.Handle).setEnabled( Enabled );

Here is the assembly code generated by fpc (3.1.1) for just the section 
where .setEnabled(Enabled) is called

->  0x1001ba68d <+61>:  movq   -0x8(%rbp), %rdi
     0x1001ba691 <+65>:  callq  0x1002016d0               ; GETHANDLE at 
     0x1001ba696 <+70>:  movq   %rax, %rbx
     0x1001ba699 <+73>:  movq   0x329ee0(%rip), %rsi      ; "setEnabled:"
     0x1001ba6a0 <+80>:  movb   -0x10(%rbp), %dl
     0x1001ba6a3 <+83>:  movq   %rbx, %rdi
     0x1001ba6a6 <+86>:  callq  0x1002d23da               ; symbol stub 
for: objc_msgSend

Notice the the value of Enabled (a BOOL) is treated as 8bit (which BOOL 
is) and placed into the lowest 8 bits of edx with the line 'movb   
-0x10(%rbp), %dl'.  Which means that the upper 24 bits of edx are not 
changed (not cleared, not signed extended, nothing, just left alone).

Here is the assembly code generated by XCode for a similar call to 
NSMenuItem.setEnable() from objective c code:
0x100001258 <+440>: movq   -0x40(%rbp), %rax
     0x10000125c <+444>: movb   -0x19(%rbp), %cl
     0x10000125f <+447>: movq   0x185a(%rip), %rsi        ; "setEnabled:"
     0x100001266 <+454>: movq   %rax, %rdi
     0x100001269 <+457>: movsbl %cl, %edx
     0x10000126c <+460>: callq  *0xd9e(%rip)              ; (void 
*)0x00007fff55e30e80: objc_msgSend

Register 'edx' is the register of concern again.  In this case the 8 bit 
BOOL value stored in %cl is placed into %edx with the line 'movsbl %cl, 
%edx'.  In other words the 8 bit BOOL value is sign extended through all 
the bits of %edx.

This is different than what fpc does and I think causing a problem. The 
top level symptom of the problem that lead me to look at this is that 
currently I cannot manually enable/disable NSMenuItem's because calling 
.setEnable doesn't work.

Looking at the disassembly for .setEnable I see that the 'disable' flag 
for the object is stored in a 32bit record bitfield (bit 8) in 
NSMenuItem._miFlags;  The code loads %ecx with the _miFlags, shifts left 
8 to get the 'disable' bit in to 0 position, and then ands with 
0x00000001 to mask off all other bits.  It then does a cmpl of %edx and 
%ecx to determine if the NSMenuITem is already in the desired state or 
not.  If it is in the desired state then the code exists without 
changing the state.

here is the assembly for the first part of NSMenuItem.setEnabled:

AppKit`-[NSMenuItem setEnabled:]:
->  0x7fff2bff1a5f <+0>:   pushq  %rbp
     0x7fff2bff1a60 <+1>:   movq   %rsp, %rbp
     0x7fff2bff1a63 <+4>:   pushq  %r15
     0x7fff2bff1a65 <+6>:   pushq  %r14
     0x7fff2bff1a67 <+8>:   pushq  %rbx
     0x7fff2bff1a68 <+9>:   pushq  %rax
     0x7fff2bff1a69 <+10>:  movq   %rdi, %rbx
      0x7fff2bff1a6c <+13>:  movq   0x5c365045(%rip), %rax    ; 
     0x7fff2bff1a73 <+20>:  movl   (%rbx,%rax), %esi
     0x7fff2bff1a76 <+23>:  movl   %esi, %ecx
     0x7fff2bff1a78 <+25>:  shrl   $0x8, %ecx
     0x7fff2bff1a7b <+28>:  andl   $0x1, %ecx
     0x7fff2bff1a7e <+31>:  cmpl   %edx, %ecx
     0x7fff2bff1a80 <+33>:  jne    0x7fff2bff1ae8            ; <+137>

The FPC code works when %edx upper bits are, by luck, empty but not when 
they have non-zero bits.  On my machine coming into the above LCL code 
%edx always has upper bits set and I cannot get .setEnable to work.

It appears to me that objc_msgSend is expecting that parameters passed 
in are signed extended and that any code called by objc_msgSend will 
treat the registers as such.  I spent some time trying to find 
documentation for objc runtime / objc_msgSend that shows this but didn't 
find anything that went into that level of detail.

If objc_msgSend does expect this then I think the FPC objective c 
bridging is not correct.  Can any of you all verify this?

David Jenkins
Scooter Software

More information about the fpc-devel mailing list