llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-x86
Author: Nikita Popov (nikic)
<details>
<summary>Changes</summary>
For X86, _BitInt(N) with N <= 32 is passed iN, while for 32 < N <= 64
it's passed as i64. I don't think there is any good reason for this difference
in behavior, and we should always use iN.
Nominally, this does not change the ABI. The previous Direct pass as i64
implied that the high bits are unspecified. In practice, the callee
zero-extended the value (independent of _BitInt signedness), but the caller did
not rely on that zero-extension. With CIR it was zero or sign extended,
depending on signedness. Both are ultimately correct (because the high bits are
unspecified).
This still leaves behind a peculiarity for 64 < N < 128, which gets
returned as `{ i64, i64 }`, until we reach N=128, when it switches to `i128`. I
believe all of those should also use iN, but I'll leave that for a separate
change. I'm also not changing argument passing here.
Followup to https://github.com/llvm/llvm-project/pull/232729.
---
Full diff: https://github.com/llvm/llvm-project/pull/234872.diff
7 Files Affected:
- (modified) clang/lib/CodeGen/Targets/X86.cpp (+1-2)
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-bitint.cir (+18-66)
- (modified) clang/test/CodeGen/X86/x86_64-abi-size.c (+1-1)
- (modified) clang/test/CodeGen/ext-int-cc.c (+1-1)
- (modified) clang/test/CodeGenCXX/ext-int.cpp (+2-2)
- (modified) llvm/lib/ABI/Targets/X86.cpp (+2-3)
- (modified) llvm/unittests/ABI/X86TargetInfoTest.cpp (+1-4)
``````````diff
diff --git a/clang/lib/CodeGen/Targets/X86.cpp
b/clang/lib/CodeGen/Targets/X86.cpp
index 4dd9eef484021..a6789e924dc6f 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2848,8 +2848,7 @@ ABIArgInfo X86_64ABIInfo::classifyReturnType(QualType
RetTy) const {
if (const auto *ED = RetTy->getAsEnumDecl())
RetTy = ED->getIntegerType();
- if (RetTy->isIntegralOrEnumerationType() &&
- isPromotableIntegerTypeForABI(RetTy))
+ if (RetTy->isIntegralOrEnumerationType())
return shouldExtendReturnType(*this, RetTy)
? ABIArgInfo::getExtend(RetTy)
: ABIArgInfo::getDirect();
diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-bitint.cir
b/clang/test/CIR/Transforms/abi-lowering/x86_64-bitint.cir
index a37021076d63e..e858299f3f362 100644
--- a/clang/test/CIR/Transforms/abi-lowering/x86_64-bitint.cir
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-bitint.cir
@@ -84,64 +84,44 @@ module attributes {
cir.return %arg0 : !b33i
}
- // CHECK-LABEL: cir.func{{.*}} @ret_b33(%arg0: !u64i) -> !u64i
- // CHECK-NEXT: %[[RETSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
+ // CHECK-LABEL: cir.func{{.*}} @ret_b33(%arg0: !u64i) -> !cir.int<s, 33,
bitint>
// CHECK-NEXT: %[[ARGSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
// CHECK-NEXT: cir.store %arg0, %[[ARGSLOT]] : !u64i, !cir.ptr<!u64i>
// CHECK-NEXT: %[[ARGVIEW:[0-9]+]] = cir.cast bitcast %[[ARGSLOT]]
// CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
// CHECK-NEXT: %[[VAL:[0-9]+]] = cir.load %[[ARGVIEW]]
// CHECK-SAME: : !cir.ptr<!cir.int<s, 33, bitint>>, !cir.int<s, 33,
bitint>
- // CHECK-NEXT: %[[RETVIEW:[0-9]+]] = cir.cast bitcast %[[RETSLOT]]
- // CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
- // CHECK-NEXT: cir.store %[[VAL]], %[[RETVIEW]]
- // CHECK-SAME: : !cir.int<s, 33, bitint>, !cir.ptr<!cir.int<s, 33,
bitint>>
- // CHECK-NEXT: %[[WIDE:[0-9]+]] = cir.load %[[RETSLOT]] :
!cir.ptr<!u64i>, !u64i
- // CHECK-NEXT: cir.return %[[WIDE]] : !u64i
+ // CHECK-NEXT: cir.return %[[VAL]] : !cir.int<s, 33, bitint>
- // LLVM-LABEL: define i64 @ret_b33(
+ // LLVM-LABEL: define i33 @ret_b33(
// LLVM-SAME: i64 %[[ARG:[0-9]+]])
- // LLVM-NEXT: %[[RETSLOT:[0-9]+]] = alloca i64
// LLVM-NEXT: %[[ARGSLOT:[0-9]+]] = alloca i64
// LLVM-NEXT: store i64 %[[ARG]], ptr %[[ARGSLOT]]
// LLVM-NEXT: %[[WIDE:[0-9]+]] = load i64, ptr %[[ARGSLOT]]
// LLVM-NEXT: %[[NARROW:[0-9]+]] = trunc i64 %[[WIDE]] to i33
- // LLVM-NEXT: %[[EXT:[0-9]+]] = sext i33 %[[NARROW]] to i64
- // LLVM-NEXT: store i64 %[[EXT]], ptr %[[RETSLOT]]
- // LLVM-NEXT: %[[RES:[0-9]+]] = load i64, ptr %[[RETSLOT]]
- // LLVM-NEXT: ret i64 %[[RES]]
+ // LLVM-NEXT: ret i33 %[[NARROW]]
// Signedness changes only the extension back into the eightbyte.
cir.func @ret_ub33(%arg0: !ub33i) -> !ub33i {
cir.return %arg0 : !ub33i
}
- // CHECK-LABEL: cir.func{{.*}} @ret_ub33(%arg0: !u64i) -> !u64i
- // CHECK-NEXT: %[[RETSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
+ // CHECK-LABEL: cir.func{{.*}} @ret_ub33(%arg0: !u64i) -> !cir.int<u, 33,
bitint>
// CHECK-NEXT: %[[ARGSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
// CHECK-NEXT: cir.store %arg0, %[[ARGSLOT]] : !u64i, !cir.ptr<!u64i>
// CHECK-NEXT: %[[ARGVIEW:[0-9]+]] = cir.cast bitcast %[[ARGSLOT]]
// CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<u, 33, bitint>>
// CHECK-NEXT: %[[VAL:[0-9]+]] = cir.load %[[ARGVIEW]]
// CHECK-SAME: : !cir.ptr<!cir.int<u, 33, bitint>>, !cir.int<u, 33,
bitint>
- // CHECK-NEXT: %[[RETVIEW:[0-9]+]] = cir.cast bitcast %[[RETSLOT]]
- // CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<u, 33, bitint>>
- // CHECK-NEXT: cir.store %[[VAL]], %[[RETVIEW]]
- // CHECK-SAME: : !cir.int<u, 33, bitint>, !cir.ptr<!cir.int<u, 33,
bitint>>
- // CHECK-NEXT: %[[WIDE:[0-9]+]] = cir.load %[[RETSLOT]] :
!cir.ptr<!u64i>, !u64i
- // CHECK-NEXT: cir.return %[[WIDE]] : !u64i
+ // CHECK-NEXT: cir.return %[[VAL]] : !cir.int<u, 33, bitint>
- // LLVM-LABEL: define i64 @ret_ub33(
+ // LLVM-LABEL: define i33 @ret_ub33(
// LLVM-SAME: i64 %[[ARG:[0-9]+]])
- // LLVM-NEXT: %[[RETSLOT:[0-9]+]] = alloca i64
// LLVM-NEXT: %[[ARGSLOT:[0-9]+]] = alloca i64
// LLVM-NEXT: store i64 %[[ARG]], ptr %[[ARGSLOT]]
// LLVM-NEXT: %[[WIDE:[0-9]+]] = load i64, ptr %[[ARGSLOT]]
// LLVM-NEXT: %[[NARROW:[0-9]+]] = trunc i64 %[[WIDE]] to i33
- // LLVM-NEXT: %[[EXT:[0-9]+]] = zext i33 %[[NARROW]] to i64
- // LLVM-NEXT: store i64 %[[EXT]], ptr %[[RETSLOT]]
- // LLVM-NEXT: %[[RES:[0-9]+]] = load i64, ptr %[[RETSLOT]]
- // LLVM-NEXT: ret i64 %[[RES]]
+ // LLVM-NEXT: ret i33 %[[NARROW]]
// 63 bits fills the eightbyte, and the slot takes whichever type is larger,
// so here it is the bit-precise one and the i64 view moves to the load.
@@ -149,31 +129,21 @@ module attributes {
cir.return %arg0 : !b63i
}
- // CHECK-LABEL: cir.func{{.*}} @ret_b63(%arg0: !u64i) -> !u64i
- // CHECK-NEXT: %[[RETSLOT:[0-9]+]] = cir.alloca "coerce" align(8)
- // CHECK-SAME: : !cir.ptr<!cir.int<s, 63, bitint>>
+ // CHECK-LABEL: cir.func{{.*}} @ret_b63(%arg0: !u64i) -> !cir.int<s, 63,
bitint>
// CHECK-NEXT: %[[ARGSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
// CHECK-NEXT: cir.store %arg0, %[[ARGSLOT]] : !u64i, !cir.ptr<!u64i>
// CHECK-NEXT: %[[ARGVIEW:[0-9]+]] = cir.cast bitcast %[[ARGSLOT]]
// CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 63, bitint>>
// CHECK-NEXT: %[[VAL:[0-9]+]] = cir.load %[[ARGVIEW]]
// CHECK-SAME: : !cir.ptr<!cir.int<s, 63, bitint>>, !cir.int<s, 63,
bitint>
- // CHECK-NEXT: cir.store %[[VAL]], %[[RETSLOT]]
- // CHECK-SAME: : !cir.int<s, 63, bitint>, !cir.ptr<!cir.int<s, 63,
bitint>>
- // CHECK-NEXT: %[[RETVIEW:[0-9]+]] = cir.cast bitcast %[[RETSLOT]]
- // CHECK-SAME: : !cir.ptr<!cir.int<s, 63, bitint>> -> !cir.ptr<!u64i>
- // CHECK-NEXT: %[[WIDE:[0-9]+]] = cir.load %[[RETVIEW]] :
!cir.ptr<!u64i>, !u64i
- // CHECK-NEXT: cir.return %[[WIDE]] : !u64i
+ // CHECK-NEXT: cir.return %[[VAL]] : !cir.int<s, 63, bitint>
- // LLVM-LABEL: define i64 @ret_b63(
+ // LLVM-LABEL: define i63 @ret_b63(
// LLVM-SAME: i64 %[[ARG:[0-9]+]])
// LLVM: store i64 %[[ARG]], ptr %[[ARGSLOT:[0-9]+]], align 8
// LLVM-NEXT: %[[WIDE:[0-9]+]] = load i64, ptr %[[ARGSLOT]], align 8
// LLVM-NEXT: %[[NARROW:[0-9]+]] = trunc i64 %[[WIDE]] to i63
- // LLVM-NEXT: %[[EXT:[0-9]+]] = sext i63 %[[NARROW]] to i64
- // LLVM-NEXT: store i64 %[[EXT]], ptr %[[RETSLOT:[0-9]+]], align 8
- // LLVM-NEXT: %[[RES:[0-9]+]] = load i64, ptr %[[RETSLOT]], align 8
- // LLVM-NEXT: ret i64 %[[RES]]
+ // LLVM-NEXT: ret i63 %[[NARROW]]
// A _BitInt exactly filling one register is Direct, signature unchanged.
cir.func @ret_b64(%arg0: !b64i) -> !b64i {
@@ -345,35 +315,23 @@ module attributes {
cir.return %0 : !b33i
}
- // CHECK-LABEL: cir.func{{.*}} @call_b33(%arg0: !u64i) -> !u64i
- // CHECK-NEXT: %[[RETSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
+ // CHECK-LABEL: cir.func{{.*}} @call_b33(%arg0: !u64i) -> !cir.int<s, 33,
bitint>
// CHECK-NEXT: %[[INSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
// CHECK-NEXT: cir.store %arg0, %[[INSLOT]] : !u64i, !cir.ptr<!u64i>
// CHECK-NEXT: %[[INVIEW:[0-9]+]] = cir.cast bitcast %[[INSLOT]]
// CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
// CHECK-NEXT: %[[VAL:[0-9]+]] = cir.load %[[INVIEW]]
// CHECK-SAME: : !cir.ptr<!cir.int<s, 33, bitint>>, !cir.int<s, 33,
bitint>
- // CHECK-NEXT: %[[RESSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
// CHECK-NEXT: %[[ARGSLOT:[0-9]+]] = cir.alloca "coerce" align(8) :
!cir.ptr<!u64i>
// CHECK-NEXT: %[[ARGVIEW:[0-9]+]] = cir.cast bitcast %[[ARGSLOT]]
// CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
// CHECK-NEXT: cir.store %[[VAL]], %[[ARGVIEW]]
// CHECK-SAME: : !cir.int<s, 33, bitint>, !cir.ptr<!cir.int<s, 33,
bitint>>
// CHECK-NEXT: %[[WIDE:[0-9]+]] = cir.load %[[ARGSLOT]] :
!cir.ptr<!u64i>, !u64i
- // CHECK-NEXT: %[[RES:[0-9]+]] = cir.call @ret_b33(%[[WIDE]]) : (!u64i)
-> !u64i
- // CHECK-NEXT: cir.store %[[RES]], %[[RESSLOT]] : !u64i, !cir.ptr<!u64i>
- // CHECK-NEXT: %[[RESVIEW:[0-9]+]] = cir.cast bitcast %[[RESSLOT]]
- // CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
- // CHECK-NEXT: %[[NARROW:[0-9]+]] = cir.load %[[RESVIEW]]
- // CHECK-SAME: : !cir.ptr<!cir.int<s, 33, bitint>>, !cir.int<s, 33,
bitint>
- // CHECK-NEXT: %[[RETVIEW:[0-9]+]] = cir.cast bitcast %[[RETSLOT]]
- // CHECK-SAME: : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
- // CHECK-NEXT: cir.store %[[NARROW]], %[[RETVIEW]]
- // CHECK-SAME: : !cir.int<s, 33, bitint>, !cir.ptr<!cir.int<s, 33,
bitint>>
- // CHECK-NEXT: %[[OUT:[0-9]+]] = cir.load %[[RETSLOT]] : !cir.ptr<!u64i>,
!u64i
- // CHECK-NEXT: cir.return %[[OUT]] : !u64i
+ // CHECK-NEXT: %[[RES:[0-9]+]] = cir.call @ret_b33(%[[WIDE]]) : (!u64i)
-> !cir.int<s, 33, bitint>
+ // CHECK-NEXT: cir.return %[[RES]] : !cir.int<s, 33, bitint>
- // LLVM-LABEL: define i64 @call_b33(
+ // LLVM-LABEL: define i33 @call_b33(
// LLVM-SAME: i64 %[[ARG:[0-9]+]])
// LLVM: store i64 %[[ARG]], ptr %[[INSLOT:[0-9]+]], align 8
// LLVM-NEXT: %[[INWIDE:[0-9]+]] = load i64, ptr %[[INSLOT]], align 8
@@ -381,14 +339,8 @@ module attributes {
// LLVM: %[[ARGEXT:[0-9]+]] = sext i33 %[[VAL]] to i64
// LLVM-NEXT: store i64 %[[ARGEXT]], ptr %[[ARGSLOT:[0-9]+]], align 8
// LLVM-NEXT: %[[WIDE:[0-9]+]] = load i64, ptr %[[ARGSLOT]], align 8
- // LLVM-NEXT: %[[RES:[0-9]+]] = call i64 @ret_b33(i64 %[[WIDE]])
- // LLVM-NEXT: store i64 %[[RES]], ptr %[[RESSLOT:[0-9]+]], align 8
- // LLVM-NEXT: %[[RESWIDE:[0-9]+]] = load i64, ptr %[[RESSLOT]], align 8
- // LLVM-NEXT: %[[RESNARROW:[0-9]+]] = trunc i64 %[[RESWIDE]] to i33
- // LLVM-NEXT: %[[RETEXT:[0-9]+]] = sext i33 %[[RESNARROW]] to i64
- // LLVM-NEXT: store i64 %[[RETEXT]], ptr %[[RETSLOT:[0-9]+]], align 8
- // LLVM-NEXT: %[[OUT:[0-9]+]] = load i64, ptr %[[RETSLOT]], align 8
- // LLVM-NEXT: ret i64 %[[OUT]]
+ // LLVM-NEXT: %[[RES:[0-9]+]] = call i33 @ret_b33(i64 %[[WIDE]])
+ // LLVM-NEXT: ret i33 %[[RES]]
// A call site decomposes the argument into the two registers the callee
// expects and reassembles the returned pair.
diff --git a/clang/test/CodeGen/X86/x86_64-abi-size.c
b/clang/test/CodeGen/X86/x86_64-abi-size.c
index 6a2760a120a94..721140a3aff1d 100644
--- a/clang/test/CodeGen/X86/x86_64-abi-size.c
+++ b/clang/test/CodeGen/X86/x86_64-abi-size.c
@@ -104,7 +104,7 @@ union LSFirst lsfirst(union LSFirst u) { return u; }
// In registers a _BitInt is coerced to the eightbytes covering its storage.
_BitInt(40) bi40(_BitInt(40) x) { return x; }
-// CHECK: define dso_local i64 @bi40(i64 noundef %{{.*}})
+// CHECK: define dso_local i40 @bi40(i64 noundef %{{.*}})
_BitInt(100) bi100(_BitInt(100) x) { return x; }
// CHECK: define dso_local { i64, i64 } @bi100(i64 noundef %{{.*}}, i64
noundef %{{.*}})
diff --git a/clang/test/CodeGen/ext-int-cc.c b/clang/test/CodeGen/ext-int-cc.c
index 0872991252eb5..3fdbe5240e392 100644
--- a/clang/test/CodeGen/ext-int-cc.c
+++ b/clang/test/CodeGen/ext-int-cc.c
@@ -159,7 +159,7 @@ void ParamPassing4(_BitInt(129) a) {}
#endif
_BitInt(63) ReturnPassing(void) { return 0; }
-// LIN64: define{{.*}} i64 @ReturnPassing(
+// LIN64: define{{.*}} i63 @ReturnPassing(
// WIN64: define dso_local i63 @ReturnPassing(
// LIN32: define{{.*}} i63 @ReturnPassing(
// WIN32: define dso_local i63 @ReturnPassing(
diff --git a/clang/test/CodeGenCXX/ext-int.cpp
b/clang/test/CodeGenCXX/ext-int.cpp
index 6ea5d2e7dcd8e..95e81dd47d33f 100644
--- a/clang/test/CodeGenCXX/ext-int.cpp
+++ b/clang/test/CodeGenCXX/ext-int.cpp
@@ -111,14 +111,14 @@ void BitfieldAssignment() {
}
unsigned _BitInt(33) ManglingTestRetParam(unsigned _BitInt(33) Param) {
-// LIN64: define{{.*}} i64 @_Z20ManglingTestRetParamDU33_(i64 %
+// LIN64: define{{.*}} i33 @_Z20ManglingTestRetParamDU33_(i64 %
// LIN32: define{{.*}} i33 @_Z20ManglingTestRetParamDU33_(i33 %
// WIN: define dso_local i33
@"?ManglingTestRetParam@@YAU?$_UBitInt@$0CB@@__clang@@U12@@Z"(i33
return 0;
}
_BitInt(33) ManglingTestRetParam(_BitInt(33) Param) {
-// LIN64: define{{.*}} i64 @_Z20ManglingTestRetParamDB33_(i64 %
+// LIN64: define{{.*}} i33 @_Z20ManglingTestRetParamDB33_(i64 %
// LIN32: define{{.*}} i33 @_Z20ManglingTestRetParamDB33_(i33 %
// WIN: define dso_local i33
@"?ManglingTestRetParam@@YAU?$_BitInt@$0CB@@__clang@@U12@@Z"(i33
return 0;
diff --git a/llvm/lib/ABI/Targets/X86.cpp b/llvm/lib/ABI/Targets/X86.cpp
index 26f2d682ad5d3..d04d624e1b9dd 100644
--- a/llvm/lib/ABI/Targets/X86.cpp
+++ b/llvm/lib/ABI/Targets/X86.cpp
@@ -759,9 +759,8 @@ ArgInfo X86_64TargetInfo::classifyReturnType(const Type
*RetTy) const {
// so that the parameter gets the right LLVM IR attributes.
if (Hi == NoClass && ResType->isInteger()) {
if (const IntegerType *IntTy = dyn_cast<IntegerType>(RetTy))
- if (isPromotableInteger(IntTy))
- return shouldExtendReturnType(IntTy) ? ArgInfo::getExtend(RetTy)
- : ArgInfo::getDirect(RetTy);
+ return shouldExtendReturnType(IntTy) ? ArgInfo::getExtend(RetTy)
+ : ArgInfo::getDirect(RetTy);
}
if (ResType->isInteger() && ResType->getSizeInBits() == 128) {
assert(Hi == Integer);
diff --git a/llvm/unittests/ABI/X86TargetInfoTest.cpp
b/llvm/unittests/ABI/X86TargetInfoTest.cpp
index 29dfb4ce1de36..4511c703f12f5 100644
--- a/llvm/unittests/ABI/X86TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/X86TargetInfoTest.cpp
@@ -942,13 +942,10 @@ TEST_F(X86TargetInfoTest, IntReturnExtension) {
TB.getIntegerType(13, llvm::Align(2), /*IsSigned=*/true,
/*IsBitInt=*/true);
expectDirectInteger(classifyRet(BitInt13, FI, TI), 13);
-
- // _BitInt > 32-bit gets passed as 64-bit, to match Clang ABI behavior.
- // Returning this as i40 would be equivalent.
const ABIType *BitInt40 =
TB.getIntegerType(40, llvm::Align(8), /*IsSigned=*/false,
/*IsBitInt=*/true);
- expectDirectInteger(classifyRet(BitInt40, FI, TI), 64);
+ expectDirectInteger(classifyRet(BitInt40, FI, TI), 40);
// char, short and int must not be extended.
expectDirectInteger(classifyRet(I8, FI, TI), 8);
``````````
</details>
https://github.com/llvm/llvm-project/pull/234872
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits