[flang-commits] [flang] [flang] Use correct int extension flags for C-ABI calls on aarch64 (PR #137105)

Asher Mancinelli via flang-commits flang-commits at lists.llvm.org
Thu Apr 24 14:03:13 PDT 2025


https://github.com/ashermancinelli updated https://github.com/llvm/llvm-project/pull/137105

>From e03bac791dd2e70eb43001dbf5a3b756e44eb76d Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Wed, 23 Apr 2025 09:14:42 -0700
Subject: [PATCH 1/2] [flang] Use correct int extension flags for C-ABI calls
 on aarch64

The AArch64 procedure call standard does not mandate that the callee extends the return
value. Clang does not add signext to functions returning i8 or i16 on aarch64, except on
Darwin, but flang does.

This means that runtime routines returning i8's (e.g. MINVAL on an array of
INTEGER(KIND=1)) will have signext on the callsite/declaration in user code,
but not on the implementation, and the call site will assume the return value has already
been sign extended when it has not.

Adjust our integer extension flags to match clang and aarch64pcs on linux. The behavior
on Darwin should be preserved. This is listed on the apple developer guide as a divergence
from aarch64pcs.
---
 flang/lib/Optimizer/CodeGen/Target.cpp    | 29 ++++++++++++++++++++++-
 flang/test/Fir/convert-to-llvm-target.fir |  2 +-
 flang/test/Fir/target-rewrite-integer.fir | 21 ++++++++++++++--
 3 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index 374308fa58947..b2223d95729a0 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -784,7 +784,7 @@ struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {
 } // namespace
 
 //===----------------------------------------------------------------------===//
-// AArch64 linux target specifics.
+// AArch64 target specifics.
 //===----------------------------------------------------------------------===//
 
 namespace {
@@ -810,6 +810,33 @@ struct TargetAArch64 : public GenericTarget<TargetAArch64> {
     return marshal;
   }
 
+  CodeGenSpecifics::Marshalling
+  integerArgumentType(mlir::Location loc,
+                      mlir::IntegerType argTy) const override {
+    if (argTy.getWidth() < getCIntTypeWidth() && argTy.isSignless()) {
+      AT::IntegerExtension intExt;
+      if (argTy.getWidth() == 1) {
+        // Zero extend for 'i1'.
+        intExt = AT::IntegerExtension::Zero;
+      } else {
+        if (triple.isOSDarwin())
+          // On Darwin, sign extend. The apple developer guide specifies this as
+          // a divergence from the AArch64PCS:
+          // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly
+          intExt = AT::IntegerExtension::Sign;
+        else
+          // On linux, pass directly and do not extend.
+          intExt = AT::IntegerExtension::None;
+      }
+      CodeGenSpecifics::Marshalling marshal;
+      marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
+                                     /*sret=*/false, /*append=*/false,
+                                     /*intExt=*/intExt});
+      return marshal;
+    }
+    return GenericTarget::integerArgumentType(loc, argTy);
+  }
+
   CodeGenSpecifics::Marshalling
   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
     CodeGenSpecifics::Marshalling marshal;
diff --git a/flang/test/Fir/convert-to-llvm-target.fir b/flang/test/Fir/convert-to-llvm-target.fir
index bb873080072fa..6adc5cd4b737f 100644
--- a/flang/test/Fir/convert-to-llvm-target.fir
+++ b/flang/test/Fir/convert-to-llvm-target.fir
@@ -1,7 +1,7 @@
 // RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefix INT64
 // RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes INT64
 // RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefixes INT32
-// RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=powerpc64le-unknown-linux-gn" %s | FileCheck %s --check-prefixes INT64
+// RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=powerpc64le-unknown-linux-gnu" %s | FileCheck %s --check-prefixes INT64
 
 //=============================================================================
 // SUMMARY: Tests for FIR --> LLVM MLIR conversion that *depend* on the target
diff --git a/flang/test/Fir/target-rewrite-integer.fir b/flang/test/Fir/target-rewrite-integer.fir
index 3be76e21a420f..4cb2026cf9ee0 100644
--- a/flang/test/Fir/target-rewrite-integer.fir
+++ b/flang/test/Fir/target-rewrite-integer.fir
@@ -1,6 +1,8 @@
 // RUN: fir-opt --split-input-file --target-rewrite="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=I32,ALL
 // RUN: fir-opt --split-input-file --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=X64,ALL
+// RUN: fir-opt --split-input-file --target-rewrite="target=x86_64-apple-darwin" %s | FileCheck %s --check-prefixes=X64,ALL
 // RUN: fir-opt --split-input-file --target-rewrite="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=AARCH64,ALL
+// RUN: fir-opt --split-input-file --target-rewrite="target=aarch64-apple-darwin" %s | FileCheck %s --check-prefixes=AARCH64DARWIN,ALL
 // RUN: fir-opt --split-input-file --target-rewrite="target=powerpc64le-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=PPC,ALL
 // RUN: fir-opt --split-input-file --target-rewrite="target=sparc64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=SPARCV9,ALL
 // RUN: fir-opt --split-input-file --target-rewrite="target=sparcv9-sun-solaris2.11" %s | FileCheck %s --check-prefixes=SPARCV9,ALL
@@ -17,6 +19,7 @@
 // I32: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
 // X64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
 // AARCH64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
+// AARCH64DARWIN: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
 // PPC: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
 // SPARCV9: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
 // LOONGARCH64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
@@ -49,6 +52,7 @@ func.func private @_FortranAioEndIoStatement(!fir.ref<i8>) -> i32 attributes {fi
 // I32: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
 // X64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
 // AARCH64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
+// AARCH64DARWIN: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
 // PPC: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
 // SPARCV9: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
 // LOONGARCH64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
@@ -69,6 +73,7 @@ func.func private @_SomeFunc_si1(si1) -> si1 attributes {fir.runtime}
 // I32: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
 // X64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
 // AARCH64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
+// AARCH64DARWIN: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
 // PPC: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
 // SPARCV9: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
 // LOONGARCH64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
@@ -99,8 +104,20 @@ func.func private @_SomeFunc_ui1(ui1) -> ui1 attributes {fir.runtime}
 // end subroutine test
 
 // ALL-LABEL: @_QPtest_bindc
-// ALL: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
-// ALL: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
+// I32: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// I32: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
+// X64: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// X64: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
+// AARCH64: func.func private @cfun8(i8) -> i8 attributes {fir.bindc_name = "cfun8"}
+// AARCH64: func.func private @cfun16(i16) -> i16 attributes {fir.bindc_name = "cfun16"}
+// AARCH64DARWIN: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// AARCH64DARWIN: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
+// PPC: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// PPC: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
+// SPARCV9: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// SPARCV9: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
+// LOONGARCH64: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// LOONGARCH64: func.func private @cfun16(i16 {llvm.signext}) -> (i16 {llvm.signext}) attributes {fir.bindc_name = "cfun16"}
 func.func @_QPtest_bindc(%arg0: !fir.ref<i8> {fir.bindc_name = "x"}, %arg1: !fir.ref<i16> {fir.bindc_name = "y"}) {
   %0 = fir.load %arg0 : !fir.ref<i8>
   %1 = fir.call @cfun8(%0) fastmath<contract> : (i8) -> i8

>From 4485825ba7428a701555f5c3c3f4196a0d982e57 Mon Sep 17 00:00:00 2001
From: Asher Mancinelli <ashermancinelli at gmail.com>
Date: Thu, 24 Apr 2025 14:02:56 -0700
Subject: [PATCH 2/2] format

---
 flang/lib/Optimizer/CodeGen/Target.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index b2223d95729a0..7dbf21ce0c125 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -819,14 +819,15 @@ struct TargetAArch64 : public GenericTarget<TargetAArch64> {
         // Zero extend for 'i1'.
         intExt = AT::IntegerExtension::Zero;
       } else {
-        if (triple.isOSDarwin())
+        if (triple.isOSDarwin()) {
           // On Darwin, sign extend. The apple developer guide specifies this as
           // a divergence from the AArch64PCS:
           // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly
           intExt = AT::IntegerExtension::Sign;
-        else
+        } else {
           // On linux, pass directly and do not extend.
           intExt = AT::IntegerExtension::None;
+        }
       }
       CodeGenSpecifics::Marshalling marshal;
       marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,



More information about the flang-commits mailing list