| Issue |
56469
|
| Summary |
PowerPC 32-bit passes float with wrong alignment
|
| Labels |
|
| Assignees |
|
| Reporter |
kernigh
|
If clang targets 32-bit PowerPC and passes a 4-byte float on the stack, then clang uses a wrong alignment of 8 bytes. This is not compatible with gcc, libffcall, nor libffi, which expect an alignment of 4 bytes. This issue is rare: it can only happen when a function has more than 8 floating-point arguments (because the 32-bit ELF ABI passes 8 such arguments in registers, without allocating stack space) and the 9th or later argument is a float in the prototype.
Given this _pass.c_,
```c
#include <stdio.h>
void pass(float a, float b, float c, float d, float e, float f,
float g, float h, float i, float j, float k, float l)
{
printf("i = %g, j = %g, k = %g, l = %g\n", i, j, k, l);
}
```
Today's clang miscompiles it,
```
$ clang-15 -v
clang version 15.0.0 ([email protected]:llvm/llvm-project.git 28b41237e6b296bf777d2f0c13c48031525fcdc4)
Target: powerpc-unknown-openbsd
Thread model: posix
InstalledDir: /home/kernigh/bin
$ clang-15 -S -O1 pass.c
```
I see wrong offsets in _pass.s_,
```
stwu 1, -16(1)
lfs 1, 24(1)
lfs 2, 32(1)
lfs 3, 40(1)
lfs 4, 48(1)
```
The stack arguments i, j, k, l are at 8(%r1) before _pass_ allocates its stack frame, or 24(%r1) after. The code must load them into %f1, %f2, %f3, %f4 for _printf_. The correct offsets are 24, 28, 32, 36 (every 4 bytes), but clang uses 24, 32, 40, 48 (every 8 bytes).
I expose the problem by building a shared object `cc -shared -o libpass.so pass.c` (with clang 13, which has the same problem), then calling it with libffcall or libffi.
This _call.lisp_ for GNU CLISP will use libffcall,
```lisp
(ffi:default-foreign-language :stdc)
(ffi:def-call-out pass (:return-type nil)
(:arguments (a single-float) (b single-float) (c single-float)
(d single-float) (e single-float) (f single-float)
(g single-float) (h single-float) (i single-float)
(j single-float) (k single-float) (l single-float))
(:library "./libpass.so"))
(pass 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0)
```
This _call.rb_ for Ruby will use libffi,
```rb
require 'fiddle/import'
module Pass
extend Fiddle::Importer
dlload './libpass.so'
extern 'void pass(float, float, float, float, float, float,
float, float, float, float, float, float)'
end
Pass.pass 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
```
The output is wrong,
```
$ clisp call.lisp
i = 9, j = 11, k = 3.54381e-29, l = 4.04994e-29
$ ruby call.rb
i = 9, j = 11, k = 1.875, l = 2
```
If I rebuild libpass.so with gcc, then they give the correct output `i = 9, j = 10, k = 11, l = 12`
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs