[clang] 1412022 - Target ABI: improve call parameters extensions handling (#100757)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 19 07:59:35 PDT 2024
Author: Jonas Paulsson
Date: 2024-09-19T16:59:31+02:00
New Revision: 14120227a34365e829d05c1413033d235d7d272c
URL: https://github.com/llvm/llvm-project/commit/14120227a34365e829d05c1413033d235d7d272c
DIFF: https://github.com/llvm/llvm-project/commit/14120227a34365e829d05c1413033d235d7d272c.diff
LOG: Target ABI: improve call parameters extensions handling (#100757)
For the purpose of verifying proper arguments extensions per the target's ABI,
introduce the NoExt attribute that may be used by a target when neither sign-
or zeroextension is required (e.g. with a struct in register). The purpose of
doing so is to be able to verify that there is always one of these attributes
present and by this detecting cases where sign/zero extension is actually
missing.
As a first step, this patch has the verification step done for the SystemZ
backend only, but left off by default until all known issues have been
addressed.
Other targets/front-ends can now also add NoExt attribute where needed and do
this check in the backend.
Added:
llvm/test/CodeGen/SystemZ/args-14.ll
llvm/test/CodeGen/SystemZ/args-15.ll
llvm/test/CodeGen/SystemZ/args-16.ll
llvm/test/CodeGen/SystemZ/args-17.ll
llvm/test/CodeGen/SystemZ/args-18.ll
llvm/test/CodeGen/SystemZ/args-19.ll
llvm/test/CodeGen/SystemZ/args-20.ll
llvm/test/CodeGen/SystemZ/args-21.ll
Modified:
clang/include/clang/CodeGen/CGFunctionInfo.h
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/Targets/SystemZ.cpp
clang/test/CodeGen/SystemZ/systemz-abi-vector.c
clang/test/CodeGen/SystemZ/systemz-abi.c
clang/test/CodeGen/SystemZ/systemz-abi.cpp
llvm/docs/LangRef.rst
llvm/include/llvm/Bitcode/LLVMBitCodes.h
llvm/include/llvm/CodeGen/TargetCallingConv.h
llvm/include/llvm/CodeGen/TargetLowering.h
llvm/include/llvm/IR/Attributes.td
llvm/include/llvm/Target/TargetOptions.h
llvm/lib/Bitcode/Reader/BitcodeReader.cpp
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
llvm/lib/Target/SystemZ/SystemZISelLowering.h
llvm/lib/Transforms/Utils/CodeExtractor.cpp
llvm/tools/llc/llc.cpp
Removed:
################################################################################
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..2e6030af7ba935 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,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
+ value has the high bits undefined, as for a struct in register, and
+ 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
@@ -9113,8 +9123,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 +9219,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 +12709,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..a24ed89e2af455 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 {
@@ -1917,6 +1924,13 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI,
if (Subtarget.isTargetXPLINK64())
IsTailCall = false;
+ // Integer args <=32 bits should have an extension attribute.
+ bool IsInternal = false;
+ if (auto *G = dyn_cast<GlobalAddressSDNode>(Callee))
+ if (const Function *Fn = dyn_cast<Function>(G->getGlobal()))
+ IsInternal = isFullyInternal(Fn);
+ verifyNarrowIntegerArgs(Outs, IsInternal);
+
// Analyze the operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
SystemZCCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, Ctx);
@@ -2177,6 +2191,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, isFullyInternal(&MF.getFunction()));
+
// Assign locations to each returned value.
SmallVector<CCValAssign, 16> RetLocs;
CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext());
@@ -9800,3 +9817,50 @@ SDValue SystemZTargetLowering::lowerVECREDUCE_ADD(SDValue Op,
ISD::EXTRACT_VECTOR_ELT, DL, VT, DAG.getBitcast(OpVT, 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.
+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 IsInternal) const {
+ if (IsInternal || !Subtarget.isTargetELF())
+ return;
+
+ // 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) {
+ 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..8c528897182d17 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -804,6 +804,10 @@ class SystemZTargetLowering : public TargetLowering {
MachineMemOperand::Flags
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 IsInternal) 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/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
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;
More information about the cfe-commits
mailing list