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;
begin
Result:=Assigned(AMenuItem) and (AMenuItem.Handle0);
if not Result then Exit;
NSMenuItem(AMenuItem.Handle).setEnabled( Enabled );
end;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 menuitem.inc:515
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 ; NSMenuItem._miFlags
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
