2018-10-11 19:41:25 UTC
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
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
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:
-> 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?
fpc-devel maillist - firstname.lastname@example.org