Issue 185052
Summary [Clang] ppc64le ELF V2 ABI "Full doubleword rule" not followed
Labels clang
Assignees
Reporter TheRealMDoerr
    The calling convention in the "Power Architecture 64-Bit ELF V2 ABI Specification" requires "When a doubleword in the Parameter Save Area (or its GPR copy) contains at least a portion of a structure, that doubleword must contain all other portions mapping to the same doubleword."
This is called the "Full doubleword rule" or "no partial DW rule". Figure 2.24 shows an example.

Unfortunately, this rule seems to be violated when building with
```
clang version 18.1.8
Target: powerpc64le-unknown-linux-gnu
```

According to the rule, `pass_struct_after_floats` in the following program must pass `s1.p0` in a floating point register and the whole `s1` on stack.

```C
// gcc -O1 -fPIC -c -o TestHFAPassing.o TestHFAPassing.c
// or
// clang -O1 -fPIC -c -o TestHFAPassing.o TestHFAPassing.c

// ld -shared -z noexecstack -o TestHFAPassing.so TestHFAPassing.o

struct S_FF { float p0; float p1; };
 
struct S_FF pass_struct_after_floats(struct S_FF (*fun)(
        float, float, float, float, float,
        float, float, float, float, float,
 float, float, struct S_FF),
        struct S_FF s1) {
    return fun(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, s1);
}
```

GCC stores the whole `s1` on stack offset 128 that, but clang 18.1.8 only stores the 2nd half violating the rule:
```
fmr f13,f1
stfs    f2,132(r1)
```

That works as long as the function `fun` only uses the value from `f13` and never from stack. Both, gcc and clang seem to prefer that, so the problem is not visible when `fun` is created by these compilers. However, other compilers may use the value from stack.

That is the case in OpenJDK 25:
```java
import java.io.File;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

import static java.lang.foreign.ValueLayout.*;

public class TestHFAPassing {

 static {
        // Resolve absolute path to the DLL in the current directory
        String libPath = new File("TestHFAPassing.so").getAbsolutePath();
 System.load(libPath);
    }

    static final Linker abi = Linker.nativeLinker();
    static final SymbolLookup lookup = SymbolLookup.loaderLookup();

    static final OfFloat C_FLOAT = (ValueLayout.OfFloat) abi.canonicalLayouts().get("float");

    static final GroupLayout S_FFLayout = MemoryLayout.structLayout(
 C_FLOAT.withName("p0"),
        C_FLOAT.withName("p1")
 ).withName("S_FF");

    static final FunctionDescriptor fdadd_float_to_struct_after_floats = FunctionDescriptor.of(S_FFLayout,
 C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT,
        C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT,
        C_FLOAT, C_FLOAT, S_FFLayout);

 static final FunctionDescriptor fdpass_struct_after_floats = FunctionDescriptor.of(S_FFLayout, ADDRESS, S_FFLayout);

    static final MethodHandle mhpass_struct_after_floats = abi.downcallHandle(lookup.find("pass_struct_after_floats").orElseThrow(),
 fdpass_struct_after_floats);

    // callback function
    public static MemorySegment addFloatToStructAfterFloats(
            float f1, float f2, float f3, float f4, float f5,
            float f6, float f7, float f8, float f9, float f10,
            float f11, float f12, MemorySegment s) {
        float val = s.get(C_FLOAT, 0);
 s.set(C_FLOAT, 0, val + 1.0f);
        return s;
    }

    void main() {
        float p0 = 0.0f, p1 = 0.0f;
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment s = arena.allocate(S_FFLayout);
            s.set(C_FLOAT, 0, 1.0f);
 s.set(C_FLOAT, 4, 1.0f);
            MethodType mt = MethodType.methodType(MemorySegment.class,
 float.class, float.class, float.class, float.class,
 float.class, float.class, float.class, float.class,
 float.class, float.class, float.class, float.class,
 MemorySegment.class);
            MemorySegment stub = abi.upcallStub(MethodHandles.lookup().findStatic(TestHFAPassing.class, "addFloatToStructAfterFloats", mt),
 fdadd_float_to_struct_after_floats, arena);
            s = (MemorySegment)mhpass_struct_after_floats.invokeExact((SegmentAllocator)arena, stub, s);
            p0 = s.get(C_FLOAT, 0);
            p1 = s.get(C_FLOAT, 4);
            System.out.println("S_FF(" + p0 + ";" + p1 + ")");
        } catch (Throwable t) {
 t.printStackTrace();
        }
        if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("wrong result from add_float_to_struct_after_floats (Upcall)");
    }
}
```

This reproduces the error.

_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to