[clang] [llvm] target ABI: improve call parameters extensions handling (PR #100757)
Jonas Paulsson via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 27 02:55:17 PDT 2024
https://github.com/JonPsson1 updated https://github.com/llvm/llvm-project/pull/100757
>From 834d8bcd0077f0691ab33983cc72697ff6e65793 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] 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 ca2c79b51ac96b..169d3c914e70a8 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 4887ae6ee99668..dcaca159b902d2 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
@@ -9118,8 +9125,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.
@@ -9214,8 +9221,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.
@@ -12700,8 +12707,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 8a2e6583af87c5..96470e6f7fd8bb 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -760,6 +760,7 @@ enum AttributeKindCodes {
ATTR_KIND_HYBRID_PATCHABLE = 95,
ATTR_KIND_SANITIZE_REALTIME = 96,
ATTR_KIND_NO_SANITIZE_REALTIME = 97,
+ ATTR_KIND_NO_EXT = 98,
};
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 eda38cd8a564d6..6538c8494d5aa9 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 80936c0ee83355..78156dfc3ea471 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 6767c7ca288bc4..8666873909e106 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2189,6 +2189,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::Range;
case bitc::ATTR_KIND_INITIALIZES:
return Attribute::Initializes;
+ 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 d21ff10d51d00f..a0bc9d82db7f1a 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -889,6 +889,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 60dcb118542785..7a9969c28ff0d2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -2283,6 +2283,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,
@@ -10983,6 +10985,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 4e796289cff0a1..e64222348b3322 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 6f84bd6c6e4ff4..939a04682f7d6a 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 1e7285e3e0fc53..0538de15e184f0 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 cf00299812bb7f..a3c228fb952e8f 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 80c84a977c26c6..3675ad0a7fb507 100644
--- a/llvm/tools/llc/llc.cpp
+++ b/llvm/tools/llc/llc.cpp
@@ -617,6 +617,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;
More information about the cfe-commits
mailing list