2
0
Эх сурвалжийг харах

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

Jonas Maebe 3 жил өмнө
parent
commit
a73ee4f403

+ 44 - 9
compiler/x86_64/cpupara.pas

@@ -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 - 0
tests/webtbs/tw39296.pp

@@ -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.