[flang-commits] [flang] [flang][CodeGen] Add initial SystemZ ABI support (PR #208618)

via flang-commits flang-commits at lists.llvm.org
Thu Jul 9 21:27:02 PDT 2026


https://github.com/anoopkg6 created https://github.com/llvm/llvm-project/pull/208618

Add initial implementation to enable target-specific code generation and rewriting support for SystemZ architecture in Flang.


@uweigand @dominik-steenken

>From 26e5075029b4bdf663b4d63f77e4a0481bf2e08c Mon Sep 17 00:00:00 2001
From: "anoop.kumar6 at ibm.com" <anoopk at b35lp63.lnxne.boe>
Date: Fri, 10 Jul 2026 03:24:18 +0200
Subject: [PATCH] [flang][CodeGen] Add initial SystemZ ABI support

---
 flang/lib/Frontend/CompilerInvocation.cpp     |  6 ++
 flang/lib/Optimizer/CodeGen/Target.cpp        | 79 ++++++++++++++++++
 flang/test/Driver/code-gen-systemz.f90        |  9 ++
 flang/test/Driver/emit-asm-systemz.f90        | 11 +++
 .../test/Driver/predefined-macros-systemz.f90 | 23 +++++
 .../test/Fir/struct-passing-systemz-byval.fir | 51 ++++++++++++
 flang/test/Fir/struct-return-systemz.fir      | 63 ++++++++++++++
 flang/test/Fir/target-rewrite-boxchar.fir     |  1 +
 flang/test/Fir/target-rewrite-char-proc.fir   |  1 +
 flang/test/Fir/target-rewrite-complex.fir     | 83 +++++++++++++++++++
 .../Fir/target-rewrite-complex16-systemz.fir  | 69 +++++++++++++++
 .../Fir/target-rewrite-indirect-calls.fir     |  1 +
 flang/test/Fir/target-rewrite-integer.fir     | 18 ++++
 flang/test/Fir/target-rewrite-selective.fir   |  7 ++
 flang/test/Lower/OpenMP/simd_systemz.f90      | 16 ++++
 flang/test/Semantics/realkinds-systemz-01.f90 | 12 +++
 16 files changed, 450 insertions(+)
 create mode 100644 flang/test/Driver/code-gen-systemz.f90
 create mode 100644 flang/test/Driver/emit-asm-systemz.f90
 create mode 100644 flang/test/Driver/predefined-macros-systemz.f90
 create mode 100644 flang/test/Fir/struct-passing-systemz-byval.fir
 create mode 100644 flang/test/Fir/struct-return-systemz.fir
 create mode 100644 flang/test/Fir/target-rewrite-complex16-systemz.fir
 create mode 100644 flang/test/Lower/OpenMP/simd_systemz.f90
 create mode 100644 flang/test/Semantics/realkinds-systemz-01.f90

diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 79ad08353b64c..339b83f351152 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1899,6 +1899,12 @@ void CompilerInvocation::setDefaultPredefinitions() {
     fortranOptions.predefinitions.emplace_back("__aarch64__", "1");
     fortranOptions.predefinitions.emplace_back("__aarch64", "1");
     break;
+  case llvm::Triple::ArchType::systemz:
+    fortranOptions.predefinitions.emplace_back("__s390__", "1");
+    fortranOptions.predefinitions.emplace_back("__s390x__", "1");
+    fortranOptions.predefinitions.emplace_back("__s390x", "1");
+    fortranOptions.predefinitions.emplace_back("__zarch__", "1");
+    break;
   }
 }
 
diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index 48946d78d900c..37838e9e2e558 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -1893,6 +1893,81 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
 };
 } // namespace
 
+//===----------------------------------------------------------------------===//
+// SystemZ target specifics.
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct TargetSystemZ : public GenericTarget<TargetSystemZ> {
+  using GenericTarget::GenericTarget;
+
+  static constexpr int defaultWidth = 64;
+
+  auto complexType(mlir::Type eleTy, bool isResult) const {
+    assert(fir::isa_real(eleTy));
+    CodeGenSpecifics::Marshalling marshal;
+    unsigned short align{std::max(
+        static_cast<unsigned short>(getDataLayout().getTypeABIAlignment(eleTy)),
+        static_cast<unsigned short>(8))};
+    marshal.emplace_back(
+        fir::ReferenceType::get(mlir::TupleType::get(
+            eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
+        AT{/*align=*/align, /*byval=*/!isResult, /*sret=*/isResult});
+    return marshal;
+  }
+
+  CodeGenSpecifics::Marshalling
+  complexArgumentType(mlir::Location /*loc*/, mlir::Type eleTy) const override {
+    return complexType(eleTy, false);
+  }
+
+  CodeGenSpecifics::Marshalling
+  complexReturnType(mlir::Location /*loc*/, mlir::Type eleTy) const override {
+    return complexType(eleTy, true);
+  }
+
+  CodeGenSpecifics::Marshalling
+  integerArgumentType(mlir::Location loc,
+                      mlir::IntegerType argTy) const override {
+    if (argTy.getWidth() == 32) {
+      AT::IntegerExtension intExt = argTy.isUnsigned()
+                                        ? AT::IntegerExtension::Zero
+                                        : AT::IntegerExtension::Sign;
+      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
+  structType(mlir::Location loc, fir::RecordType ty, bool isResult) const {
+    CodeGenSpecifics::Marshalling marshal;
+    auto sizeAndAlign{
+        fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
+    unsigned short align{
+        std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
+    marshal.emplace_back(
+        fir::ReferenceType::get(ty),
+        AT{/*align=*/align, /*byval=*/!isResult, /*sret=*/isResult});
+    return marshal;
+  }
+
+  CodeGenSpecifics::Marshalling
+  structArgumentType(mlir::Location loc, fir::RecordType ty,
+                     const Marshalling &previousArguments) const override {
+    return structType(loc, ty, false);
+  }
+
+  CodeGenSpecifics::Marshalling
+  structReturnType(mlir::Location loc, fir::RecordType ty) const override {
+    return structType(loc, ty, true);
+  }
+};
+} // namespace
+
 // Instantiate the overloaded target instance based on the triple value.
 // TODO: Add other targets to this file as needed.
 std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(
@@ -1960,6 +2035,10 @@ std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(
     return std::make_unique<TargetLoongArch64>(ctx, std::move(trp),
                                                std::move(kindMap), targetCPU,
                                                targetFeatures, targetABI, dl);
+  case llvm::Triple::ArchType::systemz:
+    return std::make_unique<TargetSystemZ>(ctx, std::move(trp),
+                                           std::move(kindMap), targetCPU,
+                                           targetFeatures, targetABI, dl);
   }
   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
 }
diff --git a/flang/test/Driver/code-gen-systemz.f90 b/flang/test/Driver/code-gen-systemz.f90
new file mode 100644
index 0000000000000..8344fe9e7929b
--- /dev/null
+++ b/flang/test/Driver/code-gen-systemz.f90
@@ -0,0 +1,9 @@
+! Test -emit-obj for SystemZ (s390x)
+
+! REQUIRES: systemz-registered-target
+
+! RUN: %flang_fc1 -triple s390x-unknown-linux-gnu -emit-obj %s -o - | \
+! RUN: llvm-readobj -h - | FileCheck %s
+
+! CHECK: Arch: s390x
+end program
diff --git a/flang/test/Driver/emit-asm-systemz.f90 b/flang/test/Driver/emit-asm-systemz.f90
new file mode 100644
index 0000000000000..af0e250b54e85
--- /dev/null
+++ b/flang/test/Driver/emit-asm-systemz.f90
@@ -0,0 +1,11 @@
+! Test -S (SystemZ)
+
+! REQUIRES: systemz-registered-target
+
+! RUN: %flang_fc1 -S -triple s390x-unknown-linux-gnu %s -o - | FileCheck %s
+! RUN: %flang -S -target s390x-unknown-linux-gnu %s -o - | FileCheck %s
+
+! CHECK-LABEL: _QQmain:
+! CHECK: br %r14
+
+end program
diff --git a/flang/test/Driver/predefined-macros-systemz.f90 b/flang/test/Driver/predefined-macros-systemz.f90
new file mode 100644
index 0000000000000..4babe8bcba3d8
--- /dev/null
+++ b/flang/test/Driver/predefined-macros-systemz.f90
@@ -0,0 +1,23 @@
+! Test predefined macros for SystemZ architecture
+! REQUIRES: systemz-registered-target
+
+! RUN: %flang_fc1 -triple s390x-unknown-linux-gnu -cpp -E %s | FileCheck %s
+
+! CHECK: integer :: var1 = 1
+! CHECK: integer :: var2 = 1
+! CHECK: integer :: var3 = 1
+! CHECK: integer :: var4 = 1
+
+#if __s390__
+  integer :: var1 = __s390__
+#endif
+#if __s390x__
+  integer :: var2 = __s390x__
+#endif
+#if __s390x
+  integer :: var3 = __s390x
+#endif
+#if __zarch__
+  integer :: var4 = __zarch__
+#endif
+end program
diff --git a/flang/test/Fir/struct-passing-systemz-byval.fir b/flang/test/Fir/struct-passing-systemz-byval.fir
new file mode 100644
index 0000000000000..3fc35e4f16b60
--- /dev/null
+++ b/flang/test/Fir/struct-passing-systemz-byval.fir
@@ -0,0 +1,51 @@
+// Test SystemZ ABI rewrite of struct passed by value (BIND(C), VALUE derived types).
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s
+
+module attributes {
+  fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:128-v128:64-a:8:16-n32:64", llvm.target_triple = "s390x-unknown-linux-gnu" } {
+
+// CHECK-LABEL: func.func private @test_i32(!fir.ref<!fir.type<t1{i:i32}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.byval = !fir.type<t1{i:i32}>})
+func.func private @test_i32(%arg0: !fir.type<t1{i:i32}>)
+
+// CHECK-LABEL: func.func @test_call_i32(
+// CHECK-SAME:    %[[ARG0:.*]]: !fir.ref<!fir.type<t1{i:i32}>>) {
+// CHECK: %[[IN:.*]] = fir.load %[[ARG0]] : !fir.ref<!fir.type<t1{i:i32}>>
+// CHECK: %[[STCK:.*]] = llvm.intr.stacksave : !llvm.ptr
+// CHECK: %[[TMP:.*]] = fir.alloca !fir.type<t1{i:i32}>
+// CHECK: fir.store %[[IN]] to %[[TMP]] : !fir.ref<!fir.type<t1{i:i32}>>
+// CHECK: fir.call @test_i32(%[[TMP]]) : (!fir.ref<!fir.type<t1{i:i32}>> {llvm.align = 8 : i32, llvm.byval = !fir.type<t1{i:i32}>}) -> ()
+// CHECK: llvm.intr.stackrestore %[[STCK]] : !llvm.ptr
+// CHECK: return
+func.func @test_call_i32(%arg0: !fir.ref<!fir.type<t1{i:i32}>>) {
+  %in = fir.load %arg0 : !fir.ref<!fir.type<t1{i:i32}>>
+  fir.call @test_i32(%in) : (!fir.type<t1{i:i32}>) -> ()
+  return
+}
+
+// CHECK-LABEL: func.func private @test_i64(!fir.ref<!fir.type<t2{i:i64}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.byval = !fir.type<t2{i:i64}>})
+func.func private @test_i64(%arg0: !fir.type<t2{i:i64}>)
+
+// CHECK-LABEL: func.func private @test_f32_f32(!fir.ref<!fir.type<t3{r:f32,i:f32}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.byval = !fir.type<t3{r:f32,i:f32}>})
+func.func private @test_f32_f32(%arg0: !fir.type<t3{r:f32,i:f32}>)
+
+// CHECK-LABEL: func.func private @test_f64_f64(!fir.ref<!fir.type<t4{r:f64,i:f64}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.byval = !fir.type<t4{r:f64,i:f64}>})
+func.func private @test_f64_f64(%arg0: !fir.type<t4{r:f64,i:f64}>)
+
+// CHECK-LABEL: func.func private @test_large(!fir.ref<!fir.type<t5{i:!fir.array<8xi32>}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.byval = !fir.type<t5{i:!fir.array<8xi32>}>})
+func.func private @test_large(%arg0: !fir.type<t5{i:!fir.array<8xi32>}>)
+
+// CHECK-LABEL: func.func private @test_mixed(!fir.ref<!fir.type<t6{i:i32,r:f64}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.byval = !fir.type<t6{i:i32,r:f64}>})
+func.func private @test_mixed(%arg0: !fir.type<t6{i:i32,r:f64}>)
+
+// Struct with f128 field — alignment is 16, not clamped down to 8.
+// CHECK-LABEL: func.func private @test_f128(!fir.ref<!fir.type<t7{r:f128}>>
+// CHECK-SAME:    {llvm.align = 16 : i32, llvm.byval = !fir.type<t7{r:f128}>})
+func.func private @test_f128(%arg0: !fir.type<t7{r:f128}>)
+
+} // end module
diff --git a/flang/test/Fir/struct-return-systemz.fir b/flang/test/Fir/struct-return-systemz.fir
new file mode 100644
index 0000000000000..d8caac40fbdb3
--- /dev/null
+++ b/flang/test/Fir/struct-return-systemz.fir
@@ -0,0 +1,63 @@
+// Test SystemZ ABI rewrite of struct returned by value (BIND(C), VALUE derived types).
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s
+
+module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:128-v128:64-a:8:16-n32:64", llvm.target_triple = "s390x-unknown-linux-gnu"} {
+
+// CHECK-LABEL: func.func private @test_i32(!fir.ref<!fir.type<t1{i:i32}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.sret = !fir.type<t1{i:i32}>})
+func.func private @test_i32() -> !fir.type<t1{i:i32}>
+
+// CHECK-LABEL: func.func @test_call_i32(
+// CHECK-SAME:    %[[ARG0:.*]]: !fir.ref<!fir.type<t1{i:i32}>>)
+func.func @test_call_i32(%arg0 : !fir.ref<!fir.type<t1{i:i32}>>) {
+  // CHECK: %[[STCK:.*]] = llvm.intr.stacksave : !llvm.ptr
+  // CHECK: %[[ARG:.*]] = fir.alloca !fir.type<t1{i:i32}>
+  // CHECK: fir.call @test_i32(%[[ARG]]) : (!fir.ref<!fir.type<t1{i:i32}>> {llvm.align = 8 : i32, llvm.sret = !fir.type<t1{i:i32}>}) -> ()
+  // CHECK: %[[CVT:.*]] = fir.convert %[[ARG]] : (!fir.ref<!fir.type<t1{i:i32}>>) -> !fir.ref<!fir.type<t1{i:i32}>>
+  // CHECK: %[[LD:.*]] = fir.load %[[CVT]] : !fir.ref<!fir.type<t1{i:i32}>>
+  // CHECK: llvm.intr.stackrestore %[[STCK]] : !llvm.ptr
+  // CHECK: fir.store %[[LD]] to %[[ARG0]] : !fir.ref<!fir.type<t1{i:i32}>>
+  // CHECK: return
+  %out = fir.call @test_i32() : () -> !fir.type<t1{i:i32}>
+  fir.store %out to %arg0 : !fir.ref<!fir.type<t1{i:i32}>>
+  return
+}
+
+// CHECK-LABEL: func.func private @test_i64(!fir.ref<!fir.type<t2{i:i64}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.sret = !fir.type<t2{i:i64}>})
+func.func private @test_i64() -> !fir.type<t2{i:i64}>
+
+// CHECK-LABEL: func.func @test_call_i64(
+// CHECK-SAME:    %[[ARG0:.*]]: !fir.ref<!fir.type<t2{i:i64}>>)
+func.func @test_call_i64(%arg0 : !fir.ref<!fir.type<t2{i:i64}>>) {
+  // CHECK: %[[STCK:.*]] = llvm.intr.stacksave : !llvm.ptr
+  // CHECK: %[[ARG:.*]] = fir.alloca !fir.type<t2{i:i64}>
+  // CHECK: fir.call @test_i64(%[[ARG]]) : (!fir.ref<!fir.type<t2{i:i64}>> {llvm.align = 8 : i32, llvm.sret = !fir.type<t2{i:i64}>}) -> ()
+  // CHECK: %[[CVT:.*]] = fir.convert %[[ARG]] : (!fir.ref<!fir.type<t2{i:i64}>>) -> !fir.ref<!fir.type<t2{i:i64}>>
+  // CHECK: %[[LD:.*]] = fir.load %[[CVT]] : !fir.ref<!fir.type<t2{i:i64}>>
+  // CHECK: llvm.intr.stackrestore %[[STCK]] : !llvm.ptr
+  // CHECK: fir.store %[[LD]] to %[[ARG0]] : !fir.ref<!fir.type<t2{i:i64}>>
+  // CHECK: return
+  %out = fir.call @test_i64() : () -> !fir.type<t2{i:i64}>
+  fir.store %out to %arg0 : !fir.ref<!fir.type<t2{i:i64}>>
+  return
+}
+
+// CHECK-LABEL: func.func private @test_f32_f32(!fir.ref<!fir.type<t3{r:f32,i:f32}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.sret = !fir.type<t3{r:f32,i:f32}>})
+func.func private @test_f32_f32() -> !fir.type<t3{r:f32,i:f32}>
+
+// CHECK-LABEL: func.func private @test_f64_f64(!fir.ref<!fir.type<t4{r:f64,i:f64}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.sret = !fir.type<t4{r:f64,i:f64}>})
+func.func private @test_f64_f64() -> !fir.type<t4{r:f64,i:f64}>
+
+// CHECK-LABEL: func.func private @test_large(!fir.ref<!fir.type<t5{i:!fir.array<8xi32>}>>
+// CHECK-SAME:    {llvm.align = 8 : i32, llvm.sret = !fir.type<t5{i:!fir.array<8xi32>}>})
+func.func private @test_large() -> !fir.type<t5{i:!fir.array<8xi32>}>
+
+// Struct with f128 field — alignment is 16, not clamped down to 8.
+// CHECK-LABEL: func.func private @test_f128(!fir.ref<!fir.type<t7{r:f128}>>
+// CHECK-SAME:    {llvm.align = 16 : i32, llvm.sret = !fir.type<t7{r:f128}>})
+func.func private @test_f128() -> !fir.type<t7{r:f128}>
+
+} // end module
diff --git a/flang/test/Fir/target-rewrite-boxchar.fir b/flang/test/Fir/target-rewrite-boxchar.fir
index 681536cde6a05..228ee7a367b71 100644
--- a/flang/test/Fir/target-rewrite-boxchar.fir
+++ b/flang/test/Fir/target-rewrite-boxchar.fir
@@ -5,6 +5,7 @@
 // RUN: fir-opt --target-rewrite="target=amdgcn-amd-amdhsa" %s | FileCheck %s --check-prefix=INT64
 // RUN: fir-opt --target-rewrite="target=nvptx64-nvidia-cuda" %s | FileCheck %s --check-prefix=INT64
 // RUN: fir-opt --target-rewrite="target=loongarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=INT64
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s --check-prefix=INT64
 
 // Test that we rewrite the signatures and bodies of functions that take boxchar
 // parameters.
diff --git a/flang/test/Fir/target-rewrite-char-proc.fir b/flang/test/Fir/target-rewrite-char-proc.fir
index ccb1fc06f6f0d..62655cd081c53 100644
--- a/flang/test/Fir/target-rewrite-char-proc.fir
+++ b/flang/test/Fir/target-rewrite-char-proc.fir
@@ -2,6 +2,7 @@
 // arguments: one for the function address, and one for the length. The length
 // argument is added after other characters.
 // RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s
 
 // CHECK:  func private @takes_char_proc(() -> () {fir.char_proc}, i64)
 func.func private @takes_char_proc(tuple<() -> (), i64> {fir.char_proc})
diff --git a/flang/test/Fir/target-rewrite-complex.fir b/flang/test/Fir/target-rewrite-complex.fir
index c8cf867713632..9b0d0464d88d6 100644
--- a/flang/test/Fir/target-rewrite-complex.fir
+++ b/flang/test/Fir/target-rewrite-complex.fir
@@ -10,6 +10,7 @@
 // RUN: fir-opt --target-rewrite="target=powerpc64-ibm-aix7.2.0.0" %s | FileCheck %s --check-prefix=PPC64
 // RUN: fir-opt --target-rewrite="target=powerpc-ibm-aix7.2.0.0" %s | FileCheck %s --check-prefix=PPC
 // RUN: fir-opt --target-rewrite="target=loongarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=LOONGARCH64
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s --check-prefix=SYSTEMZ
 
 // Test that we rewrite the signature and body of a function that returns a
 // complex<4>.
@@ -24,6 +25,8 @@
 // PPC64-LABEL: func @returncomplex4() -> tuple<f32, f32>
 // PPC-LABEL: func @returncomplex4() -> tuple<f32, f32>
 // LOONGARCH64-LABEL: func @returncomplex4() -> tuple<f32, f32>
+// SYSTEMZ-LABEL: func @returncomplex4
+// SYSTEMZ-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>>  {llvm.align = 8 : i32, llvm.sret = tuple<f32, f32>})
 func.func @returncomplex4() -> complex<f32> {
   // I32: fir.insert_value
   // I32: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
@@ -47,6 +50,8 @@ func.func @returncomplex4() -> complex<f32> {
   // PPC: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
   // LOONGARCH64: fir.insert_value
   // LOONGARCH64: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
+  // SYSTEMZ: fir.insert_value
+  // SYSTEMZ: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value
   %1 = fir.undefined complex<f32>
   %2 = arith.constant 2.0 : f32
   %3 = fir.convert %2 : (f32) -> f32
@@ -105,6 +110,9 @@ func.func @returncomplex4() -> complex<f32> {
   // PPC: return [[RES]] : tuple<f32, f32>
   // LOONGARCH64: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
   // LOONGARCH64: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ADDRT]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ: [[ADDR:%[0-9A-Za-z]+]] = fir.convert [[ARG0]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ: fir.store [[VAL]] to [[ADDR]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: return
   return %6 : complex<f32>
 }
 
@@ -124,6 +132,8 @@ func.func @returncomplex4() -> complex<f32> {
 // PPC64-LABEL: func @returncomplex8() -> tuple<f64, f64>
 // PPC-LABEL: func @returncomplex8() -> tuple<f64, f64>
 // LOONGARCH64-LABEL: func @returncomplex8() -> tuple<f64, f64>
+// SYSTEMZ-LABEL: func @returncomplex8
+// SYSTEMZ-SAME: ([[ARG0:%[0-9A-Za-z]+]]: !fir.ref<tuple<f64, f64>>  {llvm.align = 8 : i32, llvm.sret = tuple<f64, f64>})
 func.func @returncomplex8() -> complex<f64> {
   // I32: fir.insert_value
   // I32: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
@@ -147,6 +157,8 @@ func.func @returncomplex8() -> complex<f64> {
   // PPC: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
   // LOONGARCH64: fir.insert_value
   // LOONGARCH64: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
+  // SYSTEMZ: fir.insert_value
+  // SYSTEMZ: [[VAL:%[0-9A-Za-z]+]] = fir.insert_value {{.*}}
   %1 = fir.undefined complex<f64>
   %2 = arith.constant 1.0 : f64
   %3 = arith.constant -4.0 : f64
@@ -204,6 +216,9 @@ func.func @returncomplex8() -> complex<f64> {
   // LOONGARCH64: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<complex<f64>>
   // LOONGARCH64: [[RES:%[0-9A-Za-z]+]] = fir.load [[ADDRT]] : !fir.ref<tuple<f64, f64>>
   // LOONGARCH64: return [[RES]] : tuple<f64, f64>
+  // SYSTEMZ: [[ADDRC:%[0-9A-Za-z]+]] = fir.convert [[ARG0]] : (!fir.ref<tuple<f64, f64>>) -> !fir.ref<complex<f64>>
+  // SYSTEMZ: fir.store [[VAL]] to [[ADDRC]] : !fir.ref<complex<f64>>
+  // SYSTEMZ: return
   return %5 : complex<f64>
 }
 
@@ -219,6 +234,7 @@ func.func @returncomplex8() -> complex<f64> {
 // PPC64-LABEL: func private @paramcomplex4(f32, f32)
 // PPC-LABEL: func private @paramcomplex4(f32, f32)
 // LOONGARCH64-LABEL: func private @paramcomplex4(f32, f32)
+// SYSTEMZ-LABEL: func private @paramcomplex4(!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>})
 func.func private @paramcomplex4(complex<f32>) -> ()
 
 // Test that we rewrite calls to functions that return or accept complex<4>.
@@ -233,6 +249,7 @@ func.func private @paramcomplex4(complex<f32>) -> ()
 // PPC64-LABEL: func @callcomplex4
 // PPC-LABEL: func @callcomplex4
 // LOONGARCH64-LABEL: func @callcomplex4
+// SYSTEMZ-LABEL: func @callcomplex4
 func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {
 
   // I32: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> i64
@@ -246,6 +263,9 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
   // PPC64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> tuple<f32, f32>
   // PPC: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> tuple<f32, f32>
   // LOONGARCH64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex4() : () -> tuple<f32, f32>
+  // SYSTEMZ: [[STCK:%[0-9A-Za-z]+]] = llvm.intr.stacksave : !llvm.ptr
+  // SYSTEMZ: [[RES:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
+  // SYSTEMZ: fir.call @returncomplex4([[RES]]) : (!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.sret = tuple<f32, f32>}) -> ()
   %1 = fir.call @returncomplex4() : () -> complex<f32>
 
   // I32: [[ADDRI64:%[0-9A-Za-z]+]] = fir.alloca i64
@@ -343,6 +363,13 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
   // LOONGARCH64: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : i32] : (complex<f32>) -> f32
   // LOONGARCH64: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : i32] : (complex<f32>) -> f32
   // LOONGARCH64: fir.call @paramcomplex4([[A]], [[B]]) : (f32, f32) -> ()
+  // SYSTEMZ: [[RESC:%[0-9A-Za-z]+]] = fir.convert [[RES]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ: [[C:%[0-9A-Za-z]+]] = fir.load [[RESC]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: llvm.intr.stackrestore [[STCK]] : !llvm.ptr
+  // SYSTEMZ: [[TMP:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ: fir.store [[C]] to [[TMP]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: [[T:%[0-9A-Za-z]+]] = fir.convert [[TMP]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ: fir.call @paramcomplex4([[T]]) : (!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}) -> ()
   fir.call @paramcomplex4(%1) : (complex<f32>) -> ()
 
   // I32: [[RES:%[0-9A-Za-z]+]] = fir.dispatch "ret_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) -> i64 {pass_arg_pos = 0 : i32}
@@ -354,6 +381,12 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
   // SPARCV9: [[RES:%[0-9A-Za-z]+]] = fir.dispatch "ret_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) -> tuple<f32, f32> {pass_arg_pos = 0 : i32}
   // PPC64: [[RES:%[0-9A-Za-z]+]] = fir.dispatch "ret_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) -> tuple<f32, f32> {pass_arg_pos = 0 : i32}
   // PPC: [[RES:%[0-9A-Za-z]+]] = fir.dispatch "ret_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) -> tuple<f32, f32> {pass_arg_pos = 0 : i32}
+  // SYSTEMZ: [[STCK2:%[0-9A-Za-z]+]] = llvm.intr.stacksave : !llvm.ptr
+  // SYSTEMZ: [[RES2:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
+  // SYSTEMZ: fir.dispatch "ret_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) ([[RES2]], %{{.*}} : !fir.ref<tuple<f32, f32>>, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 1 : i32}
+  // SYSTEMZ: [[RESC2:%[0-9A-Za-z]+]] = fir.convert [[RES2]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ: [[C2:%[0-9A-Za-z]+]] = fir.load [[RESC2]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: llvm.intr.stackrestore [[STCK2]] : !llvm.ptr
   %2 = fir.dispatch "ret_complex"(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) -> complex<f32> {pass_arg_pos = 0 : i32}
 
   // I32: [[ADDRI64:%[0-9A-Za-z]+]] = fir.alloca i64
@@ -427,6 +460,12 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
   // PPC64: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : i32] : (complex<f32>) -> f32
   // PPC64: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : i32] : (complex<f32>) -> f32
   // PPC64: fir.dispatch "with_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, [[A]], [[B]] : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>, f32, f32) {pass_arg_pos = 0 : i32}
+  // SYSTEMZ: [[STCK3:%[0-9A-Za-z]+]] = llvm.intr.stacksave : !llvm.ptr
+  // SYSTEMZ: [[TMP3:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ: fir.store [[C2]] to [[TMP3]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: [[T3:%[0-9A-Za-z]+]] = fir.convert [[TMP3]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ: fir.dispatch "with_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, [[T3]] : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>, !fir.ref<tuple<f32, f32>>) {pass_arg_pos = 0 : i32}
+  // SYSTEMZ: llvm.intr.stackrestore [[STCK3]] : !llvm.ptr
   fir.dispatch "with_complex"(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%arg0, %2 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>, complex<f32>) {pass_arg_pos = 0 : i32}
 
   // PPC: [[ADDRT:%[0-9A-Za-z]+]] = fir.alloca tuple<f32, f32>
@@ -436,6 +475,13 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
   // PPC: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : i32] : (complex<f32>) -> f32
   // PPC: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : i32] : (complex<f32>) -> f32
   // PPC: fir.dispatch "with_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, [[A]], [[B]] : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>, f32, f32) {pass_arg_pos = 0 : i32}
+  // SYSTEMZ: [[STCK4:%[0-9A-Za-z]+]] = llvm.intr.stacksave : !llvm.ptr
+  // SYSTEMZ: [[TMP4:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ: fir.store [[C2]] to [[TMP4]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: [[T4:%[0-9A-Za-z]+]] = fir.convert [[TMP4]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ: fir.dispatch "with_complex"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, [[T4]] : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>, !fir.ref<tuple<f32, f32>>) {pass_arg_pos = 0 : i32}
+  // SYSTEMZ: llvm.intr.stackrestore [[STCK4]] : !llvm.ptr
+
   fir.dispatch "with_complex"(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%arg0, %2 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>, complex<f32>) {pass_arg_pos = 0 : i32}
 
   // I32: fir.dispatch "with_complex2"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, %{{.*}} : !fir.ref<tuple<f32, f32>>, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 1 : i32}
@@ -449,6 +495,12 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
   // PPC64: fir.dispatch "with_complex2"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, %{{.*}}, %{{.*}} : f32, f32, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 2 : i32}
   // PPC: fir.dispatch "with_complex2"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, %{{.*}}, %{{.*}} : f32, f32, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 2 : i32}
   // LOONGARCH64: fir.dispatch "with_complex2"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%{{.*}}, %{{.*}}, %{{.*}} : f32, f32, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 2 : i32}
+  // SYSTEMZ: [[STCK5:%[0-9A-Za-z]+]] = llvm.intr.stacksave : !llvm.ptr
+  // SYSTEMZ: [[TMP5:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ: fir.store [[C2]] to [[TMP5]] : !fir.ref<complex<f32>>
+  // SYSTEMZ: [[T5:%[0-9A-Za-z]+]] = fir.convert [[TMP5]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ: fir.dispatch "with_complex2"(%{{.*}} : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) ([[T5]], %{{.*}} : !fir.ref<tuple<f32, f32>>, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 1 : i32}
+  // SYSTEMZ: llvm.intr.stackrestore [[STCK5]] : !llvm.ptr
   fir.dispatch "with_complex2"(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) (%2, %arg0 : complex<f32>, !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>>) {pass_arg_pos = 1 : i32}
 
   return
@@ -466,6 +518,7 @@ func.func @callcomplex4(%arg0 : !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i3
 // PPC64-LABEL: func private @paramcomplex8(f64, f64)
 // PPC-LABEL: func private @paramcomplex8(f64, f64)
 // LOONGARCH64-LABEL: func private @paramcomplex8(f64, f64)
+// SYSTEMZ-LABEL: func private @paramcomplex8(!fir.ref<tuple<f64, f64>> {llvm.align = 8 : i32, llvm.byval = tuple<f64, f64>})
 func.func private @paramcomplex8(complex<f64>) -> ()
 
 // Test that we rewrite calls to functions that return or accept complex<8>.
@@ -480,6 +533,7 @@ func.func private @paramcomplex8(complex<f64>) -> ()
 // PPC64-LABEL: func @callcomplex8()
 // PPC-LABEL: func @callcomplex8()
 // LOONGARCH64-LABEL: func @callcomplex8()
+// SYSTEMZ-LABEL: func @callcomplex8()
 func.func @callcomplex8() {
   // I32: [[RES:%[0-9A-Za-z]+]] = fir.alloca tuple<f64, f64>
   // I32: fir.call @returncomplex8([[RES]]) : (!fir.ref<tuple<f64, f64>> {llvm.align = 4 : i32, llvm.sret = tuple<f64, f64>}) -> ()
@@ -495,6 +549,9 @@ func.func @callcomplex8() {
   // PPC64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex8() : () -> tuple<f64, f64>
   // PPC: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex8() : () -> tuple<f64, f64>
   // LOONGARCH64: [[RES:%[0-9A-Za-z]+]] = fir.call @returncomplex8() : () -> tuple<f64, f64>
+  // SYSTEMZ: [[STCK:%[0-9A-Za-z]+]] = llvm.intr.stacksave : !llvm.ptr
+  // SYSTEMZ: [[RES:%[0-9A-Za-z]+]] = fir.alloca tuple<f64, f64>
+  // SYSTEMZ: fir.call @returncomplex8([[RES]]) : (!fir.ref<tuple<f64, f64>> {llvm.align = 8 : i32, llvm.sret = tuple<f64, f64>}) -> ()
   %1 = fir.call @returncomplex8() : () -> complex<f64>
 
   // I32: [[RESC:%[0-9A-Za-z]+]] = fir.convert [[RES]] : (!fir.ref<tuple<f64, f64>>) -> !fir.ref<complex<f64>>
@@ -583,6 +640,13 @@ func.func @callcomplex8() {
   // LOONGARCH64: [[A:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [0 : i32] : (complex<f64>) -> f64
   // LOONGARCH64: [[B:%[0-9A-Za-z]+]] = fir.extract_value [[V]], [1 : i32] : (complex<f64>) -> f64
   // LOONGARCH64: fir.call @paramcomplex8([[A]], [[B]]) : (f64, f64) -> ()
+  // SYSTEMZ: [[RESC:%[0-9A-Za-z]+]] = fir.convert [[RES]] : (!fir.ref<tuple<f64, f64>>) -> !fir.ref<complex<f64>>
+  // SYSTEMZ: [[C:%[0-9A-Za-z]+]] = fir.load [[RESC]] : !fir.ref<complex<f64>>
+  // SYSTEMZ: llvm.intr.stackrestore [[STCK]] : !llvm.ptr
+  // SYSTEMZ: [[TMP:%[0-9A-Za-z]+]] = fir.alloca complex<f64>
+  // SYSTEMZ: fir.store [[C]] to [[TMP]] : !fir.ref<complex<f64>>
+  // SYSTEMZ: [[T:%[0-9A-Za-z]+]] = fir.convert [[TMP]] : (!fir.ref<complex<f64>>) -> !fir.ref<tuple<f64, f64>>
+  // SYSTEMZ: fir.call @paramcomplex8([[T]]) : (!fir.ref<tuple<f64, f64>> {llvm.align = 8 : i32, llvm.byval = tuple<f64, f64>}) -> ()
   
   fir.call @paramcomplex8(%1) : (complex<f64>) -> ()
   return
@@ -600,6 +664,7 @@ func.func @callcomplex8() {
 // PPC64-LABEL: func private @calleemultipleparamscomplex4(f32, f32, f32, f32, f32, f32)
 // PPC-LABEL: func private @calleemultipleparamscomplex4(f32, f32, f32, f32, f32, f32)
 // LOONGARCH64-LABEL: func private @calleemultipleparamscomplex4(f32, f32, f32, f32, f32, f32)
+// SYSTEMZ-LABEL: func private @calleemultipleparamscomplex4(!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>})
 func.func private @calleemultipleparamscomplex4(complex<f32>, complex<f32>, complex<f32>) -> ()
 
 // I32-LABEL: func @multipleparamscomplex4
@@ -624,6 +689,8 @@ func.func private @calleemultipleparamscomplex4(complex<f32>, complex<f32>, comp
 // PPC-SAME: ([[A1:%[0-9A-Za-z]+]]: f32, [[B1:%[0-9A-Za-z]+]]: f32, [[A2:%[0-9A-Za-z]+]]: f32, [[B2:%[0-9A-Za-z]+]]: f32, [[A3:%[0-9A-Za-z]+]]: f32, [[B3:%[0-9A-Za-z]+]]: f32)
 // LOONGARCH64-LABEL: func @multipleparamscomplex4
 // LOONGARCH64-SAME: ([[A1:%[0-9A-Za-z]+]]: f32, [[B1:%[0-9A-Za-z]+]]: f32, [[A2:%[0-9A-Za-z]+]]: f32, [[B2:%[0-9A-Za-z]+]]: f32, [[A3:%[0-9A-Za-z]+]]: f32, [[B3:%[0-9A-Za-z]+]]: f32)
+// SYSTEMZ-LABEL: func @multipleparamscomplex4
+// SYSTEMZ-SAME: ([[Z1:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}, [[Z2:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}, [[Z3:%[0-9A-Za-z]+]]: !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>})
 func.func @multipleparamscomplex4(%z1 : complex<f32>, %z2 : complex<f32>, %z3 : complex<f32>) {
   // I32-DAG: [[Z1_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z1]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
   // I32-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDR]] : !fir.ref<complex<f32>>
@@ -860,6 +927,22 @@ func.func @multipleparamscomplex4(%z1 : complex<f32>, %z2 : complex<f32>, %z3 :
   // LOONGARCH64-DAG: [[B3_EXTR:%[0-9A-Za-z]+]] = fir.extract_value [[Z3]], [1 : i32] : (complex<f32>) -> f32
 
   // LOONGARCH64: fir.call @calleemultipleparamscomplex4([[A1_EXTR]], [[B1_EXTR]], [[A2_EXTR]], [[B2_EXTR]], [[A3_EXTR]], [[B3_EXTR]]) : (f32, f32, f32, f32, f32, f32) -> ()
+  // SYSTEMZ-DAG: [[Z1_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z1]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z1_VAL:%[0-9A-Za-z]+]] = fir.load [[Z1_ADDR]] : !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z2_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z2]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z2_VAL:%[0-9A-Za-z]+]] = fir.load [[Z2_ADDR]] : !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z3_ADDR:%[0-9A-Za-z]+]] = fir.convert [[Z3]] : (!fir.ref<tuple<f32, f32>>) -> !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z3_VAL:%[0-9A-Za-z]+]] = fir.load [[Z3_ADDR]] : !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z1_ADDRC:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ-DAG: fir.store [[Z1_VAL]] to [[Z1_ADDRC]] : !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z1_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z1_ADDRC]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ-DAG: [[Z2_ADDRC:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ-DAG: fir.store [[Z2_VAL]] to [[Z2_ADDRC]] : !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z2_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z2_ADDRC]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ-DAG: [[Z3_ADDRC:%[0-9A-Za-z]+]] = fir.alloca complex<f32>
+  // SYSTEMZ-DAG: fir.store [[Z3_VAL]] to [[Z3_ADDRC]] : !fir.ref<complex<f32>>
+  // SYSTEMZ-DAG: [[Z3_ADDRT:%[0-9A-Za-z]+]] = fir.convert [[Z3_ADDRC]] : (!fir.ref<complex<f32>>) -> !fir.ref<tuple<f32, f32>>
+  // SYSTEMZ: fir.call @calleemultipleparamscomplex4([[Z1_ADDRT]], [[Z2_ADDRT]], [[Z3_ADDRT]]) : (!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}) -> ()
   fir.call @calleemultipleparamscomplex4(%z1, %z2, %z3) : (complex<f32>, complex<f32>, complex<f32>) -> ()
   return
 }
diff --git a/flang/test/Fir/target-rewrite-complex16-systemz.fir b/flang/test/Fir/target-rewrite-complex16-systemz.fir
new file mode 100644
index 0000000000000..71703e543c610
--- /dev/null
+++ b/flang/test/Fir/target-rewrite-complex16-systemz.fir
@@ -0,0 +1,69 @@
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s
+// This test checks SystemZ target rewriting for complex(16) / complex<f128>.
+// An explicit `llvm.data_layout` is required to override the generic
+// MLIR fallback data layout (which defaults f128 to 16-byte alignment).
+// The SystemZ ABI requires an 8-byte alignment for complex(16) aggregates.
+module attributes {
+  llvm.data_layout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64",
+  llvm.target_triple = "s390x-unknown-linux-gnu" } {
+
+func.func @returncomplex16() -> complex<f128> {
+  %1 = fir.undefined complex<f128>
+  %2 = arith.constant 2.0 : f128
+  %3 = fir.convert %2 : (f128) -> f128
+  %c0 = arith.constant 0 : i32
+  %4 = fir.insert_value %1, %3, [0 : i32] : (complex<f128>, f128) -> complex<f128>
+  %c1 = arith.constant 1 : i32
+  %5 = arith.constant -42.0 : f128
+  %6 = fir.insert_value %4, %5, [1 : i32] : (complex<f128>, f128) -> complex<f128>
+  return %6 : complex<f128>
+}
+
+func.func private @paramcomplex16(complex<f128>) -> ()
+
+func.func @callcomplex16() {
+  %1 = fir.call @returncomplex16() : () -> complex<f128>
+  fir.call @paramcomplex16(%1) : (complex<f128>) -> ()
+  return
+}
+
+func.func private @calleemultipleparamscomplex16(complex<f128>, complex<f128>, complex<f128>) -> ()
+
+func.func @multipleparamscomplex16(%z1 : complex<f128>, %z2 : complex<f128>, %z3 : complex<f128>) {
+  fir.call @calleemultipleparamscomplex16(%z1, %z2, %z3) : (complex<f128>, complex<f128>, complex<f128>) -> ()
+  return
+}
+
+func.func private @mlircomplexf128(%z1: complex<f128>, %z2: complex<f128>) -> complex<f128> {
+  %0 = fir.call @mlircomplexf128(%z1, %z2) : (complex<f128>, complex<f128>) -> complex<f128>
+  return %0 : complex<f128>
+}
+
+func.func @addrof() {
+  %r = fir.address_of(@returncomplex16) : () -> complex<f128>
+  %p = fir.address_of(@paramcomplex16) : (complex<f128>) -> ()
+  return
+}
+
+} // module
+
+// CHECK-LABEL: func.func @returncomplex16(
+// CHECK-SAME:    %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.sret = tuple<f128, f128>}) {
+
+// CHECK: func.func private @paramcomplex16(!fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>})
+
+// CHECK-LABEL: func.func @callcomplex16() {
+// CHECK:         fir.call @returncomplex16(%{{.*}}) : (!fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.sret = tuple<f128, f128>}) -> ()
+// CHECK:         fir.call @paramcomplex16(%{{.*}}) : (!fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}) -> ()
+
+// CHECK: func.func private @calleemultipleparamscomplex16(!fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}, !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}, !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>})
+
+// CHECK-LABEL: func.func @multipleparamscomplex16(
+// CHECK-SAME:    %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}, %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}, %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}) {
+
+// CHECK-LABEL: func.func private @mlircomplexf128(
+// CHECK-SAME:    %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.sret = tuple<f128, f128>}, %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}, %{{.*}}: !fir.ref<tuple<f128, f128>> {llvm.align = 8 : i32, llvm.byval = tuple<f128, f128>}) {
+
+// CHECK-LABEL: func.func @addrof() {
+// CHECK:         fir.address_of(@returncomplex16) : (!fir.ref<tuple<f128, f128>>) -> ()
+// CHECK:         fir.address_of(@paramcomplex16) : (!fir.ref<tuple<f128, f128>>) -> ()
diff --git a/flang/test/Fir/target-rewrite-indirect-calls.fir b/flang/test/Fir/target-rewrite-indirect-calls.fir
index 58a9a260b9a98..ccf9bf0e0be59 100644
--- a/flang/test/Fir/target-rewrite-indirect-calls.fir
+++ b/flang/test/Fir/target-rewrite-indirect-calls.fir
@@ -1,5 +1,6 @@
 // Test that ABI attributes are set in indirect calls to BIND(C) functions.
 // RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s
 
 func.func @test(%arg0: () -> (), %arg1: !fir.ref<!fir.type<t{a:!fir.array<5xf64>}>>, %arg2: f64) {
   %0 = fir.load %arg1 : !fir.ref<!fir.type<t{a:!fir.array<5xf64>}>>
diff --git a/flang/test/Fir/target-rewrite-integer.fir b/flang/test/Fir/target-rewrite-integer.fir
index 4cb2026cf9ee0..66a3cce49a470 100644
--- a/flang/test/Fir/target-rewrite-integer.fir
+++ b/flang/test/Fir/target-rewrite-integer.fir
@@ -7,6 +7,7 @@
 // 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
 // RUN: fir-opt --split-input-file --target-rewrite="target=loongarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=LOONGARCH64,ALL
+// RUN: fir-opt --split-input-file --target-rewrite="target=s390x-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=SYSTEMZ,ALL
 
 // -----
 
@@ -23,6 +24,7 @@
 // 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})
+// SYSTEMZ: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
 func.func @_QPtest_i1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"}) {
   %c3_i32 = arith.constant 3 : i32
   %c-1_i32 = arith.constant -1 : i32
@@ -56,6 +58,7 @@ func.func private @_FortranAioEndIoStatement(!fir.ref<i8>) -> i32 attributes {fi
 // 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})
+// SYSTEMZ: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
 func.func @_QPtest_si1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"}) {
   %0 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
   %1 = fir.convert %0 : (!fir.logical<4>) -> si1
@@ -77,6 +80,7 @@ func.func private @_SomeFunc_si1(si1) -> si1 attributes {fir.runtime}
 // 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})
+// SYSTEMZ: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
 func.func @_QPtest_ui1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"}) {
   %0 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
   %1 = fir.convert %0 : (!fir.logical<4>) -> ui1
@@ -118,6 +122,8 @@ func.func private @_SomeFunc_ui1(ui1) -> ui1 attributes {fir.runtime}
 // 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"}
+// SYSTEMZ: func.func private @cfun8(i8 {llvm.signext}) -> (i8 {llvm.signext}) attributes {fir.bindc_name = "cfun8"}
+// SYSTEMZ: 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
@@ -129,3 +135,15 @@ func.func @_QPtest_bindc(%arg0: !fir.ref<i8> {fir.bindc_name = "x"}, %arg1: !fir
 }
 func.func private @cfun8(i8) -> i8 attributes {fir.bindc_name = "cfun8"}
 func.func private @cfun16(i16) -> i16 attributes {fir.bindc_name = "cfun16"}
+
+// -----
+
+// SYSTEMZ-LABEL: @_QPtest_bindc32
+// SYSTEMZ: func.func private @cfun32(i32 {llvm.signext}) -> (i32 {llvm.signext}) attributes {fir.bindc_name = "cfun32"}
+func.func @_QPtest_bindc32(%arg0: !fir.ref<i32> {fir.bindc_name = "x"}) {
+  %0 = fir.load %arg0 : !fir.ref<i32>
+  %1 = fir.call @cfun32(%0) fastmath<contract> : (i32) -> i32
+  fir.store %1 to %arg0 : !fir.ref<i32>
+  return
+}
+func.func private @cfun32(i32) -> i32 attributes {fir.bindc_name = "cfun32"}
diff --git a/flang/test/Fir/target-rewrite-selective.fir b/flang/test/Fir/target-rewrite-selective.fir
index eac1d35f8d24a..51f52de8c833b 100644
--- a/flang/test/Fir/target-rewrite-selective.fir
+++ b/flang/test/Fir/target-rewrite-selective.fir
@@ -1,5 +1,7 @@
 // RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu no-complex-conversion" %s | FileCheck --check-prefix=CMPLXOFF %s
 // RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu no-character-conversion" %s | FileCheck --check-prefix=CHAROFF %s
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu no-complex-conversion" %s | FileCheck --check-prefix=CMPLXOFF %s
+// RUN: fir-opt --target-rewrite="target=s390x-unknown-linux-gnu no-character-conversion" %s | FileCheck --check-prefix=SYSTEMZ_CHAROFF %s
 
 // Verify that selective TargetRewrite with no-complex-conversion and
 // no-character-conversion options works as expected.
@@ -31,6 +33,11 @@
 // CHAROFF-DAG: func.func private @_QPtest1(!fir.boxchar<1>, !fir.vector<2:f32>) -> !fir.vector<2:f32>
 // CHAROFF-DAG: func.func private @_QPtest2(!fir.ref<!fir.char<1,10>>, index, !fir.vector<2:f32>) -> !fir.boxchar<1>
 
+// SYSTEMZ_CHAROFF-DAG: fir.call @_QPtest1({{.*}}, {{.*}}, {{.*}}) fastmath<contract> : (!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.sret = tuple<f32, f32>}, !fir.boxchar<1>, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}) -> ()
+// SYSTEMZ_CHAROFF-DAG: fir.call @_QPtest2({{.*}}, {{.*}}, {{.*}}) fastmath<contract> : (!fir.ref<!fir.char<1,10>>, index, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}) -> !fir.boxchar<1>
+// SYSTEMZ_CHAROFF-DAG: func.func private @_QPtest1(!fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.sret = tuple<f32, f32>}, !fir.boxchar<1>, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>})
+// SYSTEMZ_CHAROFF-DAG: func.func private @_QPtest2(!fir.ref<!fir.char<1,10>>, index, !fir.ref<tuple<f32, f32>> {llvm.align = 8 : i32, llvm.byval = tuple<f32, f32>}) -> !fir.boxchar<1>
+
 module {
   func.func @_QPtest(%arg0: complex<f32> {fir.bindc_name = "value"}) -> complex<f32> {
     %c10 = arith.constant 10 : index
diff --git a/flang/test/Lower/OpenMP/simd_systemz.f90 b/flang/test/Lower/OpenMP/simd_systemz.f90
new file mode 100644
index 0000000000000..a0c1e6f22f08f
--- /dev/null
+++ b/flang/test/Lower/OpenMP/simd_systemz.f90
@@ -0,0 +1,16 @@
+! Tests for 2.9.3.1 Simd and target dependent default alignment for SystemZ
+! The default alignment for SystemZ is 0 so we do not emit aligned clause
+! REQUIRES: systemz-registered-target
+! RUN: %flang_fc1 -triple s390x-unknown-linux-gnu -emit-hlfir -fopenmp %s -o - | FileCheck %s
+subroutine simdloop_aligned_cptr(A)
+    use iso_c_binding
+    integer :: i
+    type (c_ptr) :: A
+  !CHECK: omp.simd
+  !CHECK-NOT: aligned(
+    !$OMP SIMD ALIGNED(A)
+    do i = 1, 10
+      call c_test_call(A)
+    end do
+    !$OMP END SIMD
+end subroutine
diff --git a/flang/test/Semantics/realkinds-systemz-01.f90 b/flang/test/Semantics/realkinds-systemz-01.f90
new file mode 100644
index 0000000000000..20d7ee707349c
--- /dev/null
+++ b/flang/test/Semantics/realkinds-systemz-01.f90
@@ -0,0 +1,12 @@
+! REQUIRES: systemz-registered-target
+! REQUIRES: flang-supports-f128-math
+! RUN: %python %S/test_modfile.py %s %flang_fc1 -triple s390x-unknown-linux-gnu
+
+module m1
+  logical, parameter :: realpcheck = 16 == selected_real_kind(16)
+end module m1
+!Expect: m1.mod
+!module m1
+!logical(4),parameter::realpcheck=.true._4
+!intrinsic::selected_real_kind
+!end



More information about the flang-commits mailing list