Index: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -429,6 +429,17 @@ !OtherOpF->getType()->isVectorTy())) return nullptr; + // It may not be safe to sink div/rem after a select. If the select condition + // is poison, then the select itself is poison and div/rem may induce + // immediate UB with a poison operand. + // For example, the following transform is not safe if Cond can ever be poison + // because we can replace poison with zero and then we have div-by-zero that + // didn't exist in the original code: + // Cond ? x/y : x/z --> x / (Cond ? y : z) + auto *BO = dyn_cast(TI); + if (BO && BO->isIntDivRem() && !isGuaranteedNotToBePoison(Cond)) + return nullptr; + // If we reach here, they do have operations in common. Value *NewSI = Builder.CreateSelect(Cond, OtherOpT, OtherOpF, SI.getName() + ".v", &SI); Index: llvm/test/Transforms/InstCombine/select-divrem.ll =================================================================== --- llvm/test/Transforms/InstCombine/select-divrem.ll +++ llvm/test/Transforms/InstCombine/select-divrem.ll @@ -3,8 +3,9 @@ define i5 @sdiv_common_divisor(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @sdiv_common_divisor( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = sdiv i5 [[SEL_V]], [[X:%.*]] +; CHECK-NEXT: [[R1:%.*]] = sdiv i5 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[R2:%.*]] = sdiv i5 [[Z:%.*]], [[X]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = sdiv i5 %y, %x @@ -15,8 +16,9 @@ define i5 @srem_common_divisor(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @srem_common_divisor( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = srem i5 [[SEL_V]], [[X:%.*]] +; CHECK-NEXT: [[R1:%.*]] = srem i5 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[R2:%.*]] = srem i5 [[Z:%.*]], [[X]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = srem i5 %y, %x @@ -27,8 +29,9 @@ define i5 @udiv_common_divisor(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @udiv_common_divisor( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = udiv i5 [[SEL_V]], [[X:%.*]] +; CHECK-NEXT: [[R1:%.*]] = udiv i5 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[R2:%.*]] = udiv i5 [[Z:%.*]], [[X]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = udiv i5 %y, %x @@ -39,8 +42,9 @@ define i5 @urem_common_divisor(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @urem_common_divisor( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = urem i5 [[SEL_V]], [[X:%.*]] +; CHECK-NEXT: [[R1:%.*]] = urem i5 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[R2:%.*]] = urem i5 [[Z:%.*]], [[X]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = urem i5 %y, %x @@ -51,8 +55,9 @@ define i5 @sdiv_common_dividend(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @sdiv_common_dividend( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = sdiv i5 [[X:%.*]], [[SEL_V]] +; CHECK-NEXT: [[R1:%.*]] = sdiv i5 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R2:%.*]] = sdiv i5 [[X]], [[Z:%.*]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = sdiv i5 %x, %y @@ -63,8 +68,9 @@ define i5 @srem_common_dividend(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @srem_common_dividend( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = srem i5 [[X:%.*]], [[SEL_V]] +; CHECK-NEXT: [[R1:%.*]] = srem i5 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R2:%.*]] = srem i5 [[X]], [[Z:%.*]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = srem i5 %x, %y @@ -75,8 +81,9 @@ define i5 @udiv_common_dividend(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @udiv_common_dividend( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = udiv i5 [[X:%.*]], [[SEL_V]] +; CHECK-NEXT: [[R1:%.*]] = udiv i5 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R2:%.*]] = udiv i5 [[X]], [[Z:%.*]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = udiv i5 %x, %y @@ -87,8 +94,9 @@ define i5 @urem_common_dividend(i1 %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @urem_common_dividend( -; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = urem i5 [[X:%.*]], [[SEL_V]] +; CHECK-NEXT: [[R1:%.*]] = urem i5 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R2:%.*]] = urem i5 [[X]], [[Z:%.*]] +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i5 [[R2]], i5 [[R1]] ; CHECK-NEXT: ret i5 [[SEL]] ; %r1 = urem i5 %x, %y @@ -97,6 +105,10 @@ ret i5 %sel } +; Repeat the above tests, but guarantee that the select +; condition is not poison via argument attribute. That +; makes it safe to execute the select before div/rem. + define i5 @sdiv_common_divisor_defined_cond(i1 noundef %b, i5 %x, i5 %y, i5 %z) { ; CHECK-LABEL: @sdiv_common_divisor_defined_cond( ; CHECK-NEXT: [[SEL_V:%.*]] = select i1 [[B:%.*]], i5 [[Z:%.*]], i5 [[Y:%.*]]