switch (II->getIntrinsicID()) {
// TODO: Add more intrinsics.
case Intrinsic::ctpop:
+ case Intrinsic::sadd_with_overflow:
+ case Intrinsic::ssub_with_overflow:
+ case Intrinsic::smul_with_overflow:
+ case Intrinsic::uadd_with_overflow:
+ case Intrinsic::usub_with_overflow:
+ case Intrinsic::umul_with_overflow:
return false;
}
}
case Instruction::Freeze:
case Instruction::Select:
case Instruction::PHI:
- case Instruction::Call:
case Instruction::Invoke:
return false;
+ case Instruction::Call:
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+ switch (II->getIntrinsicID()) {
+ // TODO: Add more intrinsics.
+ case Intrinsic::sadd_with_overflow:
+ case Intrinsic::ssub_with_overflow:
+ case Intrinsic::smul_with_overflow:
+ case Intrinsic::uadd_with_overflow:
+ case Intrinsic::usub_with_overflow:
+ case Intrinsic::umul_with_overflow:
+ // If an input is a vector containing a poison element, the
+ // two output vectors (calculated results, overflow bits)'
+ // corresponding lanes are poison.
+ return true;
+ }
+ }
+ return false;
case Instruction::ICmp:
case Instruction::FCmp:
case Instruction::GetElementPtr:
}
TEST(ValueTracking, propagatesPoison) {
- std::string AsmHead = "declare i32 @g(i32)\n"
- "define void @f(i32 %x, i32 %y, float %fx, float %fy, "
- "i1 %cond, i8* %p) {\n";
+ std::string AsmHead =
+ "declare i32 @g(i32)\n"
+ "declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)\n"
+ "define void @f(i32 %x, i32 %y, float %fx, float %fy, "
+ "i1 %cond, i8* %p) {\n";
std::string AsmTail = " ret void\n}";
// (propagates poison?, IR instruction)
SmallVector<std::pair<bool, std::string>, 32> Data = {
{true, "urem i32 %x, %y"},
{true, "sdiv exact i32 %x, %y"},
{true, "srem i32 %x, %y"},
- {false, "call i32 @g(i32 %x)"}};
+ {false, "call i32 @g(i32 %x)"},
+ {true, "call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)"},
+ {true, "call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)"},
+ {true, "call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %y)"},
+ {true, "call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)"},
+ {true, "call {i32, i1} @llvm.usub.with.overflow.i32(i32 %x, i32 %y)"},
+ {true, "call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %y)"}};
std::string AssemblyStr = AsmHead;
for (auto &Itm : Data)
std::string AsmHead =
"@s = external dso_local global i32, align 1\n"
"declare i32 @g(i32)\n"
+ "declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)\n"
+ "declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)\n"
"define void @f(i32 %x, i32 %y, float %fx, float %fy, i1 %cond, "
"<4 x i32> %vx, <4 x i32> %vx2, <vscale x 4 x i32> %svx, i8* %p) {\n";
std::string AsmTail = " ret void\n}";
{{true, false},
"ashr <4 x i32> %vx, select (i1 icmp sgt (i32 ptrtoint (i32* @s to "
"i32), i32 1), <4 x i32> zeroinitializer, <4 x i32> <i32 0, i32 1, i32 "
- "2, i32 3>)"}};
+ "2, i32 3>)"},
+ {{false, false},
+ "call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)"},
+ {{false, false},
+ "call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)"},
+ {{false, false},
+ "call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %y)"},
+ {{false, false},
+ "call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)"},
+ {{false, false},
+ "call {i32, i1} @llvm.usub.with.overflow.i32(i32 %x, i32 %y)"},
+ {{false, false},
+ "call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %y)"}};
std::string AssemblyStr = AsmHead;
for (auto &Itm : Data)