Solves #39296: x86-64 parameter zero/sign extension

This commit is contained in:
Jonas Maebe 2021-08-22 14:56:19 +00:00 committed by FPK
parent a77f5221f3
commit a73ee4f403
2 changed files with 70 additions and 9 deletions

View File

@ -176,15 +176,13 @@ unit cpupara;
if size<=4 then
begin
cl.typ:=X86_64_INTEGERSI_CLASS;
{ gcc/clang sign/zero-extend all values to 32 bits, except for
_Bool (= Pascal boolean), which is only zero-extended to 8 bits
as per the x86-64 ABI -> do the same
some testing showed, that this is not true for 8 bit values:
in case of an 8 bit value, it is not zero/sign extended }
{ The ABI does not require any sign/zero extension for parameters,
except for _Bool (= Pascal boolean) to 8 bits. However, some
compilers (clang) extend them to 32 bits anyway and rely on it
-> also do it for compatibility when calling such code }
if not assigned(cl.def) or
not(cl.def.typ=orddef) or
not(torddef(cl.def).ordtype in [uchar,u8bit,s8bit,pasbool1]) then
(cl.def.typ<>orddef) or
(torddef(cl.def).ordtype<>pasbool1) then
cl.def:=u32inttype;
end
else
@ -1489,7 +1487,20 @@ unit cpupara;
end
else if result.intsize in [1,2,4] then
begin
paraloc^.size:=def_cgsize(paraloc^.def);
{ The ABI does not require sign/zero-extended function
results, but older versions of clang did so and
on Darwin current versions of clang keep doing so
for backward compatibility. On other platforms, it
doesn't and hence we don't either }
if (i=0) and
not(target_info.system in systems_darwin) and
(result.intsize in [1,2]) then
begin
paraloc^.size:=int_cgsize(result.intsize);
paraloc^.def:=cgsize_orddef(paraloc^.size);
end
else
paraloc^.size:=def_cgsize(paraloc^.def);
end
else
begin
@ -1785,6 +1796,30 @@ unit cpupara;
end
else
begin
{ some compilers sign/zero-extend on the callerside,
others don't. To be compatible with both, FPC
extends on the callerside, and assumes no
extension has been performed on the calleeside.
This is less efficient, but the alternative is
occasional crashes when calling code generated
by certain other compilers, or being called from
code generated by other compilers.
Exception: Darwin, since everyone there needs to
be compatible with the system compiler clang
(which extends on the caller side).
Not for LLVM, since there the zero/signext
attributes by definition only apply to the
caller side }
{$ifndef LLVM}
if not(target_info.system in systems_darwin) and
(side=calleeside) and
(hp.paraloc[side].intsize in [1,2]) then
begin
paraloc^.def:=hp.paraloc[side].def
end;
{$endif not LLVM}
paraloc^.size:=def_cgsize(paraloc^.def);
{ s64comp is pushed in an int register }
if paraloc^.size=OS_C64 then

26
tests/webtbs/tw39296.pp Normal file
View File

@ -0,0 +1,26 @@
{ %cpu=x86_64 }
{ %skiptarget=win64 }
function bytepara(b: byte; s: shortint): boolean; assembler; nostackframe;
asm
xorl %eax, %eax
cmpl $5, %edi
seteb %al
cmpl $-3, %esi
seteb %dl
andb %dl, %al
end;
var
b1: byte;
s1: shortint;
begin
b1:=5;
s1:=-3;
asm
movl $0x12345678, %edi
movl $0xabcdef01, %esi
end ['rsi', 'rdi'];
if not bytepara(b1,s1) then
halt(1);
end.