[GlobalISel] Fix sign-extended byte mask in lowerBswap#199387
Conversation
The per-byte mask in `LegalizerHelper::lowerBswap` was constructed via ``` APInt APMask(SizeInBytes * 8, 0xFF << (i * 8)); ``` where `0xFF << (i * 8)` is evaluated as a signed `int`. For `i*8 >= 24` (byte-3 mask of an s64 G_BSWAP) the value `0xFF000000` does not fit in a positive 32-bit `int`; the conversion to signed `int` is implementation-defined under C++17 (UB under C++11, fully defined under C++20) and on two's-complement targets produces `-16777216`. The modular conversion to `uint64_t` in the `APInt` constructor then materializes that negative `int` as `0xFFFFFFFFFF000000` — the intended mask was `0x00000000FF000000`. The over-wide mask preserved bytes 4-7 of the source where only byte 3 was intended, and the spurious bytes propagated through the subsequent shift/OR chain. This is a runtime miscompile on any GlobalISel target that legalizes `G_BSWAP` via `lowerBswap` at a type with `SizeInBytes >= 8` — currently RV64 without Zbb/Zbkb. Native-bswap targets (AArch64, X86) and `maxScalar(0,s32).lower()`-style RISC-V rules are unaffected because the buggy loop iteration is never reached. Use `APInt::getBitsSet` to construct the mask, matching the file's other bit-range mask constructions. Fixes llvm#199386
|
Hello @lonemeow 👋 Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.
Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description. Frequently asked questionsHow do I add reviewers? This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically. You can also add reviewers manually using the Reviewers section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using What if there are no comments? If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers. Are any special GitHub settings required to contribute to LLVM? We only require contributors to have a public email address associated with their GitHub commits, see this section of LLVM Developer Policy for details. If you have questions, feel free to leave a comment on this PR, or ask on LLVM Discord or LLVM Discourse. Thank you, |
|
@llvm/pr-subscribers-llvm-globalisel Author: Ilpo Ruotsalainen (lonemeow) ChangesThe per-byte mask in where Use Fixes #199386 Full diff: https://github.com/llvm/llvm-project/pull/199387.diff 3 Files Affected:
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index a200aa29f95ca..0cf101e8420dd 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -10042,7 +10042,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::lowerBswap(MachineInstr &MI) {
// Set i-th high/low byte in Res to i-th low/high byte from Src.
for (unsigned i = 1; i < SizeInBytes / 2; ++i) {
// AND with Mask leaves byte i unchanged and sets remaining bytes to 0.
- APInt APMask(SizeInBytes * 8, 0xFF << (i * 8));
+ APInt APMask = APInt::getBitsSet(SizeInBytes * 8, i * 8, i * 8 + 8);
auto Mask = MIRBuilder.buildConstant(Ty, APMask);
auto ShiftAmt = MIRBuilder.buildConstant(Ty, BaseShiftAmt - 16 * i);
// Low byte shifted left to place of high byte: (Src & Mask) << ShiftAmt.
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir
index b5d28765889e1..be4c8e7c2a12e 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir
@@ -117,7 +117,7 @@ body: |
; RV64I-NEXT: [[LSHR2:%[0-9]+]]:_(s64) = G_LSHR [[COPY]], [[C4]](s64)
; RV64I-NEXT: [[AND3:%[0-9]+]]:_(s64) = G_AND [[LSHR2]], [[C3]]
; RV64I-NEXT: [[OR4:%[0-9]+]]:_(s64) = G_OR [[OR3]], [[AND3]]
- ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 -16777216
+ ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 4278190080
; RV64I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; RV64I-NEXT: [[AND4:%[0-9]+]]:_(s64) = G_AND [[COPY]], [[C5]]
; RV64I-NEXT: [[SHL3:%[0-9]+]]:_(s64) = G_SHL [[AND4]], [[C6]](s64)
diff --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
index 809d8880faaed..0e9f571ed5175 100644
--- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
@@ -3373,6 +3373,34 @@ TEST_F(AArch64GISelMITest, LowerBSWAP) {
EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
}
+// Test lowering of scalar s64 G_BSWAP.
+TEST_F(AArch64GISelMITest, LowerBSWAPScalarS64) {
+ setUp();
+ if (!TM)
+ GTEST_SKIP();
+
+ DefineLegalizerInfo(A, {});
+
+ auto BSwap = B.buildBSwap(LLT::scalar(64), Copies[0]);
+ AInfo Info(MF->getSubtarget());
+ DummyGISelObserver Observer;
+ LegalizerHelper Helper(*MF, Info, Observer, B, &*LibcallLowering);
+ EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
+ Helper.lower(*BSwap, 0, LLT()));
+
+ // Per-byte AND masks the lowering loop emits for i = 1, 2, 3:
+ // 0x000000000000FF00 = 65280
+ // 0x0000000000FF0000 = 16711680
+ // 0x00000000FF000000 = 4278190080
+ auto CheckStr = R"(
+ CHECK-DAG: G_CONSTANT i64 65280
+ CHECK-DAG: G_CONSTANT i64 16711680
+ CHECK-DAG: G_CONSTANT i64 4278190080
+ )";
+
+ EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
+}
+
// Test lowering of G_SDIVREM into G_SDIV and G_SREM
TEST_F(AArch64GISelMITest, LowerSDIVREM) {
setUp();
|
|
@llvm/pr-subscribers-backend-risc-v Author: Ilpo Ruotsalainen (lonemeow) ChangesThe per-byte mask in where Use Fixes #199386 Full diff: https://github.com/llvm/llvm-project/pull/199387.diff 3 Files Affected:
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index a200aa29f95ca..0cf101e8420dd 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -10042,7 +10042,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::lowerBswap(MachineInstr &MI) {
// Set i-th high/low byte in Res to i-th low/high byte from Src.
for (unsigned i = 1; i < SizeInBytes / 2; ++i) {
// AND with Mask leaves byte i unchanged and sets remaining bytes to 0.
- APInt APMask(SizeInBytes * 8, 0xFF << (i * 8));
+ APInt APMask = APInt::getBitsSet(SizeInBytes * 8, i * 8, i * 8 + 8);
auto Mask = MIRBuilder.buildConstant(Ty, APMask);
auto ShiftAmt = MIRBuilder.buildConstant(Ty, BaseShiftAmt - 16 * i);
// Low byte shifted left to place of high byte: (Src & Mask) << ShiftAmt.
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir
index b5d28765889e1..be4c8e7c2a12e 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-bswap-rv64.mir
@@ -117,7 +117,7 @@ body: |
; RV64I-NEXT: [[LSHR2:%[0-9]+]]:_(s64) = G_LSHR [[COPY]], [[C4]](s64)
; RV64I-NEXT: [[AND3:%[0-9]+]]:_(s64) = G_AND [[LSHR2]], [[C3]]
; RV64I-NEXT: [[OR4:%[0-9]+]]:_(s64) = G_OR [[OR3]], [[AND3]]
- ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 -16777216
+ ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 4278190080
; RV64I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; RV64I-NEXT: [[AND4:%[0-9]+]]:_(s64) = G_AND [[COPY]], [[C5]]
; RV64I-NEXT: [[SHL3:%[0-9]+]]:_(s64) = G_SHL [[AND4]], [[C6]](s64)
diff --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
index 809d8880faaed..0e9f571ed5175 100644
--- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
@@ -3373,6 +3373,34 @@ TEST_F(AArch64GISelMITest, LowerBSWAP) {
EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
}
+// Test lowering of scalar s64 G_BSWAP.
+TEST_F(AArch64GISelMITest, LowerBSWAPScalarS64) {
+ setUp();
+ if (!TM)
+ GTEST_SKIP();
+
+ DefineLegalizerInfo(A, {});
+
+ auto BSwap = B.buildBSwap(LLT::scalar(64), Copies[0]);
+ AInfo Info(MF->getSubtarget());
+ DummyGISelObserver Observer;
+ LegalizerHelper Helper(*MF, Info, Observer, B, &*LibcallLowering);
+ EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
+ Helper.lower(*BSwap, 0, LLT()));
+
+ // Per-byte AND masks the lowering loop emits for i = 1, 2, 3:
+ // 0x000000000000FF00 = 65280
+ // 0x0000000000FF0000 = 16711680
+ // 0x00000000FF000000 = 4278190080
+ auto CheckStr = R"(
+ CHECK-DAG: G_CONSTANT i64 65280
+ CHECK-DAG: G_CONSTANT i64 16711680
+ CHECK-DAG: G_CONSTANT i64 4278190080
+ )";
+
+ EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
+}
+
// Test lowering of G_SDIVREM into G_SDIV and G_SREM
TEST_F(AArch64GISelMITest, LowerSDIVREM) {
setUp();
|
The per-byte mask in
LegalizerHelper::lowerBswapwas constructed viawhere
0xFF << (i * 8)is evaluated as a signedint. Fori*8 >= 24(byte-3 mask of an s64 G_BSWAP) the value0xFF000000does not fit in a positive 32-bitint; the conversion to signedintis implementation-defined under C++17 (UB under C++11, fully defined under C++20) and on two's-complement targets produces-16777216. The modular conversion touint64_tin theAPIntconstructor then materializes that negativeintas0xFFFFFFFFFF000000— the intended mask was0x00000000FF000000. The over-wide mask preserved bytes 4-7 of the source where only byte 3 was intended, and the spurious bytes propagated through the subsequent shift/OR chain.Use
APInt::getBitsSetto construct the mask, matching the file's other bit-range mask constructions.Fixes #199386