From 2a1dde83cc83058e85d6ec5f6620f8dcf6f8fc06 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Tue, 12 Nov 2024 12:39:37 +0000 Subject: [PATCH] ConstraintElim: teach fact-transfer about samesign When the samesign flag is present on an icmp, we can transfer all the facts on the unsigned system to the signed system, and vice-versa: we do this by specializing transferToOtherSystem when samesign is present. Supporting samesign in ConstraintElimination has necessitated adding extra information to ConditionFacts, as the information would otherwise be lost. --- .../Scalar/ConstraintElimination.cpp | 47 +-- .../transfer-samesign-facts.ll | 305 ++++++++++++++++++ 2 files changed, 332 insertions(+), 20 deletions(-) create mode 100644 llvm/test/Transforms/ConstraintElimination/transfer-samesign-facts.ll diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index 589bfd05bb5d55..86de8959595765 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -88,13 +88,12 @@ static Instruction *getContextInstForUse(Use &U) { namespace { /// Struct to express a condition of the form %Op0 Pred %Op1. struct ConditionTy { - CmpInst::Predicate Pred; - Value *Op0; - Value *Op1; + CmpPredicate Pred; + Value *Op0 = nullptr; + Value *Op1 = nullptr; - ConditionTy() - : Pred(CmpInst::BAD_ICMP_PREDICATE), Op0(nullptr), Op1(nullptr) {} - ConditionTy(CmpInst::Predicate Pred, Value *Op0, Value *Op1) + ConditionTy() = default; + ConditionTy(CmpPredicate Pred, Value *Op0, Value *Op1) : Pred(Pred), Op0(Op0), Op1(Op1) {} }; @@ -132,18 +131,17 @@ struct FactOrCheck { Ty(Ty) {} FactOrCheck(DomTreeNode *DTN, Use *U) - : U(U), DoesHold(CmpInst::BAD_ICMP_PREDICATE, nullptr, nullptr), - NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), + : U(U), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), Ty(EntryTy::UseCheck) {} - FactOrCheck(DomTreeNode *DTN, CmpInst::Predicate Pred, Value *Op0, Value *Op1, - ConditionTy Precond = ConditionTy()) + FactOrCheck(DomTreeNode *DTN, CmpPredicate Pred, Value *Op0, Value *Op1, + ConditionTy Precond = {}) : Cond(Pred, Op0, Op1), DoesHold(Precond), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), Ty(EntryTy::ConditionFact) {} - static FactOrCheck getConditionFact(DomTreeNode *DTN, CmpInst::Predicate Pred, + static FactOrCheck getConditionFact(DomTreeNode *DTN, CmpPredicate Pred, Value *Op0, Value *Op1, - ConditionTy Precond = ConditionTy()) { + ConditionTy Precond = {}) { return FactOrCheck(DTN, Pred, Op0, Op1, Precond); } @@ -1176,8 +1174,7 @@ void State::addInfoFor(BasicBlock &BB) { if (auto *Cmp = dyn_cast(Cur)) { WorkList.emplace_back(FactOrCheck::getConditionFact( DT.getNode(Successor), - IsOr ? CmpInst::getInversePredicate(Cmp->getPredicate()) - : Cmp->getPredicate(), + IsOr ? Cmp->getInverseCmpPredicate() : Cmp->getCmpPredicate(), Cmp->getOperand(0), Cmp->getOperand(1))); continue; } @@ -1201,13 +1198,12 @@ void State::addInfoFor(BasicBlock &BB) { return; if (canAddSuccessor(BB, Br->getSuccessor(0))) WorkList.emplace_back(FactOrCheck::getConditionFact( - DT.getNode(Br->getSuccessor(0)), CmpI->getPredicate(), + DT.getNode(Br->getSuccessor(0)), CmpI->getCmpPredicate(), CmpI->getOperand(0), CmpI->getOperand(1))); if (canAddSuccessor(BB, Br->getSuccessor(1))) WorkList.emplace_back(FactOrCheck::getConditionFact( - DT.getNode(Br->getSuccessor(1)), - CmpInst::getInversePredicate(CmpI->getPredicate()), CmpI->getOperand(0), - CmpI->getOperand(1))); + DT.getNode(Br->getSuccessor(1)), CmpI->getInverseCmpPredicate(), + CmpI->getOperand(0), CmpI->getOperand(1))); } #ifndef NDEBUG @@ -1806,7 +1802,7 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, continue; } - auto AddFact = [&](CmpInst::Predicate Pred, Value *A, Value *B) { + auto AddFact = [&](CmpPredicate Pred, Value *A, Value *B) { LLVM_DEBUG(dbgs() << "Processing fact to add to the system: "; dumpUnpackedICmp(dbgs(), Pred, A, B); dbgs() << "\n"); if (Info.getCS(CmpInst::isSigned(Pred)).size() > MaxRows) { @@ -1820,7 +1816,18 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, if (ReproducerModule && DFSInStack.size() > ReproducerCondStack.size()) ReproducerCondStack.emplace_back(Pred, A, B); - Info.transferToOtherSystem(Pred, A, B, CB.NumIn, CB.NumOut, DFSInStack); + if (ICmpInst::isRelational(Pred)) { + // If samesign is present on the ICmp, simply flip the sign of the + // predicate, transferring the information from the signed system to the + // unsigned system, and viceversa. + if (Pred.hasSameSign()) + Info.addFact(ICmpInst::getFlippedSignednessPredicate(Pred), A, B, + CB.NumIn, CB.NumOut, DFSInStack); + else + Info.transferToOtherSystem(Pred, A, B, CB.NumIn, CB.NumOut, + DFSInStack); + } + if (ReproducerModule && DFSInStack.size() > ReproducerCondStack.size()) { // Add dummy entries to ReproducerCondStack to keep it in sync with // DFSInStack. diff --git a/llvm/test/Transforms/ConstraintElimination/transfer-samesign-facts.ll b/llvm/test/Transforms/ConstraintElimination/transfer-samesign-facts.ll new file mode 100644 index 00000000000000..1b155e050de29e --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/transfer-samesign-facts.ll @@ -0,0 +1,305 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +define i1 @idx_known_positive_via_len_1(i8 %len, i8 %idx) { +; CHECK-LABEL: @idx_known_positive_via_len_1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_ULT_LEN:%.*]] = icmp samesign ult i8 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: [[AND_1:%.*]] = and i1 true, [[IDX_ULT_LEN]] +; CHECK-NEXT: br i1 [[AND_1]], label [[THEN_1:%.*]], label [[ELSE:%.*]] +; CHECK: then.1: +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, true +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[IDX]], 1 +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], [[C_1]] +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[LEN]], 1 +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], [[C_2]] +; CHECK-NEXT: ret i1 [[R_3]] +; CHECK: else: +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[IDX]], 0 +; CHECK-NEXT: ret i1 [[C_3]] +; +entry: + %len.pos = icmp samesign uge i8 %len, 0 + %idx.ult.len = icmp samesign ult i8 %idx, %len + %and.1 = and i1 %len.pos, %idx.ult.len + br i1 %and.1, label %then.1, label %else + +then.1: + %t.1 = icmp slt i8 %idx, %len + %t.2 = icmp samesign uge i8 %idx, 0 + %r.1 = xor i1 %t.1, %t.2 + + %c.1 = icmp sge i8 %idx, 1 + %r.2 = xor i1 %r.1, %c.1 + + %c.2 = icmp sge i8 %len, 1 + %r.3 = xor i1 %r.2, %c.2 + ret i1 %r.3 + +else: + %c.3 = icmp sge i8 %idx, 0 + ret i1 %c.3 +} + +; Like @idx_known_positive_via_len_1, but with a different order of known facts. +define i1 @idx_known_positive_via_len_2(i8 %len, i8 %idx) { +; CHECK-LABEL: @idx_known_positive_via_len_2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_ULT_LEN:%.*]] = icmp samesign ult i8 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: [[AND_1:%.*]] = and i1 true, [[IDX_ULT_LEN]] +; CHECK-NEXT: br i1 [[AND_1]], label [[THEN_1:%.*]], label [[ELSE:%.*]] +; CHECK: then.1: +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, true +; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[IDX]], 1 +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], [[C_1]] +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[LEN]], 1 +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], [[C_2]] +; CHECK-NEXT: ret i1 [[R_3]] +; CHECK: else: +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[IDX]], 0 +; CHECK-NEXT: ret i1 [[C_3]] +; +entry: + %idx.ult.len = icmp samesign ult i8 %idx, %len + %len.pos = icmp samesign uge i8 %len, 0 + %and.1 = and i1 %len.pos, %idx.ult.len + br i1 %and.1, label %then.1, label %else + +then.1: + %t.1 = icmp slt i8 %idx, %len + %t.2 = icmp samesign uge i8 %idx, 0 + %r.1 = xor i1 %t.1, %t.2 + + %c.1 = icmp sge i8 %idx, 1 + %r.2 = xor i1 %r.1, %c.1 + + %c.2 = icmp sge i8 %len, 1 + %r.3 = xor i1 %r.2, %c.2 + ret i1 %r.3 + +else: + %c.3 = icmp sge i8 %idx, 0 + ret i1 %c.3 +} + + +define i1 @idx_not_known_positive_via_len_uge(i8 %len, i8 %idx) { +; CHECK-LABEL: @idx_not_known_positive_via_len_uge( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_ULT_LEN:%.*]] = icmp samesign ult i8 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[IDX_ULT_LEN]], label [[THEN_1:%.*]], label [[ELSE:%.*]] +; CHECK: then.1: +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[IDX]], 0 +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, [[C_2]] +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[IDX]], 1 +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], [[C_3]] +; CHECK-NEXT: [[C_4:%.*]] = icmp sge i8 [[LEN]], 1 +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], [[C_4]] +; CHECK-NEXT: ret i1 [[R_3]] +; CHECK: else: +; CHECK-NEXT: [[C_5:%.*]] = icmp sge i8 [[IDX]], 0 +; CHECK-NEXT: ret i1 [[C_5]] +; +entry: + %idx.ult.len = icmp samesign ult i8 %idx, %len + br i1 %idx.ult.len, label %then.1, label %else + +then.1: + %c.1 = icmp slt i8 %idx, %len + %c.2 = icmp sge i8 %idx, 0 + %r.1 = xor i1 %c.1, %c.2 + + %c.3 = icmp sge i8 %idx, 1 + %r.2 = xor i1 %r.1, %c.3 + + %c.4 = icmp sge i8 %len, 1 + %r.3 = xor i1 %r.2, %c.4 + ret i1 %r.3 + +else: + %c.5 = icmp sge i8 %idx, 0 + ret i1 %c.5 +} + +define i1 @idx_not_known_positive_via_len(i8 %len, i8 %idx) { +; CHECK-LABEL: @idx_not_known_positive_via_len( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IDX_ULT_LEN:%.*]] = icmp samesign ult i8 [[IDX:%.*]], [[LEN:%.*]] +; CHECK-NEXT: br i1 [[IDX_ULT_LEN]], label [[THEN_1:%.*]], label [[ELSE:%.*]] +; CHECK: then.1: +; CHECK-NEXT: [[C_2:%.*]] = icmp sge i8 [[IDX]], 0 +; CHECK-NEXT: [[R_1:%.*]] = xor i1 true, [[C_2]] +; CHECK-NEXT: [[C_3:%.*]] = icmp sge i8 [[IDX]], 1 +; CHECK-NEXT: [[R_2:%.*]] = xor i1 [[R_1]], [[C_3]] +; CHECK-NEXT: [[C_4:%.*]] = icmp sge i8 [[LEN]], 1 +; CHECK-NEXT: [[R_3:%.*]] = xor i1 [[R_2]], [[C_4]] +; CHECK-NEXT: ret i1 [[R_3]] +; CHECK: else: +; CHECK-NEXT: [[C_5:%.*]] = icmp sge i8 [[IDX]], 0 +; CHECK-NEXT: ret i1 [[C_5]] +; +entry: + %idx.ult.len = icmp samesign ult i8 %idx, %len + br i1 %idx.ult.len, label %then.1, label %else + +then.1: + %c.1 = icmp slt i8 %idx, %len + %c.2 = icmp sge i8 %idx, 0 + %r.1 = xor i1 %c.1, %c.2 + + %c.3 = icmp sge i8 %idx, 1 + %r.2 = xor i1 %r.1, %c.3 + + %c.4 = icmp sge i8 %len, 1 + %r.3 = xor i1 %r.2, %c.4 + ret i1 %r.3 + +else: + %c.5 = icmp sge i8 %idx, 0 + ret i1 %c.5 +} + +define i1 @ult_signed_pos_constant(i8 %a) { +; CHECK-LABEL: @ult_signed_pos_constant( +; CHECK-NEXT: [[A_ULT_4:%.*]] = icmp samesign ult i8 [[A:%.*]], 4 +; CHECK-NEXT: br i1 [[A_ULT_4]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[RES_1:%.*]] = icmp sge i8 [[A]], 0 +; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], true +; CHECK-NEXT: [[RES_5:%.*]] = xor i1 [[RES_2]], true +; CHECK-NEXT: ret i1 [[RES_5]] +; CHECK: else: +; CHECK-NEXT: [[RES_3:%.*]] = xor i1 true, false +; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[A]], 5 +; CHECK-NEXT: [[RES_4:%.*]] = xor i1 [[RES_3]], [[C_4]] +; CHECK-NEXT: ret i1 [[RES_4]] +; + %a.ult.4 = icmp samesign ult i8 %a, 4 + br i1 %a.ult.4, label %then, label %else + +then: + %t.0 = icmp sge i8 %a, 0 + %t.1 = icmp slt i8 %a, 4 + %res.1 = xor i1 %t.0, %t.1 + + %c.0 = icmp slt i8 %a, 5 + %res.2 = xor i1 %res.1, %c.0 + ret i1 %res.2 + +else: + %c.2 = icmp sge i8 %a, 0 + %c.3 = icmp slt i8 %a, 4 + %res.3 = xor i1 %c.2, %c.3 + + %c.4 = icmp slt i8 %a, 5 + %res.4 = xor i1 %res.3, %c.4 + + ret i1 %res.4 +} + +define i1 @ult_signed_neg_constant(i8 %a) { +; CHECK-LABEL: @ult_signed_neg_constant( +; CHECK-NEXT: [[A_ULT_4:%.*]] = icmp samesign ult i8 [[A:%.*]], -2 +; CHECK-NEXT: br i1 [[A_ULT_4]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[RES_1:%.*]] = xor i1 false, true +; CHECK-NEXT: ret i1 [[RES_1]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; + %a.ult.4 = icmp samesign ult i8 %a, -2 + br i1 %a.ult.4, label %then, label %else + +then: + %c.0 = icmp sge i8 %a, 0 + %c.1 = icmp slt i8 %a, -2 + %res.1 = xor i1 %c.0, %c.1 + ret i1 %res.1 + +else: + ret i1 0 +} + +define i1 @ule_signed_pos_constant_1(i8 %a, i8 %b) { +; CHECK-LABEL: @ule_signed_pos_constant_1( +; CHECK-NEXT: [[A_ULE_B:%.*]] = icmp samesign ule i8 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: call void @llvm.assume(i1 [[A_ULE_B]]) +; CHECK-NEXT: [[SLT_TEST:%.*]] = icmp slt i8 [[A]], [[B]] +; CHECK-NEXT: [[RESULT_XOR:%.*]] = xor i1 true, [[SLT_TEST]] +; CHECK-NEXT: ret i1 [[RESULT_XOR]] +; + %a_ule_b = icmp samesign ule i8 %a, %b + call void @llvm.assume(i1 %a_ule_b) + + %sle_test = icmp sle i8 %a, %b + %slt_test = icmp slt i8 %a, %b + %result_xor = xor i1 %sle_test, %slt_test + + ret i1 %result_xor +} + +define i1 @ule_signed_pos_constant_2(i8 %a) { +; CHECK-LABEL: @ule_signed_pos_constant_2( +; CHECK-NEXT: [[A_ULT_4:%.*]] = icmp samesign ule i8 [[A:%.*]], 4 +; CHECK-NEXT: br i1 [[A_ULT_4]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[RES_1:%.*]] = icmp sge i8 [[A]], 0 +; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], true +; CHECK-NEXT: [[RES_5:%.*]] = xor i1 [[RES_2]], true +; CHECK-NEXT: ret i1 [[RES_5]] +; CHECK: else: +; CHECK-NEXT: [[RES_3:%.*]] = xor i1 true, false +; CHECK-NEXT: [[C_4:%.*]] = icmp sle i8 [[A]], 5 +; CHECK-NEXT: [[RES_4:%.*]] = xor i1 [[RES_3]], [[C_4]] +; CHECK-NEXT: ret i1 [[RES_4]] +; + %a.ult.4 = icmp samesign ule i8 %a, 4 + br i1 %a.ult.4, label %then, label %else + +then: + %t.0 = icmp sge i8 %a, 0 + %t.1 = icmp sle i8 %a, 4 + %res.1 = xor i1 %t.0, %t.1 + + %c.0 = icmp sle i8 %a, 5 + %res.2 = xor i1 %res.1, %c.0 + ret i1 %res.2 + +else: + %c.2 = icmp sge i8 %a, 0 + %c.3 = icmp sle i8 %a, 4 + %res.3 = xor i1 %c.2, %c.3 + + %c.4 = icmp sle i8 %a, 5 + %res.4 = xor i1 %res.3, %c.4 + + ret i1 %res.4 +} + +define i1 @uge_assumed_positive_values(i8 %a, i8 %b) { +; CHECK-LABEL: @uge_assumed_positive_values( +; CHECK-NEXT: [[A_UGT_B:%.*]] = icmp samesign uge i8 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: call void @llvm.assume(i1 [[A_UGT_B]]) +; CHECK-NEXT: ret i1 true +; + %a_ugt_b = icmp samesign uge i8 %a, %b + call void @llvm.assume(i1 %a_ugt_b) + + %result = icmp sge i8 %a, %b + + ret i1 %result +} + +define i1 @ugt_assumed_positive_values(i8 %a, i8 %b) { +; CHECK-LABEL: @ugt_assumed_positive_values( +; CHECK-NEXT: [[A_UGT_B:%.*]] = icmp samesign ugt i8 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: call void @llvm.assume(i1 [[A_UGT_B]]) +; CHECK-NEXT: ret i1 true +; + %a_ugt_b = icmp samesign ugt i8 %a, %b + call void @llvm.assume(i1 %a_ugt_b) + + %result = icmp sgt i8 %a, %b + + ret i1 %result +}