[flang-commits] [flang] [flang] Inherit target specific code for BIND(C) types on Windows (PR #129579)

via flang-commits flang-commits at lists.llvm.org
Thu Mar 6 07:30:35 PST 2025


jeanPerier wrote:

> Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller. For these aggregate types passed as a pointer, including __m128, the caller-allocated temporary memory must be 16-byte aligned.

It actually looks like a pretty straightforward ABI. I am reluctant to implement it myself because I cannot test end-to-end and I am not familiar with windows environment, but here is what the implementation could be below.

Two things need to be clarified by someone familiar with the windows 64 ABI and clang:
- why clang does not set the byval attribute when passing the struct in memory (like it is done for linux)?
- should the alignment of struct in memory be 16 like required when passing arguments, or the default alignment for the type?

```
diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index e2f8fb9d239a..a22c82316a4d 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -780,6 +780,45 @@ struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {
     }
     return marshal;
   }
+
+  CodeGenSpecifics::Marshalling
+  structArgumentType(mlir::Location loc, fir::RecordType type,
+                     const Marshalling &) const override {
+    std::uint64_t size =
+        fir::getTypeSizeAndAlignmentOrCrash(loc, type, getDataLayout(), kindMap)
+            .first;
+    CodeGenSpecifics::Marshalling marshal;
+    if (size <= 8)
+      marshal.emplace_back(mlir::IntegerType::get(type.getContext(), size * 8),
+                           AT{});
+    else
+      // TODO: investigate: clang is not setting the byval attribute, it is not
+      // clear why. Currently, this needs to be set here for flang so that the
+      // target-rewrite pass allocate memory as expected. Is it OK to set byval
+      // when clang does not?
+      marshal.emplace_back(fir::ReferenceType::get(type),
+                           AT{16, /*byval=*/true, /*sret=*/false});
+    return marshal;
+  }
+
+  CodeGenSpecifics::Marshalling
+  structReturnType(mlir::Location loc, fir::RecordType type) const override {
+    std::uint64_t size =
+        fir::getTypeSizeAndAlignmentOrCrash(loc, type, getDataLayout(), kindMap)
+            .first;
+    CodeGenSpecifics::Marshalling marshal;
+    if (size <= 8)
+      marshal.emplace_back(mlir::IntegerType::get(type.getContext(), size * 8),
+                           AT{});
+    else
+      // TODO: investigate: the ABI is not very clear about the alignment for
+      // the return in memory (while it was explicit about 16 for the argument
+      // passing case). Should it be the default alignment for that type
+      // instead?
+      marshal.emplace_back(fir::ReferenceType::get(type),
+                           AT{16, /*byval=*/false, /*sret=*/true});
+    return marshal;
+  }
 };
 } // namespace
```

I validated on simple example that it does what I would expect reading the ABI:

```
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"} {

// Cases where the type must be passed/returned in register.

func.func @test_t1(%0 : !t1) -> () {
  return
}

func.func @test_call_t1(%0 : !fir.ref<!t1>) {
  %1 = fir.load %0 : !fir.ref<!t1>
  fir.call @test_t1(%1)  : (!t1) -> ()
  return
}

func.func @test_return_t1(%0 : !fir.ref<!t1>) -> !t1 {
  %1 = fir.load %0 : !fir.ref<!t1>
  return %1 : !t1
}

func.func @test_call_return_t1(%0 : !fir.ref<!t1>) -> () {
  %1 = fir.call @test_return_t1(%0)  : (!fir.ref<!t1>) -> !t1
  fir.store %1 to %0 : !fir.ref<!t1>
  return
}

// Cases where the type must be passed/returned on the stack.

func.func @test_call_t2(%0 : !fir.ref<!t2>) {
  %1 = fir.load %0 : !fir.ref<!t2>
  fir.call @test_t2(%1)  : (!t2) -> ()
  return
}
func.func @test_t2(%0 : !t2) -> () {
  return
}

func.func @test_return_t2(%0 : !fir.ref<!t2>) -> !t2 {
  %1 = fir.load %0 : !fir.ref<!t2>
  return %1 : !t2
}

func.func @test_call_return_t2(%0 : !fir.ref<!t2>) -> () {
  %1 = fir.call @test_return_t2(%0)  : (!fir.ref<!t2>) -> !t2
  fir.store %1 to %0 : !fir.ref<!t2>
  return
}
}
```

https://github.com/llvm/llvm-project/pull/129579


More information about the flang-commits mailing list