[clang] [llvm] target ABI: improve call parameters extensions handling (PR #100757)

Jonas Paulsson via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 19 05:56:11 PDT 2024


https://github.com/JonPsson1 updated https://github.com/llvm/llvm-project/pull/100757

>From 1e7544f684579ed2016f4037a611a5939adabee9 Mon Sep 17 00:00:00 2001
From: Jonas Paulsson <paulsson at linux.vnet.ibm.com>
Date: Mon, 11 Oct 2021 19:08:42 +0200
Subject: [PATCH 1/3] VerifyIntegerArgs() i8 test. LangRef text. tests IP Tests
 passing, mostly with -no-arg-exts NoExtend IP NoExt attribute instead tests
 at least passing ZeroExt flag instead of NoExt flag Updated was aab1ee8 IP
 Verify by default Fix in NoExt handling. SystemZ changes for ver was 02868e6

Rebase
Try TargetOption
was 4b7020d
---
 clang/include/clang/CodeGen/CGFunctionInfo.h  | 33 ++++++++++++++++---
 clang/lib/CodeGen/CGCall.cpp                  | 10 ++++--
 clang/lib/CodeGen/Targets/SystemZ.cpp         | 16 ++++-----
 .../test/CodeGen/SystemZ/systemz-abi-vector.c | 10 +++---
 clang/test/CodeGen/SystemZ/systemz-abi.c      | 12 +++----
 clang/test/CodeGen/SystemZ/systemz-abi.cpp    | 10 +++---
 llvm/docs/LangRef.rst                         | 19 +++++++----
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |  1 +
 llvm/include/llvm/CodeGen/TargetCallingConv.h |  8 +++--
 llvm/include/llvm/CodeGen/TargetLowering.h    | 10 +++---
 llvm/include/llvm/IR/Attributes.td            |  3 ++
 llvm/include/llvm/Target/TargetOptions.h      |  7 ++++
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |  2 ++
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |  2 ++
 .../SelectionDAG/SelectionDAGBuilder.cpp      |  4 +++
 .../CodeGen/SelectionDAG/TargetLowering.cpp   |  1 +
 .../Target/SystemZ/SystemZISelLowering.cpp    | 31 +++++++++++++++++
 llvm/lib/Target/SystemZ/SystemZISelLowering.h |  3 ++
 llvm/lib/Transforms/Utils/CodeExtractor.cpp   |  1 +
 llvm/tools/llc/llc.cpp                        |  4 +++
 20 files changed, 144 insertions(+), 43 deletions(-)

diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h
index 811f33407368c6..d19f84d198876f 100644
--- a/clang/include/clang/CodeGen/CGFunctionInfo.h
+++ b/clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -116,6 +116,7 @@ class ABIArgInfo {
   bool InReg : 1;           // isDirect() || isExtend() || isIndirect()
   bool CanBeFlattened: 1;   // isDirect()
   bool SignExt : 1;         // isExtend()
+  bool ZeroExt : 1;         // isExtend()
 
   bool canHavePaddingType() const {
     return isDirect() || isExtend() || isIndirect() || isIndirectAliased() ||
@@ -137,7 +138,7 @@ class ABIArgInfo {
         PaddingInReg(false), InAllocaSRet(false),
         InAllocaIndirect(false), IndirectByVal(false), IndirectRealign(false),
         SRetAfterThis(false), InReg(false), CanBeFlattened(false),
-        SignExt(false) {}
+        SignExt(false), ZeroExt(false) {}
 
   static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0,
                               llvm::Type *Padding = nullptr,
@@ -174,12 +175,12 @@ class ABIArgInfo {
     AI.setPaddingType(nullptr);
     AI.setDirectOffset(0);
     AI.setDirectAlign(0);
-    AI.setSignExt(false);
+    AI.setZeroExt(true);
     return AI;
   }
 
   // ABIArgInfo will record the argument as being extended based on the sign
-  // of its type.
+  // of its type. Produces a sign or zero extension.
   static ABIArgInfo getExtend(QualType Ty, llvm::Type *T = nullptr) {
     assert(Ty->isIntegralOrEnumerationType() && "Unexpected QualType");
     if (Ty->hasSignedIntegerRepresentation())
@@ -187,6 +188,16 @@ class ABIArgInfo {
     return getZeroExtend(Ty, T);
   }
 
+  // Struct in register marked explicitly as not needing extension.
+  static ABIArgInfo getNoExtend(llvm::IntegerType *T) {
+    auto AI = ABIArgInfo(Extend);
+    AI.setCoerceToType(T);
+    AI.setPaddingType(nullptr);
+    AI.setDirectOffset(0);
+    AI.setDirectAlign(0);
+    return AI;
+  }
+
   static ABIArgInfo getExtendInReg(QualType Ty, llvm::Type *T = nullptr) {
     auto AI = getExtend(Ty, T);
     AI.setInReg(true);
@@ -326,7 +337,7 @@ class ABIArgInfo {
   }
 
   bool isSignExt() const {
-    assert(isExtend() && "Invalid kind!");
+    assert(isExtend() && (SignExt + ZeroExt <= 1) && "Invalid kind / flags!");
     return SignExt;
   }
   void setSignExt(bool SExt) {
@@ -334,6 +345,20 @@ class ABIArgInfo {
     SignExt = SExt;
   }
 
+  bool isZeroExt() const {
+    assert(isExtend() && (SignExt + ZeroExt <= 1) && "Invalid kind / flags!");
+    return ZeroExt;
+  }
+  void setZeroExt(bool ZExt) {
+    assert(isExtend() && "Invalid kind!");
+    ZeroExt = ZExt;
+  }
+
+  bool isNoExt() const {
+    assert(isExtend() && (SignExt + ZeroExt <= 1) && "Invalid kind / flags!");
+    return !SignExt && !ZeroExt;
+  }
+
   llvm::Type *getPaddingType() const {
     return (canHavePaddingType() ? PaddingType : nullptr);
   }
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 997510d71c3efb..4ae981e4013e9c 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2182,7 +2182,7 @@ static bool DetermineNoUndef(QualType QTy, CodeGenTypes &Types,
   if (AI.getKind() == ABIArgInfo::Indirect ||
       AI.getKind() == ABIArgInfo::IndirectAliased)
     return true;
-  if (AI.getKind() == ABIArgInfo::Extend)
+  if (AI.getKind() == ABIArgInfo::Extend && !AI.isNoExt())
     return true;
   if (!DL.typeSizeEqualsStoreSize(Ty))
     // TODO: This will result in a modest amount of values not marked noundef
@@ -2567,8 +2567,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
   case ABIArgInfo::Extend:
     if (RetAI.isSignExt())
       RetAttrs.addAttribute(llvm::Attribute::SExt);
-    else
+    else if (RetAI.isZeroExt())
       RetAttrs.addAttribute(llvm::Attribute::ZExt);
+    else
+      RetAttrs.addAttribute(llvm::Attribute::NoExt);
     [[fallthrough]];
   case ABIArgInfo::Direct:
     if (RetAI.getInReg())
@@ -2708,8 +2710,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
     case ABIArgInfo::Extend:
       if (AI.isSignExt())
         Attrs.addAttribute(llvm::Attribute::SExt);
-      else
+      else if (AI.isZeroExt())
         Attrs.addAttribute(llvm::Attribute::ZExt);
+      else
+        Attrs.addAttribute(llvm::Attribute::NoExt);
       [[fallthrough]];
     case ABIArgInfo::Direct:
       if (ArgNo == 0 && FI.isChainCall())
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 4d61f513793463..56129622f48dbd 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -445,16 +445,16 @@ ABIArgInfo SystemZABIInfo::classifyArgumentType(QualType Ty) const {
       return getNaturalAlignIndirect(Ty, /*ByVal=*/false);
 
     // The structure is passed as an unextended integer, a float, or a double.
-    llvm::Type *PassTy;
     if (isFPArgumentType(SingleElementTy)) {
       assert(Size == 32 || Size == 64);
-      if (Size == 32)
-        PassTy = llvm::Type::getFloatTy(getVMContext());
-      else
-        PassTy = llvm::Type::getDoubleTy(getVMContext());
-    } else
-      PassTy = llvm::IntegerType::get(getVMContext(), Size);
-    return ABIArgInfo::getDirect(PassTy);
+      return ABIArgInfo::getDirect(
+          Size == 32 ? llvm::Type::getFloatTy(getVMContext())
+                     : llvm::Type::getDoubleTy(getVMContext()));
+    } else {
+      llvm::IntegerType *PassTy = llvm::IntegerType::get(getVMContext(), Size);
+      return Size <= 32 ? ABIArgInfo::getNoExtend(PassTy)
+                        : ABIArgInfo::getDirect(PassTy);
+    }
   }
 
   // Non-structure compounds are passed indirectly.
diff --git a/clang/test/CodeGen/SystemZ/systemz-abi-vector.c b/clang/test/CodeGen/SystemZ/systemz-abi-vector.c
index 23f4996723f826..8361ccef21022d 100644
--- a/clang/test/CodeGen/SystemZ/systemz-abi-vector.c
+++ b/clang/test/CodeGen/SystemZ/systemz-abi-vector.c
@@ -146,17 +146,17 @@ v1f128 pass_v1f128(v1f128 arg) { return arg; }
 
 struct agg_v1i8 { v1i8 a; };
 struct agg_v1i8 pass_agg_v1i8(struct agg_v1i8 arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_v1i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v1i8) align 1 %{{.*}}, i8 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_v1i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v1i8) align 1 %{{.*}}, i8 noext %{{.*}})
 // CHECK-VECTOR-LABEL: define{{.*}} void @pass_agg_v1i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v1i8) align 1 %{{.*}}, <1 x i8> %{{.*}})
 
 struct agg_v2i8 { v2i8 a; };
 struct agg_v2i8 pass_agg_v2i8(struct agg_v2i8 arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_v2i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v2i8) align 2 %{{.*}}, i16 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_v2i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v2i8) align 2 %{{.*}}, i16 noext %{{.*}})
 // CHECK-VECTOR-LABEL: define{{.*}} void @pass_agg_v2i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v2i8) align 2 %{{.*}}, <2 x i8> %{{.*}})
 
 struct agg_v4i8 { v4i8 a; };
 struct agg_v4i8 pass_agg_v4i8(struct agg_v4i8 arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_v4i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v4i8) align 4 %{{.*}}, i32 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_v4i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v4i8) align 4 %{{.*}}, i32 noext %{{.*}})
 // CHECK-VECTOR-LABEL: define{{.*}} void @pass_agg_v4i8(ptr dead_on_unwind noalias writable sret(%struct.agg_v4i8) align 4 %{{.*}}, <4 x i8> %{{.*}})
 
 struct agg_v8i8 { v8i8 a; };
@@ -189,8 +189,8 @@ struct agg_novector2 pass_agg_novector2(struct agg_novector2 arg) { return arg;
 
 struct agg_novector3 { v4i8 a; int : 0; };
 struct agg_novector3 pass_agg_novector3(struct agg_novector3 arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_novector3(ptr dead_on_unwind noalias writable sret(%struct.agg_novector3) align 4 %{{.*}}, i32 %{{.*}})
-// CHECK-VECTOR-LABEL: define{{.*}} void @pass_agg_novector3(ptr dead_on_unwind noalias writable sret(%struct.agg_novector3) align 4 %{{.*}}, i32 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_novector3(ptr dead_on_unwind noalias writable sret(%struct.agg_novector3) align 4 %{{.*}}, i32 noext %{{.*}})
+// CHECK-VECTOR-LABEL: define{{.*}} void @pass_agg_novector3(ptr dead_on_unwind noalias writable sret(%struct.agg_novector3) align 4 %{{.*}}, i32 noext %{{.*}})
 
 struct agg_novector4 { v4i8 a __attribute__((aligned (8))); };
 struct agg_novector4 pass_agg_novector4(struct agg_novector4 arg) { return arg; }
diff --git a/clang/test/CodeGen/SystemZ/systemz-abi.c b/clang/test/CodeGen/SystemZ/systemz-abi.c
index 3526772008d382..fd2b5d450cc643 100644
--- a/clang/test/CodeGen/SystemZ/systemz-abi.c
+++ b/clang/test/CodeGen/SystemZ/systemz-abi.c
@@ -86,11 +86,11 @@ _Complex long double pass_complex_longdouble(_Complex long double arg) { return
 
 struct agg_1byte { char a[1]; };
 struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_1byte(ptr dead_on_unwind noalias writable sret(%struct.agg_1byte) align 1 %{{.*}}, i8 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_1byte(ptr dead_on_unwind noalias writable sret(%struct.agg_1byte) align 1 %{{.*}}, i8 noext %{{.*}})
 
 struct agg_2byte { char a[2]; };
 struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_2byte(ptr dead_on_unwind noalias writable sret(%struct.agg_2byte) align 1 %{{.*}}, i16 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_2byte(ptr dead_on_unwind noalias writable sret(%struct.agg_2byte) align 1 %{{.*}}, i16 noext %{{.*}})
 
 struct agg_3byte { char a[3]; };
 struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; }
@@ -98,7 +98,7 @@ struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; }
 
 struct agg_4byte { char a[4]; };
 struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_4byte(ptr dead_on_unwind noalias writable sret(%struct.agg_4byte) align 1 %{{.*}}, i32 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_4byte(ptr dead_on_unwind noalias writable sret(%struct.agg_4byte) align 1 %{{.*}}, i32 noext %{{.*}})
 
 struct agg_5byte { char a[5]; };
 struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; }
@@ -126,7 +126,7 @@ struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; }
 struct agg_float { float a; };
 struct agg_float pass_agg_float(struct agg_float arg) { return arg; }
 // HARD-FLOAT-LABEL: define{{.*}} void @pass_agg_float(ptr dead_on_unwind noalias writable sret(%struct.agg_float) align 4 %{{.*}}, float %{{.*}})
-// SOFT-FLOAT-LABEL: define{{.*}} void @pass_agg_float(ptr dead_on_unwind noalias writable sret(%struct.agg_float) align 4 %{{.*}}, i32 %{{.*}})
+// SOFT-FLOAT-LABEL: define{{.*}} void @pass_agg_float(ptr dead_on_unwind noalias writable sret(%struct.agg_float) align 4 %{{.*}}, i32 noext %{{.*}})
 
 struct agg_double { double a; };
 struct agg_double pass_agg_double(struct agg_double arg) { return arg; }
@@ -159,14 +159,14 @@ struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; }
 
 struct agg_nofloat3 { float a; int : 0; };
 struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_agg_nofloat3(ptr dead_on_unwind noalias writable sret(%struct.agg_nofloat3) align 4 %{{.*}}, i32 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_agg_nofloat3(ptr dead_on_unwind noalias writable sret(%struct.agg_nofloat3) align 4 %{{.*}}, i32 noext %{{.*}})
 
 
 // Union types likewise are *not* float-like aggregate types
 
 union union_float { float a; };
 union union_float pass_union_float(union union_float arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @pass_union_float(ptr dead_on_unwind noalias writable sret(%union.union_float) align 4 %{{.*}}, i32 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @pass_union_float(ptr dead_on_unwind noalias writable sret(%union.union_float) align 4 %{{.*}}, i32 noext %{{.*}})
 
 union union_double { double a; };
 union union_double pass_union_double(union union_double arg) { return arg; }
diff --git a/clang/test/CodeGen/SystemZ/systemz-abi.cpp b/clang/test/CodeGen/SystemZ/systemz-abi.cpp
index 06be85421ba17a..b13aedfca464b7 100644
--- a/clang/test/CodeGen/SystemZ/systemz-abi.cpp
+++ b/clang/test/CodeGen/SystemZ/systemz-abi.cpp
@@ -7,7 +7,7 @@
 class agg_float_class { float a; };
 class agg_float_class pass_agg_float_class(class agg_float_class arg) { return arg; }
 // CHECK-LABEL: define{{.*}} void @_Z20pass_agg_float_class15agg_float_class(ptr dead_on_unwind noalias writable sret(%class.agg_float_class) align 4 %{{.*}}, float %{{.*}})
-// SOFT-FLOAT-LABEL: define{{.*}} void @_Z20pass_agg_float_class15agg_float_class(ptr dead_on_unwind noalias writable sret(%class.agg_float_class) align 4 %{{.*}}, i32 %{{.*}})
+// SOFT-FLOAT-LABEL: define{{.*}} void @_Z20pass_agg_float_class15agg_float_class(ptr dead_on_unwind noalias writable sret(%class.agg_float_class) align 4 %{{.*}}, i32 noext %{{.*}})
 
 class agg_double_class { double a; };
 class agg_double_class pass_agg_double_class(class agg_double_class arg) { return arg; }
@@ -18,8 +18,8 @@ class agg_double_class pass_agg_double_class(class agg_double_class arg) { retur
 // This structure is passed in a GPR in C++ (and C, checked in systemz-abi.c).
 struct agg_float_cpp { float a; int : 0; };
 struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return arg; }
-// CHECK-LABEL: define{{.*}} void @_Z18pass_agg_float_cpp13agg_float_cpp(ptr dead_on_unwind noalias writable sret(%struct.agg_float_cpp) align 4 %{{.*}}, i32 %{{.*}})
-// SOFT-FLOAT-LABEL:  define{{.*}} void @_Z18pass_agg_float_cpp13agg_float_cpp(ptr dead_on_unwind noalias writable sret(%struct.agg_float_cpp) align 4 %{{.*}}, i32 %{{.*}})
+// CHECK-LABEL: define{{.*}} void @_Z18pass_agg_float_cpp13agg_float_cpp(ptr dead_on_unwind noalias writable sret(%struct.agg_float_cpp) align 4 %{{.*}}, i32 noext %{{.*}})
+// SOFT-FLOAT-LABEL:  define{{.*}} void @_Z18pass_agg_float_cpp13agg_float_cpp(ptr dead_on_unwind noalias writable sret(%struct.agg_float_cpp) align 4 %{{.*}}, i32 noext %{{.*}})
 
 
 // A field member of empty class type in C++ makes the record nonhomogeneous,
@@ -32,7 +32,7 @@ struct agg_nofloat_empty pass_agg_nofloat_empty(struct agg_nofloat_empty arg) {
 struct agg_float_empty { float a; [[no_unique_address]] empty dummy; };
 struct agg_float_empty pass_agg_float_empty(struct agg_float_empty arg) { return arg; }
 // CHECK-LABEL: define{{.*}} void @_Z20pass_agg_float_empty15agg_float_empty(ptr dead_on_unwind noalias writable sret(%struct.agg_float_empty) align 4 %{{.*}}, float %{{.*}})
-// SOFT-FLOAT-LABEL:  define{{.*}} void @_Z20pass_agg_float_empty15agg_float_empty(ptr dead_on_unwind noalias writable sret(%struct.agg_float_empty) align 4 %{{.*}}, i32 %{{.*}})
+// SOFT-FLOAT-LABEL:  define{{.*}} void @_Z20pass_agg_float_empty15agg_float_empty(ptr dead_on_unwind noalias writable sret(%struct.agg_float_empty) align 4 %{{.*}}, i32 noext %{{.*}})
 struct agg_nofloat_emptyarray { float a; [[no_unique_address]] empty dummy[3]; };
 struct agg_nofloat_emptyarray pass_agg_nofloat_emptyarray(struct agg_nofloat_emptyarray arg) { return arg; }
 // CHECK-LABEL: define{{.*}} void @_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(ptr dead_on_unwind noalias writable sret(%struct.agg_nofloat_emptyarray) align 4 %{{.*}}, i64 %{{.*}})
@@ -48,7 +48,7 @@ struct emptybase { [[no_unique_address]] empty dummy; };
 struct agg_float_emptybase : emptybase { float a; };
 struct agg_float_emptybase pass_agg_float_emptybase(struct agg_float_emptybase arg) { return arg; }
 // CHECK-LABEL: define{{.*}} void @_Z24pass_agg_float_emptybase19agg_float_emptybase(ptr dead_on_unwind noalias writable sret(%struct.agg_float_emptybase) align 4 %{{.*}}, float %{{.*}})
-// SOFT-FLOAT-LABEL:  define{{.*}} void @_Z24pass_agg_float_emptybase19agg_float_emptybase(ptr dead_on_unwind noalias writable sret(%struct.agg_float_emptybase) align 4 %{{.*}}, i32 %{{.*}})
+// SOFT-FLOAT-LABEL:  define{{.*}} void @_Z24pass_agg_float_emptybase19agg_float_emptybase(ptr dead_on_unwind noalias writable sret(%struct.agg_float_emptybase) align 4 %{{.*}}, i32 noext %{{.*}})
 struct noemptybasearray { [[no_unique_address]] empty dummy[3]; };
 struct agg_nofloat_emptybasearray : noemptybasearray { float a; };
 struct agg_nofloat_emptybasearray pass_agg_nofloat_emptybasearray(struct agg_nofloat_emptybasearray arg) { return arg; }
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index abeafb7616201a..5356748469bcd8 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1174,6 +1174,10 @@ For example:
 Note that any attributes for the function result (``nonnull``,
 ``signext``) come before the result type.
 
+If an integer argument to a function is not marked signext/zeroext/noext, the
+kind of extension used is target-specific. Some targets depend for
+correctness on the kind of extension to be explicitly specified.
+
 Currently, only the following parameter attributes are defined:
 
 ``zeroext``
@@ -1185,6 +1189,9 @@ Currently, only the following parameter attributes are defined:
     value should be sign-extended to the extent required by the target's
     ABI (which is usually 32-bits) by the caller (for a parameter) or
     the callee (for a return value).
+``noext`` This indicates to the code generator that the parameter or return
+    value has the high bits undefined, as for a struct in register, and
+    therefore does not need to be sign or zero extended.
 ``inreg``
     This indicates that this parameter or return value should be treated
     in a special target-dependent fashion while emitting code for
@@ -9113,8 +9120,8 @@ This instruction requires several arguments:
    convention <callingconv>` the call should use. If none is
    specified, the call defaults to using C calling conventions.
 #. The optional :ref:`Parameter Attributes <paramattrs>` list for return
-   values. Only '``zeroext``', '``signext``', and '``inreg``' attributes
-   are valid here.
+   values. Only '``zeroext``', '``signext``', '``noext``', and '``inreg``'
+   attributes are valid here.
 #. The optional addrspace attribute can be used to indicate the address space
    of the called function. If it is not specified, the program address space
    from the :ref:`datalayout string<langref_datalayout>` will be used.
@@ -9209,8 +9216,8 @@ This instruction requires several arguments:
    convention <callingconv>` the call should use. If none is
    specified, the call defaults to using C calling conventions.
 #. The optional :ref:`Parameter Attributes <paramattrs>` list for return
-   values. Only '``zeroext``', '``signext``', and '``inreg``' attributes
-   are valid here.
+   values. Only '``zeroext``', '``signext``', '``noext``', and '``inreg``'
+   attributes are valid here.
 #. The optional addrspace attribute can be used to indicate the address space
    of the called function. If it is not specified, the program address space
    from the :ref:`datalayout string<langref_datalayout>` will be used.
@@ -12699,8 +12706,8 @@ This instruction requires several arguments:
    calling convention of the call must match the calling convention of
    the target function, or else the behavior is undefined.
 #. The optional :ref:`Parameter Attributes <paramattrs>` list for return
-   values. Only '``zeroext``', '``signext``', and '``inreg``' attributes
-   are valid here.
+   values. Only '``zeroext``', '``signext``', '``noext``', and '``inreg``'
+   attributes are valid here.
 #. The optional addrspace attribute can be used to indicate the address space
    of the called function. If it is not specified, the program address space
    from the :ref:`datalayout string<langref_datalayout>` will be used.
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 05ed148148d7cb..96668435e8d7bc 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -763,6 +763,7 @@ enum AttributeKindCodes {
   ATTR_KIND_SANITIZE_REALTIME = 96,
   ATTR_KIND_NO_SANITIZE_REALTIME = 97,
   ATTR_KIND_CORO_ELIDE_SAFE = 98,
+  ATTR_KIND_NO_EXT = 99,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/CodeGen/TargetCallingConv.h b/llvm/include/llvm/CodeGen/TargetCallingConv.h
index cb0055633f4f33..a28c7a99fb3b5a 100644
--- a/llvm/include/llvm/CodeGen/TargetCallingConv.h
+++ b/llvm/include/llvm/CodeGen/TargetCallingConv.h
@@ -28,6 +28,7 @@ namespace ISD {
   private:
     unsigned IsZExt : 1;     ///< Zero extended
     unsigned IsSExt : 1;     ///< Sign extended
+    unsigned IsNoExt : 1;    ///< No extension
     unsigned IsInReg : 1;    ///< Passed in register
     unsigned IsSRet : 1;     ///< Hidden struct-ret ptr
     unsigned IsByVal : 1;    ///< Struct passed by value
@@ -60,8 +61,8 @@ namespace ISD {
 
   public:
     ArgFlagsTy()
-        : IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsByRef(0),
-          IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0),
+        : IsZExt(0), IsSExt(0), IsNoExt(0), IsInReg(0), IsSRet(0), IsByVal(0),
+          IsByRef(0), IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0),
           IsPreallocated(0), IsSplitEnd(0), IsSwiftSelf(0), IsSwiftAsync(0),
           IsSwiftError(0), IsCFGuardTarget(0), IsHva(0), IsHvaStart(0),
           IsSecArgPass(0), MemAlign(0), OrigAlign(0),
@@ -76,6 +77,9 @@ namespace ISD {
     bool isSExt() const { return IsSExt; }
     void setSExt() { IsSExt = 1; }
 
+    bool isNoExt() const { return IsNoExt; }
+    void setNoExt() { IsNoExt = 1; }
+
     bool isInReg() const { return IsInReg; }
     void setInReg() { IsInReg = 1; }
 
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 5888eaa6fbdb52..3842af56e6b3d7 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -301,6 +301,7 @@ class TargetLoweringBase {
     Type *Ty = nullptr;
     bool IsSExt : 1;
     bool IsZExt : 1;
+    bool IsNoExt : 1;
     bool IsInReg : 1;
     bool IsSRet : 1;
     bool IsNest : 1;
@@ -317,10 +318,11 @@ class TargetLoweringBase {
     Type *IndirectType = nullptr;
 
     ArgListEntry()
-        : IsSExt(false), IsZExt(false), IsInReg(false), IsSRet(false),
-          IsNest(false), IsByVal(false), IsByRef(false), IsInAlloca(false),
-          IsPreallocated(false), IsReturned(false), IsSwiftSelf(false),
-          IsSwiftAsync(false), IsSwiftError(false), IsCFGuardTarget(false) {}
+        : IsSExt(false), IsZExt(false), IsNoExt(false), IsInReg(false),
+          IsSRet(false), IsNest(false), IsByVal(false), IsByRef(false),
+          IsInAlloca(false), IsPreallocated(false), IsReturned(false),
+          IsSwiftSelf(false), IsSwiftAsync(false), IsSwiftError(false),
+          IsCFGuardTarget(false) {}
 
     void setAttributes(const CallBase *Call, unsigned ArgIdx);
   };
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index f3ef1e707675eb..24070d646e184e 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -160,6 +160,9 @@ def NoCapture : EnumAttr<"nocapture", [ParamAttr]>;
 /// Call cannot be duplicated.
 def NoDuplicate : EnumAttr<"noduplicate", [FnAttr]>;
 
+/// No extension needed before/after call (high bits are undefined).
+def NoExt : EnumAttr<"noext", [ParamAttr, RetAttr]>;
+
 /// Function does not deallocate memory.
 def NoFree : EnumAttr<"nofree", [FnAttr, ParamAttr]>;
 
diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h
index d3464b5202ff32..94e0fa2404d6fc 100644
--- a/llvm/include/llvm/Target/TargetOptions.h
+++ b/llvm/include/llvm/Target/TargetOptions.h
@@ -155,6 +155,7 @@ namespace llvm {
           XRayFunctionIndex(true), DebugStrictDwarf(false), Hotpatch(false),
           PPCGenScalarMASSEntries(false), JMCInstrument(false),
           EnableCFIFixup(false), MisExpect(false), XCOFFReadOnlyPointers(false),
+          VerifyArgABICompliance(true),
           FPDenormalMode(DenormalMode::IEEE, DenormalMode::IEEE) {}
 
     /// DisableFramePointerElim - This returns true if frame pointer elimination
@@ -381,6 +382,12 @@ namespace llvm {
     /// into the RO data section.
     unsigned XCOFFReadOnlyPointers : 1;
 
+    /// When set to true, call/return argument extensions of narrow integers
+    /// are verified in the target backend if it cares about them. This is
+    /// not done with internal tools like llc that run many tests that ignore
+    /// (lack) these extensions.
+    unsigned VerifyArgABICompliance : 1;
+
     /// Name of the stack usage file (i.e., .su file) if user passes
     /// -fstack-usage. If empty, it can be implied that -fstack-usage is not
     /// passed on the command line.
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 027a29764148f0..b7db631d7d432f 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2190,6 +2190,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::Initializes;
   case bitc::ATTR_KIND_CORO_ELIDE_SAFE:
     return Attribute::CoroElideSafe;
+  case bitc::ATTR_KIND_NO_EXT:
+    return Attribute::NoExt;
   }
 }
 
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index a5942153dc2d64..5ead94218c3f87 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -893,6 +893,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_RANGE;
   case Attribute::Initializes:
     return bitc::ATTR_KIND_INITIALIZES;
+  case Attribute::NoExt:
+    return bitc::ATTR_KIND_NO_EXT;
   case Attribute::EndAttrKinds:
     llvm_unreachable("Can not encode end-attribute kinds marker.");
   case Attribute::None:
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index eec89f04c6356d..492e23231065d5 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -2284,6 +2284,8 @@ void SelectionDAGBuilder::visitRet(const ReturnInst &I) {
           Flags.setSExt();
         else if (ExtendKind == ISD::ZERO_EXTEND)
           Flags.setZExt();
+        else if (F->getAttributes().hasRetAttr(Attribute::NoExt))
+          Flags.setNoExt();
 
         for (unsigned i = 0; i < NumParts; ++i) {
           Outs.push_back(ISD::OutputArg(Flags,
@@ -11004,6 +11006,8 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
         Flags.setZExt();
       if (Args[i].IsSExt)
         Flags.setSExt();
+      if (Args[i].IsNoExt)
+        Flags.setNoExt();
       if (Args[i].IsInReg) {
         // If we are using vectorcall calling convention, a structure that is
         // passed InReg - is surely an HVA
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index a293c2391c3283..a2a232ed93b72f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -113,6 +113,7 @@ void TargetLoweringBase::ArgListEntry::setAttributes(const CallBase *Call,
                                                      unsigned ArgIdx) {
   IsSExt = Call->paramHasAttr(ArgIdx, Attribute::SExt);
   IsZExt = Call->paramHasAttr(ArgIdx, Attribute::ZExt);
+  IsNoExt = Call->paramHasAttr(ArgIdx, Attribute::NoExt);
   IsInReg = Call->paramHasAttr(ArgIdx, Attribute::InReg);
   IsSRet = Call->paramHasAttr(ArgIdx, Attribute::StructRet);
   IsNest = Call->paramHasAttr(ArgIdx, Attribute::Nest);
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 582a8c139b2937..dcf7b30922521f 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -1917,6 +1917,13 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI,
   if (Subtarget.isTargetXPLINK64())
     IsTailCall = false;
 
+  // Integer args <=32 bits should have an extension attribute.
+  bool HasLocalLinkage = false;
+  if (auto *G = dyn_cast<GlobalAddressSDNode>(Callee))
+    if (const Function *Fn = dyn_cast<Function>(G->getGlobal()))
+      HasLocalLinkage = Fn->hasLocalLinkage();
+  verifyNarrowIntegerArgs(Outs, HasLocalLinkage);
+
   // Analyze the operands of the call, assigning locations to each operand.
   SmallVector<CCValAssign, 16> ArgLocs;
   SystemZCCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, Ctx);
@@ -2177,6 +2184,9 @@ SystemZTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
                                    const SDLoc &DL, SelectionDAG &DAG) const {
   MachineFunction &MF = DAG.getMachineFunction();
 
+  // Integer args <=32 bits should have an extension attribute.
+  verifyNarrowIntegerArgs(Outs, MF.getFunction().hasLocalLinkage());
+
   // Assign locations to each returned value.
   SmallVector<CCValAssign, 16> RetLocs;
   CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext());
@@ -9800,3 +9810,24 @@ SDValue SystemZTargetLowering::lowerVECREDUCE_ADD(SDValue Op,
       ISD::EXTRACT_VECTOR_ELT, DL, VT, DAG.getBitcast(OpVT, Op),
       DAG.getConstant(OpVT.getVectorNumElements() - 1, DL, MVT::i32));
 }
+
+// Verify that narrow integer arguments are extended as required by the ABI.
+void SystemZTargetLowering::
+verifyNarrowIntegerArgs(const SmallVectorImpl<ISD::OutputArg> &Outs,
+                        bool HasLocalLinkage) const {
+  if (!getTargetMachine().Options.VerifyArgABICompliance || HasLocalLinkage ||
+      !Subtarget.isTargetELF())
+    return;
+
+  for (unsigned i = 0; i < Outs.size(); ++i) {
+    MVT VT = Outs[i].VT;
+    ISD::ArgFlagsTy Flags = Outs[i].Flags;
+    if (VT.isInteger()) {
+      assert((VT == MVT::i32 || VT.getSizeInBits() >= 64) &&
+             "Unexpected integer argument VT.");
+      assert((VT != MVT::i32 ||
+              (Flags.isSExt() || Flags.isZExt() || Flags.isNoExt())) &&
+             "Narrow integer argument must have a valid extension type.");
+    }
+  }
+}
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index 4a18bde00a0b98..ce01969c0ccaf8 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -804,6 +804,9 @@ class SystemZTargetLowering : public TargetLowering {
   MachineMemOperand::Flags
   getTargetMMOFlags(const Instruction &I) const override;
   const TargetRegisterClass *getRepRegClassFor(MVT VT) const override;
+
+  void verifyNarrowIntegerArgs(const SmallVectorImpl<ISD::OutputArg> &Outs,
+                               bool HasLocalLinkage) const;
 };
 
 struct SystemZVectorConstantInfo {
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 895b588a9e5ac3..894e02d314125e 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -996,6 +996,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
       case Attribute::DeadOnUnwind:
       case Attribute::Range:
       case Attribute::Initializes:
+      case Attribute::NoExt:
       //  These are not really attributes.
       case Attribute::None:
       case Attribute::EndAttrKinds:
diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp
index 3f27a5fd1a0eb9..2c1901cdd49d8b 100644
--- a/llvm/tools/llc/llc.cpp
+++ b/llvm/tools/llc/llc.cpp
@@ -620,6 +620,10 @@ static int compileModule(char **argv, LLVMContext &Context) {
   // Ensure the filename is passed down to CodeViewDebug.
   Target->Options.ObjectFilenameForDebug = Out->outputFilename();
 
+  // Tell target that this tool is not necessarily used with argument ABI
+  // compliance (i.e. narrow integer argument extensions).
+  Target->Options.VerifyArgABICompliance = 0;
+
   std::unique_ptr<ToolOutputFile> DwoOut;
   if (!SplitDwarfOutputFile.empty()) {
     std::error_code EC;

>From b35bf4e8e1d36551e4afa31e00e35c7e5eaab4e2 Mon Sep 17 00:00:00 2001
From: Jonas Paulsson <paulson1 at linux.ibm.com>
Date: Thu, 12 Sep 2024 17:12:51 +0200
Subject: [PATCH 2/3] Add a CL option for verification (default off) + tests

---
 .../Target/SystemZ/SystemZISelLowering.cpp    | 42 +++++++++++++++----
 llvm/lib/Target/SystemZ/SystemZISelLowering.h |  3 +-
 llvm/test/CodeGen/SystemZ/args-14.ll          | 39 +++++++++++++++++
 llvm/test/CodeGen/SystemZ/args-15.ll          | 11 +++++
 llvm/test/CodeGen/SystemZ/args-16.ll          | 12 ++++++
 llvm/test/CodeGen/SystemZ/args-17.ll          | 11 +++++
 llvm/test/CodeGen/SystemZ/args-18.ll          | 14 +++++++
 llvm/test/CodeGen/SystemZ/args-19.ll          | 14 +++++++
 llvm/test/CodeGen/SystemZ/args-20.ll          | 14 +++++++
 llvm/test/CodeGen/SystemZ/args-21.ll          | 19 +++++++++
 10 files changed, 171 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/SystemZ/args-14.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-15.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-16.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-17.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-18.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-19.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-20.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/args-21.ll

diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index dcf7b30922521f..4e212c58c20620 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -34,6 +34,13 @@ using namespace llvm;
 
 #define DEBUG_TYPE "systemz-lower"
 
+// Temporarily let this be disabled by default until all known problems
+// related to argument extensions are fixed.
+static cl::opt<bool> EnableIntArgExtCheck(
+    "argext-abi-check", cl::init(false),
+    cl::desc("Verify that narrow int args are properly extended per the "
+             "SystemZ ABI."));
+
 namespace {
 // Represents information about a comparison.
 struct Comparison {
@@ -1918,11 +1925,11 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI,
     IsTailCall = false;
 
   // Integer args <=32 bits should have an extension attribute.
-  bool HasLocalLinkage = false;
+  bool IsInternal = false;
   if (auto *G = dyn_cast<GlobalAddressSDNode>(Callee))
     if (const Function *Fn = dyn_cast<Function>(G->getGlobal()))
-      HasLocalLinkage = Fn->hasLocalLinkage();
-  verifyNarrowIntegerArgs(Outs, HasLocalLinkage);
+      IsInternal = isFullyInternal(Fn);
+  verifyNarrowIntegerArgs(Outs, IsInternal);
 
   // Analyze the operands of the call, assigning locations to each operand.
   SmallVector<CCValAssign, 16> ArgLocs;
@@ -2185,7 +2192,7 @@ SystemZTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
   MachineFunction &MF = DAG.getMachineFunction();
 
   // Integer args <=32 bits should have an extension attribute.
-  verifyNarrowIntegerArgs(Outs, MF.getFunction().hasLocalLinkage());
+  verifyNarrowIntegerArgs(Outs, isFullyInternal(&MF.getFunction()));
 
   // Assign locations to each returned value.
   SmallVector<CCValAssign, 16> RetLocs;
@@ -9811,12 +9818,33 @@ SDValue SystemZTargetLowering::lowerVECREDUCE_ADD(SDValue Op,
       DAG.getConstant(OpVT.getVectorNumElements() - 1, DL, MVT::i32));
 }
 
+// Only consider a function fully internal as long as it has local linkage
+// and is not used in any other way than acting as the called function at
+// call sites.  TODO: Remove this when/if all internal functions adhere to
+// the ABI.
+bool SystemZTargetLowering::isFullyInternal(const Function *Fn) const {
+  if (!Fn->hasLocalLinkage())
+    return false;
+  for (const User *U : Fn->users()) {
+    if (auto *CB = dyn_cast<CallBase>(U)) {
+      if (CB->getCalledFunction() != Fn)
+        return false;
+    } else
+      return false;
+  }
+  return true;
+}
+
 // Verify that narrow integer arguments are extended as required by the ABI.
 void SystemZTargetLowering::
 verifyNarrowIntegerArgs(const SmallVectorImpl<ISD::OutputArg> &Outs,
-                        bool HasLocalLinkage) const {
-  if (!getTargetMachine().Options.VerifyArgABICompliance || HasLocalLinkage ||
-      !Subtarget.isTargetELF())
+                        bool IsInternal) const {
+  if (IsInternal || !Subtarget.isTargetELF())
+    return;
+
+  // Temporarily only do the check when explicitly requested.
+  if (/* !getTargetMachine().Options.VerifyArgABICompliance && */
+      !EnableIntArgExtCheck)
     return;
 
   for (unsigned i = 0; i < Outs.size(); ++i) {
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index ce01969c0ccaf8..8c528897182d17 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -805,8 +805,9 @@ class SystemZTargetLowering : public TargetLowering {
   getTargetMMOFlags(const Instruction &I) const override;
   const TargetRegisterClass *getRepRegClassFor(MVT VT) const override;
 
+  bool isFullyInternal(const Function *Fn) const;
   void verifyNarrowIntegerArgs(const SmallVectorImpl<ISD::OutputArg> &Outs,
-                               bool HasLocalLinkage) const;
+                               bool IsInternal) const;
 };
 
 struct SystemZVectorConstantInfo {
diff --git a/llvm/test/CodeGen/SystemZ/args-14.ll b/llvm/test/CodeGen/SystemZ/args-14.ll
new file mode 100644
index 00000000000000..84e7523e788de4
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-14.ll
@@ -0,0 +1,39 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu -argext-abi-check
+
+; Test that it works to pass structs as outgoing call arguments when the
+; NoExt attribute is given, either in the call instruction or in the
+; prototype of the called function.
+define void @caller() {
+  call void @bar_Struct_32(i32 noext 123)
+  call void @bar_Struct_16(i16 123)
+  call void @bar_Struct_8(i8 noext 123)
+  ret void
+}
+
+declare void @bar_Struct_32(i32 %Arg)
+declare void @bar_Struct_16(i16 noext %Arg)
+declare void @bar_Struct_8(i8 %Arg)
+
+; Test that it works to return values with the NoExt attribute.
+define noext i8 @callee_NoExtRet_i8() {
+  ret i8 -1
+}
+
+define noext i16 @callee_NoExtRet_i16() {
+  ret i16 -1
+}
+
+define noext i32 @callee_NoExtRet_i32() {
+  ret i32 -1
+}
+
+; An internal function is not checked for an extension attribute.
+define internal i32 @callee_NoExtRet_internal(i32 %Arg) {
+  ret i32 %Arg
+}
+
+; A call to an internal function is ok without argument extension.
+define void @caller_internal() {
+  call i32 @callee_NoExtRet_internal(i32 0)
+  ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/args-15.ll b/llvm/test/CodeGen/SystemZ/args-15.ll
new file mode 100644
index 00000000000000..c810aeb8c46c5d
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-15.ll
@@ -0,0 +1,11 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension of an i32 return value.
+
+define i32 @callee_MissingRetAttr() {
+  ret i32 -1
+}
+
+; CHECK: Narrow integer argument must have a valid extension type.
diff --git a/llvm/test/CodeGen/SystemZ/args-16.ll b/llvm/test/CodeGen/SystemZ/args-16.ll
new file mode 100644
index 00000000000000..b76a2afea50775
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-16.ll
@@ -0,0 +1,12 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension of an i16 return value.
+
+define i16 @callee_MissingRetAttr() {
+  ret i16 -1
+}
+
+; CHECK: Narrow integer argument must have a valid extension type.
+
diff --git a/llvm/test/CodeGen/SystemZ/args-17.ll b/llvm/test/CodeGen/SystemZ/args-17.ll
new file mode 100644
index 00000000000000..bce54b3d2aa1fe
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-17.ll
@@ -0,0 +1,11 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension of an i8 return value.
+
+define i8 @callee_MissingRetAttr() {
+  ret i8 -1
+}
+
+; CHECK: Narrow integer argument must have a valid extension type.
diff --git a/llvm/test/CodeGen/SystemZ/args-18.ll b/llvm/test/CodeGen/SystemZ/args-18.ll
new file mode 100644
index 00000000000000..82e9729d3a2dfd
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-18.ll
@@ -0,0 +1,14 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension of an outgoing i32 call argument.
+
+define void @caller() {
+  call void @bar_Struct(i32 123)
+  ret void
+}
+
+declare void @bar_Struct(i32 %Arg)
+
+; CHECK: Narrow integer argument must have a valid extension type
diff --git a/llvm/test/CodeGen/SystemZ/args-19.ll b/llvm/test/CodeGen/SystemZ/args-19.ll
new file mode 100644
index 00000000000000..40a794417b4c6f
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-19.ll
@@ -0,0 +1,14 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension of an outgoing i16 call argument.
+
+define void @caller() {
+  call void @bar_Struct(i16 123)
+  ret void
+}
+
+declare void @bar_Struct(i16 %Arg)
+
+; CHECK: Narrow integer argument must have a valid extension type
diff --git a/llvm/test/CodeGen/SystemZ/args-20.ll b/llvm/test/CodeGen/SystemZ/args-20.ll
new file mode 100644
index 00000000000000..ce8b828a2d539a
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-20.ll
@@ -0,0 +1,14 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension of an outgoing i8 call argument.
+
+define void @caller() {
+  call void @bar_Struct(i8 123)
+  ret void
+}
+
+declare void @bar_Struct(i8 %Arg)
+
+; CHECK: Narrow integer argument must have a valid extension type
diff --git a/llvm/test/CodeGen/SystemZ/args-21.ll b/llvm/test/CodeGen/SystemZ/args-21.ll
new file mode 100644
index 00000000000000..c64233094c7df9
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-21.ll
@@ -0,0 +1,19 @@
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu -argext-abi-check 2>&1 \
+; RUN:   | FileCheck %s
+; REQUIRES: asserts
+;
+; Test detection of missing extension involving an internal function which is
+; passed as a function pointer to an external function.
+
+define internal i32 @bar(i32 %Arg) {
+  ret i32 %Arg
+}
+
+declare void @ExtFun(ptr %FunPtr);
+
+define void @foo() {
+  call void @ExtFun(ptr @bar)
+  ret void
+}
+
+; CHECK: Narrow integer argument must have a valid extension type

>From a74fb7cac005078aef7dc84c9ae753c1e0ff2fb3 Mon Sep 17 00:00:00 2001
From: Jonas Paulsson <paulson1 at linux.ibm.com>
Date: Thu, 19 Sep 2024 14:11:22 +0200
Subject: [PATCH 3/3] Updates per review.

---
 llvm/docs/LangRef.rst                           |  7 +++++--
 llvm/lib/Target/SystemZ/SystemZISelLowering.cpp | 15 ++++++++++-----
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 5356748469bcd8..2e6030af7ba935 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1189,9 +1189,12 @@ Currently, only the following parameter attributes are defined:
     value should be sign-extended to the extent required by the target's
     ABI (which is usually 32-bits) by the caller (for a parameter) or
     the callee (for a return value).
-``noext`` This indicates to the code generator that the parameter or return
+``noext``
+    This indicates to the code generator that the parameter or return
     value has the high bits undefined, as for a struct in register, and
-    therefore does not need to be sign or zero extended.
+    therefore does not need to be sign or zero extended. This is the same
+    as default behavior and is only actually used (by some targets) to
+    validate that one of the attributes is always present.
 ``inreg``
     This indicates that this parameter or return value should be treated
     in a special target-dependent fashion while emitting code for
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 4e212c58c20620..a24ed89e2af455 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -9820,8 +9820,7 @@ SDValue SystemZTargetLowering::lowerVECREDUCE_ADD(SDValue Op,
 
 // Only consider a function fully internal as long as it has local linkage
 // and is not used in any other way than acting as the called function at
-// call sites.  TODO: Remove this when/if all internal functions adhere to
-// the ABI.
+// call sites.
 bool SystemZTargetLowering::isFullyInternal(const Function *Fn) const {
   if (!Fn->hasLocalLinkage())
     return false;
@@ -9842,9 +9841,15 @@ verifyNarrowIntegerArgs(const SmallVectorImpl<ISD::OutputArg> &Outs,
   if (IsInternal || !Subtarget.isTargetELF())
     return;
 
-  // Temporarily only do the check when explicitly requested.
-  if (/* !getTargetMachine().Options.VerifyArgABICompliance && */
-      !EnableIntArgExtCheck)
+  // Temporarily only do the check when explicitly requested, until it can be
+  // enabled by default.
+  if (!EnableIntArgExtCheck)
+    return;
+
+  if (EnableIntArgExtCheck.getNumOccurrences()) {
+    if (!EnableIntArgExtCheck)
+      return;
+  } else if (!getTargetMachine().Options.VerifyArgABICompliance)
     return;
 
   for (unsigned i = 0; i < Outs.size(); ++i) {



More information about the cfe-commits mailing list