https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/218685
>From f163ff6ed42ef45b7a54560b00184e643fdeea57 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Mon, 17 Aug 2026 16:48:02 +0100 Subject: [PATCH 1/5] [ConstraintElim] Precommit tests for flags strengthening --- .../materialize-flags-narrow-pointer-index.ll | 208 ++++++++ .../materialize-flags.ll | 457 ++++++++++++++++++ ...onstraint-elimination-materialize-flags.ll | 262 ++++++++++ 3 files changed, 927 insertions(+) create mode 100644 llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll create mode 100644 llvm/test/Transforms/ConstraintElimination/materialize-flags.ll create mode 100644 llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll new file mode 100644 index 0000000000000..bcdf22c109743 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll @@ -0,0 +1,208 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +; The offsets of a GEP are computed in the pointer index type. With a 16 bit +; index type, a stride or struct member offset that does not fit in 15 bits is +; negative there, so a non-negative index does not imply a non-negative offset +; and nuw must not be added. + +target datalayout = "p:16:16" + +%S.big = type { [40000 x i8], i8 } +%S.small = type { i16, i8 } + +; The stride 40000 is negative as an i16, so the offset for a positive index is +; negative and the GEP may wrap in the unsigned sense. +define ptr @gep_no_nuw_stride_negative_in_index_type(ptr %p, i16 %i, i16 %j) { +; CHECK-LABEL: define ptr @gep_no_nuw_stride_negative_in_index_type( +; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [40000 x i8], ptr [[P]], i16 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i16 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i16 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw [40000 x i8], ptr %p, i16 %i + ret ptr %gep + +exit: + ret ptr null +} + +; Same for the offset of a struct member, which is 40000 for the second field. +define ptr @gep_no_nuw_struct_offset_negative_in_index_type(ptr %p, i16 %i, i16 %j) { +; CHECK-LABEL: define ptr @gep_no_nuw_struct_offset_negative_in_index_type( +; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [[S_BIG:%.*]], ptr [[P]], i16 [[I]], i32 1 +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i16 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i16 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw %S.big, ptr %p, i16 %i, i32 1 + ret ptr %gep + +exit: + ret ptr null +} + +; The stride of the outer array is 160000, which is 28928 and thus non-negative +; as an i16, but the stride of the inner array is negative. +define ptr @gep_no_nuw_inner_stride_negative_in_index_type(ptr %p, i16 %i, i16 %j) { +; CHECK-LABEL: define ptr @gep_no_nuw_inner_stride_negative_in_index_type( +; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [4 x [40000 x i8]], ptr [[P]], i16 [[I]], i16 [[J]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i16 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i16 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw [4 x [40000 x i8]], ptr %p, i16 %i, i16 %j + ret ptr %gep + +exit: + ret ptr null +} + +; The stride fits in the index type, so nuw can be added. +define ptr @gep_nuw_stride_fits_index_type(ptr %p, i16 %i, i16 %j) { +; CHECK-LABEL: define ptr @gep_nuw_stride_fits_index_type( +; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i16, ptr [[P]], i16 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i16 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i16 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw i16, ptr %p, i16 %i + ret ptr %gep + +exit: + ret ptr null +} + +; The struct member offset fits in the index type, so nuw can be added. +define ptr @gep_nuw_struct_offset_fits_index_type(ptr %p, i16 %i, i16 %j) { +; CHECK-LABEL: define ptr @gep_nuw_struct_offset_fits_index_type( +; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [[S_SMALL:%.*]], ptr [[P]], i16 [[I]], i32 1 +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i16 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i16 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw %S.small, ptr %p, i16 %i, i32 1 + ret ptr %gep + +exit: + ret ptr null +} + +; An index wider than the index type is truncated. nusw guarantees the +; truncation preserves the signed value, which together with the index being +; non-negative means it also preserves the unsigned value. +define ptr @gep_nuw_index_wider_than_index_type(ptr %p, i64 %i, i64 %j) { +; CHECK-LABEL: define ptr @gep_nuw_index_wider_than_index_type( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i16, ptr [[P]], i64 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i64 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i64 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw i16, ptr %p, i64 %i + ret ptr %gep + +exit: + ret ptr null +} diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll new file mode 100644 index 0000000000000..6dfa70e044638 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll @@ -0,0 +1,457 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +define i64 @sext_to_zext_nneg(i32 %n, i32 %m) { +; CHECK-LABEL: define i64 @sext_to_zext_nneg( +; CHECK-SAME: i32 [[N:%.*]], i32 [[M:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[THEN:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[N]], [[M]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret i64 0 +; +entry: + %c = icmp slt i32 %n, %m + br i1 %c, label %exit, label %then + +then: + %sub = sub nsw i32 %n, %m + %ext = sext i32 %sub to i64 + ret i64 %ext + +exit: + ret i64 0 +} + +define i64 @sext_to_zext_nneg_negated_condition(i32 %n, i32 %m) { +; CHECK-LABEL: define i64 @sext_to_zext_nneg_negated_condition( +; CHECK-SAME: i32 [[N:%.*]], i32 [[M:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[N]], [[M]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret i64 0 +; +entry: + %c = icmp slt i32 %n, %m + br i1 %c, label %then, label %exit + +then: + %sub = sub nsw i32 %n, %m + %ext = sext i32 %sub to i64 + ret i64 %ext + +exit: + ret i64 0 +} + +define i64 @sext_no_conversion_known_via_value_tracking(i32 %n) { +; CHECK-LABEL: define i64 @sext_no_conversion_known_via_value_tracking( +; CHECK-SAME: i32 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[AND:%.*]] = and i32 [[N]], 255 +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[AND]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; +entry: + %and = and i32 %n, 255 + %ext = sext i32 %and to i64 + ret i64 %ext +} + +define i32 @sub_nuw_relational(i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @sub_nuw_relational( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C:%.*]] = icmp uge i32 [[A]], [[B]] +; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A]], [[B]] +; CHECK-NEXT: ret i32 [[SUB]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret i32 0 +; +entry: + %c = icmp uge i32 %a, %b + br i1 %c, label %then, label %exit + +then: + %sub = sub i32 %a, %b + ret i32 %sub + +exit: + ret i32 0 +} + +define i32 @sub_nuw_transitive(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: define i32 @sub_nuw_transitive( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp uge i32 [[A]], [[B]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[B]], [[C]] +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A]], [[C]] +; CHECK-NEXT: ret i32 [[SUB]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret i32 0 +; +entry: + %c.0 = icmp uge i32 %a, %b + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp uge i32 %b, %c + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %sub = sub i32 %a, %c + ret i32 %sub + +exit: + ret i32 0 +} + +; A signed fact does not imply the unsigned no-wrap flag. +define i32 @sub_no_nuw_signed_fact(i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @sub_no_nuw_signed_fact( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C:%.*]] = icmp sge i32 [[A]], [[B]] +; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A]], [[B]] +; CHECK-NEXT: ret i32 [[SUB]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret i32 0 +; +entry: + %c = icmp sge i32 %a, %b + br i1 %c, label %then, label %exit + +then: + %sub = sub i32 %a, %b + ret i32 %sub + +exit: + ret i32 0 +} + +define ptr @gep_nuw_transitive(ptr %p, i64 %i, i64 %j) { +; CHECK-LABEL: define ptr @gep_nuw_transitive( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i64 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i64 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw i32, ptr %p, i64 %i + ret ptr %gep + +exit: + ret ptr null +} + +define ptr @gep_no_nuw_unknown_index(ptr %p, i64 %i, i64 %n) { +; CHECK-LABEL: define ptr @gep_no_nuw_unknown_index( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C:%.*]] = icmp sge i64 [[I]], [[N]] +; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[THEN:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c = icmp sge i64 %i, %n + br i1 %c, label %exit, label %then + +then: + %gep = getelementptr nusw i32, ptr %p, i64 %i + ret ptr %gep + +exit: + ret ptr null +} + +; The negative constant index means the GEP wraps in the unsigned sense. +define ptr @gep_no_nuw_negative_constant_index(ptr %p, i64 %i, i64 %j) { +; CHECK-LABEL: define ptr @gep_no_nuw_negative_constant_index( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[I]] +; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr nusw i32, ptr [[GEP]], i64 -1 +; CHECK-NEXT: ret ptr [[GEP_2]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i64 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i64 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw i32, ptr %p, i64 %i + %gep.2 = getelementptr nusw i32, ptr %gep, i64 -1 + ret ptr %gep.2 + +exit: + ret ptr null +} + +; Without nusw, nuw cannot be implied by non-negative offsets. +define ptr @gep_no_nusw(ptr %p, i64 %i, i64 %j) { +; CHECK-LABEL: define ptr @gep_no_nusw( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[P]], i64 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i64 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i64 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr i32, ptr %p, i64 %i + ret ptr %gep + +exit: + ret ptr null +} + +define ptr @gep_no_nuw_constant_index(ptr %p) { +; CHECK-LABEL: define ptr @gep_no_nuw_constant_index( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C:%.*]] = icmp ne ptr [[P]], null +; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 3 +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c = icmp ne ptr %p, null + br i1 %c, label %then, label %exit + +then: + %gep = getelementptr nusw i32, ptr %p, i64 3 + ret ptr %gep + +exit: + ret ptr null +} + +define i64 @no_facts(i32 %n, i32 %m, ptr %p) { +; CHECK-LABEL: define i64 @no_facts( +; CHECK-SAME: i32 [[N:%.*]], i32 [[M:%.*]], ptr [[P:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[N]], [[M]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: [[SUB_2:%.*]] = sub i32 [[N]], [[M]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i32 [[SUB_2]] +; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 +; CHECK-NEXT: ret i64 [[EXT]] +; +entry: + %sub = sub nsw i32 %n, %m + %ext = sext i32 %sub to i64 + %sub.2 = sub i32 %n, %m + %gep = getelementptr nusw i32, ptr %p, i32 %sub.2 + store i32 0, ptr %gep, align 4 + ret i64 %ext +} + +define void @induction_start_bound(ptr %p, i32 %start, i32 %n) { +; CHECK-LABEL: define void @induction_start_bound( +; CHECK-SAME: ptr [[P:%.*]], i32 [[START:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ] +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[IV]], [[START]] +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[IDX]] +; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 +; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1 +; CHECK-NEXT: [[EC:%.*]] = icmp slt i32 [[IV_NEXT]], [[N]] +; CHECK-NEXT: br i1 [[EC]], label %[[LOOP]], label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: + %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop ] + %sub = sub nsw i32 %iv, %start + %idx = sext i32 %sub to i64 + %gep = getelementptr nusw i32, ptr %p, i64 %idx + store i32 0, ptr %gep, align 4 + %iv.next = add nsw i32 %iv, 1 + %ec = icmp slt i32 %iv.next, %n + br i1 %ec, label %loop, label %exit + +exit: + ret void +} + +define void @induction_start_bound_ne_latch(ptr %p, i32 %start, i32 %n) { +; CHECK-LABEL: define void @induction_start_bound_ne_latch( +; CHECK-SAME: ptr [[P:%.*]], i32 [[START:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[LOOP_HEADER:.*]] +; CHECK: [[LOOP_HEADER]]: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ] +; CHECK-NEXT: [[EC:%.*]] = icmp ne i32 [[IV]], [[N]] +; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_BODY:.*]], label %[[EXIT:.*]] +; CHECK: [[LOOP_BODY]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[IV]], [[START]] +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[IDX]] +; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 +; CHECK-NEXT: br label %[[LOOP_LATCH]] +; CHECK: [[LOOP_LATCH]]: +; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1 +; CHECK-NEXT: br label %[[LOOP_HEADER]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + br label %loop.header + +loop.header: + %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop.latch ] + %ec = icmp ne i32 %iv, %n + br i1 %ec, label %loop.body, label %exit + +loop.body: + %sub = sub nsw i32 %iv, %start + %idx = sext i32 %sub to i64 + %gep = getelementptr nusw i32, ptr %p, i64 %idx + store i32 0, ptr %gep, align 4 + br label %loop.latch + +loop.latch: + %iv.next = add nsw i32 %iv, 1 + br label %loop.header + +exit: + ret void +} + +; The stride 2^63 is negative when interpreted in the pointer index type, so a +; non-negative index does not imply a non-negative offset and nuw must not be +; added. +define ptr @gep_no_nuw_stride_negative_in_index_type(ptr %p, i64 %i, i64 %j) { +; CHECK-LABEL: define ptr @gep_no_nuw_stride_negative_in_index_type( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [9223372036854775808 x i8], ptr [[P]], i64 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i64 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i64 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw [9223372036854775808 x i8], ptr %p, i64 %i + ret ptr %gep + +exit: + ret ptr null +} + +; The sign of a scalable stride in the pointer index type is not known. +define ptr @gep_no_nuw_scalable_stride(ptr %p, i64 %i, i64 %j) { +; CHECK-LABEL: define ptr @gep_no_nuw_scalable_stride( +; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]] +; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]] +; CHECK: [[BB_1]]: +; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0 +; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] +; CHECK: [[BB_2]]: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw <vscale x 2 x i8>, ptr [[P]], i64 [[I]] +; CHECK-NEXT: ret ptr [[GEP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret ptr null +; +entry: + %c.0 = icmp sgt i64 %i, %j + br i1 %c.0, label %bb.1, label %exit + +bb.1: + %c.1 = icmp sgt i64 %j, 0 + br i1 %c.1, label %bb.2, label %exit + +bb.2: + %gep = getelementptr nusw <vscale x 2 x i8>, ptr %p, i64 %i + ret ptr %gep + +exit: + ret ptr null +} diff --git a/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll b/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll new file mode 100644 index 0000000000000..b94b0e6a384df --- /dev/null +++ b/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll @@ -0,0 +1,262 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6 +; RUN: opt -passes='default<O3>' -S %s | FileCheck %s + +define void @index_mask_removed1(ptr %A, i16 %start, i16 %v, i16 %n) { +; CHECK-LABEL: define void @index_mask_removed1( +; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i16 [[START:%.*]], i16 [[V:%.*]], i16 [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PRE:%.*]] = icmp slt i16 [[START]], [[V]] +; CHECK-NEXT: [[EC_NOT1:%.*]] = icmp eq i16 [[START]], [[N]] +; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[PRE]], i1 true, i1 [[EC_NOT1]] +; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[LOOP_BODY_PREHEADER:.*]] +; CHECK: [[LOOP_BODY_PREHEADER]]: +; CHECK-NEXT: [[TMP0:%.*]] = xor i16 [[START]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[N]], [[TMP0]] +; CHECK-NEXT: [[TMP2:%.*]] = zext i16 [[TMP1]] to i32 +; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i32 [[TMP2]], 1 +; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i16 [[TMP1]], 11 +; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[LOOP_BODY_PREHEADER3:.*]], label %[[VECTOR_SCEVCHECK:.*]] +; CHECK: [[VECTOR_SCEVCHECK]]: +; CHECK-NEXT: [[TMP4:%.*]] = xor i16 [[START]], -1 +; CHECK-NEXT: [[TMP5:%.*]] = add i16 [[N]], [[TMP4]] +; CHECK-NEXT: [[TMP6:%.*]] = sext i16 [[V]] to i32 +; CHECK-NEXT: [[TMP7:%.*]] = zext i16 [[TMP5]] to i32 +; CHECK-NEXT: [[TMP8:%.*]] = xor i16 [[START]], -1 +; CHECK-NEXT: [[TMP9:%.*]] = sext i16 [[TMP8]] to i32 +; CHECK-NEXT: [[TMP10:%.*]] = add nsw i32 [[TMP9]], [[TMP6]] +; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 [[TMP10]], [[TMP7]] +; CHECK-NEXT: br i1 [[TMP11]], label %[[LOOP_BODY_PREHEADER3]], label %[[VECTOR_PH:.*]] +; CHECK: [[VECTOR_PH]]: +; CHECK-NEXT: [[N_VEC:%.*]] = and i32 [[TMP3]], 131068 +; CHECK-NEXT: [[TMP12:%.*]] = trunc i32 [[N_VEC]] to i16 +; CHECK-NEXT: [[TMP13:%.*]] = add i16 [[START]], [[TMP12]] +; CHECK-NEXT: [[INVARIANT_OP:%.*]] = sub i16 [[START]], [[V]] +; CHECK-NEXT: br label %[[VECTOR_BODY:.*]] +; CHECK: [[VECTOR_BODY]]: +; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ] +; CHECK-NEXT: [[TMP14:%.*]] = trunc i32 [[INDEX]] to i16 +; CHECK-NEXT: [[DOTREASS:%.*]] = add i16 [[TMP14]], [[INVARIANT_OP]] +; CHECK-NEXT: [[TMP15:%.*]] = sext i16 [[DOTREASS]] to i64 +; CHECK-NEXT: [[TMP16:%.*]] = and i64 [[TMP15]], 4294967295 +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP16]] +; CHECK-NEXT: store <4 x i8> splat (i8 1), ptr [[TMP17]], align 1 +; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 4 +; CHECK-NEXT: [[TMP18:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] +; CHECK-NEXT: br i1 [[TMP18]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]] +; CHECK: [[MIDDLE_BLOCK]]: +; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP3]], [[N_VEC]] +; CHECK-NEXT: br i1 [[CMP_N]], label %[[EXIT]], label %[[LOOP_BODY_PREHEADER3]] +; CHECK: [[LOOP_BODY_PREHEADER3]]: +; CHECK-NEXT: [[IV2_PH:%.*]] = phi i16 [ [[START]], %[[VECTOR_SCEVCHECK]] ], [ [[START]], %[[LOOP_BODY_PREHEADER]] ], [ [[TMP13]], %[[MIDDLE_BLOCK]] ] +; CHECK-NEXT: br label %[[LOOP_BODY:.*]] +; CHECK: [[LOOP_BODY]]: +; CHECK-NEXT: [[IV2:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[IV2_PH]], %[[LOOP_BODY_PREHEADER3]] ] +; CHECK-NEXT: [[OFF:%.*]] = sub nsw i16 [[IV2]], [[V]] +; CHECK-NEXT: [[EXT:%.*]] = sext i16 [[OFF]] to i64 +; CHECK-NEXT: [[MASK:%.*]] = and i64 [[EXT]], 4294967295 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[MASK]] +; CHECK-NEXT: store i8 1, ptr [[GEP]], align 1 +; CHECK-NEXT: [[IV_NEXT]] = add nsw i16 [[IV2]], 1 +; CHECK-NEXT: [[EC_NOT:%.*]] = icmp eq i16 [[IV_NEXT]], [[N]] +; CHECK-NEXT: br i1 [[EC_NOT]], label %[[EXIT]], label %[[LOOP_BODY]], !llvm.loop [[LOOP3:![0-9]+]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + %pre = icmp slt i16 %start, %v + br i1 %pre, label %exit, label %loop.header + +loop.header: + %iv = phi i16 [ %start, %entry ], [ %iv.next, %loop.latch ] + %ec = icmp ne i16 %iv, %n + br i1 %ec, label %loop.body, label %exit + +loop.body: + %off = sub nsw i16 %iv, %v + %ext = sext i16 %off to i64 + %mask = and i64 %ext, 4294967295 + %gep = getelementptr inbounds i8, ptr %A, i64 %mask + store i8 1, ptr %gep, align 1 + br label %loop.latch + +loop.latch: + %iv.next = add nsw i16 %iv, 1 + br label %loop.header + +exit: + ret void +} + +define void @index_mask_removed2(ptr %A, i16 %start, i16 %v, i16 %n) { +; CHECK-LABEL: define void @index_mask_removed2( +; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i16 [[START:%.*]], i16 [[V:%.*]], i16 [[N:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PRE:%.*]] = icmp slt i16 [[START]], [[V]] +; CHECK-NEXT: [[EC_NOT1:%.*]] = icmp eq i16 [[START]], [[N]] +; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[PRE]], i1 true, i1 [[EC_NOT1]] +; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[LOOP_BODY_PREHEADER:.*]] +; CHECK: [[LOOP_BODY_PREHEADER]]: +; CHECK-NEXT: [[TMP0:%.*]] = xor i16 [[START]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[N]], [[TMP0]] +; CHECK-NEXT: [[TMP2:%.*]] = zext i16 [[TMP1]] to i32 +; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i32 [[TMP2]], 1 +; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i16 [[TMP1]], 11 +; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[LOOP_BODY_PREHEADER3:.*]], label %[[VECTOR_SCEVCHECK:.*]] +; CHECK: [[VECTOR_SCEVCHECK]]: +; CHECK-NEXT: [[TMP4:%.*]] = xor i16 [[START]], -1 +; CHECK-NEXT: [[TMP5:%.*]] = add i16 [[N]], [[TMP4]] +; CHECK-NEXT: [[TMP6:%.*]] = sext i16 [[V]] to i32 +; CHECK-NEXT: [[TMP7:%.*]] = zext i16 [[TMP5]] to i32 +; CHECK-NEXT: [[TMP8:%.*]] = xor i16 [[START]], -1 +; CHECK-NEXT: [[TMP9:%.*]] = sext i16 [[TMP8]] to i32 +; CHECK-NEXT: [[TMP10:%.*]] = add nsw i32 [[TMP9]], [[TMP6]] +; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 [[TMP10]], [[TMP7]] +; CHECK-NEXT: br i1 [[TMP11]], label %[[LOOP_BODY_PREHEADER3]], label %[[VECTOR_PH:.*]] +; CHECK: [[VECTOR_PH]]: +; CHECK-NEXT: [[N_VEC:%.*]] = and i32 [[TMP3]], 131068 +; CHECK-NEXT: [[TMP12:%.*]] = trunc i32 [[N_VEC]] to i16 +; CHECK-NEXT: [[TMP13:%.*]] = add i16 [[START]], [[TMP12]] +; CHECK-NEXT: [[INVARIANT_OP:%.*]] = sub i16 [[START]], [[V]] +; CHECK-NEXT: br label %[[VECTOR_BODY:.*]] +; CHECK: [[VECTOR_BODY]]: +; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ] +; CHECK-NEXT: [[TMP14:%.*]] = trunc i32 [[INDEX]] to i16 +; CHECK-NEXT: [[DOTREASS:%.*]] = add i16 [[TMP14]], [[INVARIANT_OP]] +; CHECK-NEXT: [[TMP15:%.*]] = sext i16 [[DOTREASS]] to i64 +; CHECK-NEXT: [[TMP16:%.*]] = and i64 [[TMP15]], 4294967295 +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP16]] +; CHECK-NEXT: store <4 x i8> splat (i8 1), ptr [[TMP17]], align 1 +; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 4 +; CHECK-NEXT: [[TMP18:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] +; CHECK-NEXT: br i1 [[TMP18]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP4:![0-9]+]] +; CHECK: [[MIDDLE_BLOCK]]: +; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP3]], [[N_VEC]] +; CHECK-NEXT: br i1 [[CMP_N]], label %[[EXIT]], label %[[LOOP_BODY_PREHEADER3]] +; CHECK: [[LOOP_BODY_PREHEADER3]]: +; CHECK-NEXT: [[IV2_PH:%.*]] = phi i16 [ [[START]], %[[VECTOR_SCEVCHECK]] ], [ [[START]], %[[LOOP_BODY_PREHEADER]] ], [ [[TMP13]], %[[MIDDLE_BLOCK]] ] +; CHECK-NEXT: br label %[[LOOP_BODY:.*]] +; CHECK: [[LOOP_BODY]]: +; CHECK-NEXT: [[IV2:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[IV2_PH]], %[[LOOP_BODY_PREHEADER3]] ] +; CHECK-NEXT: [[OFF:%.*]] = sub nsw i16 [[IV2]], [[V]] +; CHECK-NEXT: [[EXT:%.*]] = sext i16 [[OFF]] to i64 +; CHECK-NEXT: [[MASK:%.*]] = and i64 [[EXT]], 4294967295 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[MASK]] +; CHECK-NEXT: store i8 1, ptr [[GEP]], align 1 +; CHECK-NEXT: [[IV_NEXT]] = add nsw i16 [[IV2]], 1 +; CHECK-NEXT: [[EC_NOT:%.*]] = icmp eq i16 [[IV_NEXT]], [[N]] +; CHECK-NEXT: br i1 [[EC_NOT]], label %[[EXIT]], label %[[LOOP_BODY]], !llvm.loop [[LOOP5:![0-9]+]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + %pre = icmp slt i16 %start, %v + br i1 %pre, label %exit, label %loop.header + +loop.header: + %iv = phi i16 [ %start, %entry ], [ %iv.next, %loop.latch ] + %ec = icmp ne i16 %iv, %n + br i1 %ec, label %loop.body, label %exit + +loop.body: + %off = sub nsw i16 %iv, %v + %ext = sext i16 %off to i64 + %mask = and i64 %ext, 4294967295 + %gep = getelementptr inbounds i8, ptr %A, i64 %mask + store i8 1, ptr %gep, align 1 + br label %loop.latch + +loop.latch: + %iv.next = add nsw i16 %iv, 1 + br label %loop.header + +exit: + ret void +} + +define void @index_not_widened1(ptr %A, i32 %start, i32 %v, i32 %n) { +; CHECK-LABEL: define void @index_not_widened1( +; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i32 [[START:%.*]], i32 [[V:%.*]], i32 [[N:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[PRE:%.*]] = icmp slt i32 [[START]], [[V]] +; CHECK-NEXT: [[EC_NOT1:%.*]] = icmp eq i32 [[START]], [[N]] +; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[PRE]], i1 true, i1 [[EC_NOT1]] +; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[LOOP_BODY:.*]] +; CHECK: [[LOOP_BODY]]: +; CHECK-NEXT: [[IV2:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[START]], %[[ENTRY]] ] +; CHECK-NEXT: [[OFF:%.*]] = sub nsw i32 [[IV2]], [[V]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[OFF]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [4 x i8], ptr [[A]], i64 [[EXT]] +; CHECK-NEXT: store i32 [[IV2]], ptr [[GEP]], align 4 +; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV2]], 1 +; CHECK-NEXT: [[EC_NOT:%.*]] = icmp eq i32 [[IV_NEXT]], [[N]] +; CHECK-NEXT: br i1 [[EC_NOT]], label %[[EXIT]], label %[[LOOP_BODY]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + %pre = icmp slt i32 %start, %v + br i1 %pre, label %exit, label %loop.header + +loop.header: + %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop.latch ] + %ec = icmp ne i32 %iv, %n + br i1 %ec, label %loop.body, label %exit + +loop.body: + %off = sub nsw i32 %iv, %v + %ext = sext i32 %off to i64 + %gep = getelementptr inbounds [4 x i8], ptr %A, i64 %ext + store i32 %iv, ptr %gep, align 4 + br label %loop.latch + +loop.latch: + %iv.next = add nsw i32 %iv, 1 + br label %loop.header + +exit: + ret void +} + +define void @index_not_widened2(ptr %A, i32 %start, i32 %v, i32 %n) { +; CHECK-LABEL: define void @index_not_widened2( +; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i32 [[START:%.*]], i32 [[V:%.*]], i32 [[N:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[PRE:%.*]] = icmp slt i32 [[START]], [[V]] +; CHECK-NEXT: [[EC_NOT1:%.*]] = icmp eq i32 [[START]], [[N]] +; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[PRE]], i1 true, i1 [[EC_NOT1]] +; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[LOOP_BODY:.*]] +; CHECK: [[LOOP_BODY]]: +; CHECK-NEXT: [[IV2:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[START]], %[[ENTRY]] ] +; CHECK-NEXT: [[OFF:%.*]] = sub nsw i32 [[IV2]], [[V]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[OFF]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [4 x i8], ptr [[A]], i64 [[EXT]] +; CHECK-NEXT: store i32 [[IV2]], ptr [[GEP]], align 4 +; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV2]], 1 +; CHECK-NEXT: [[EC_NOT:%.*]] = icmp eq i32 [[IV_NEXT]], [[N]] +; CHECK-NEXT: br i1 [[EC_NOT]], label %[[EXIT]], label %[[LOOP_BODY]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + %pre = icmp slt i32 %start, %v + br i1 %pre, label %exit, label %loop.header + +loop.header: + %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop.latch ] + %ec = icmp ne i32 %iv, %n + br i1 %ec, label %loop.body, label %exit + +loop.body: + %off = sub nsw i32 %iv, %v + %ext = sext i32 %off to i64 + %gep = getelementptr inbounds [4 x i8], ptr %A, i64 %ext + store i32 %iv, ptr %gep, align 4 + br label %loop.latch + +loop.latch: + %iv.next = add nsw i32 %iv, 1 + br label %loop.header + +exit: + ret void +} >From f825edff1bd1c74f386b5b66e2283db2a9601527 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Wed, 26 Aug 2026 12:39:48 +0100 Subject: [PATCH 2/5] !fixup add sub phase-ordering test, clean up duplication --- ...onstraint-elimination-materialize-flags.ll | 190 ++++++------------ 1 file changed, 63 insertions(+), 127 deletions(-) diff --git a/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll b/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll index b94b0e6a384df..e499751615004 100644 --- a/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll +++ b/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll @@ -1,8 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6 ; RUN: opt -passes='default<O3>' -S %s | FileCheck %s -define void @index_mask_removed1(ptr %A, i16 %start, i16 %v, i16 %n) { -; CHECK-LABEL: define void @index_mask_removed1( +define void @index_mask_removed(ptr %A, i16 %start, i16 %v, i16 %n) { +; CHECK-LABEL: define void @index_mask_removed( ; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i16 [[START:%.*]], i16 [[V:%.*]], i16 [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[PRE:%.*]] = icmp slt i16 [[START]], [[V]] @@ -87,94 +87,8 @@ exit: ret void } -define void @index_mask_removed2(ptr %A, i16 %start, i16 %v, i16 %n) { -; CHECK-LABEL: define void @index_mask_removed2( -; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i16 [[START:%.*]], i16 [[V:%.*]], i16 [[N:%.*]]) local_unnamed_addr #[[ATTR0]] { -; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[PRE:%.*]] = icmp slt i16 [[START]], [[V]] -; CHECK-NEXT: [[EC_NOT1:%.*]] = icmp eq i16 [[START]], [[N]] -; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[PRE]], i1 true, i1 [[EC_NOT1]] -; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[LOOP_BODY_PREHEADER:.*]] -; CHECK: [[LOOP_BODY_PREHEADER]]: -; CHECK-NEXT: [[TMP0:%.*]] = xor i16 [[START]], -1 -; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[N]], [[TMP0]] -; CHECK-NEXT: [[TMP2:%.*]] = zext i16 [[TMP1]] to i32 -; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i32 [[TMP2]], 1 -; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i16 [[TMP1]], 11 -; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[LOOP_BODY_PREHEADER3:.*]], label %[[VECTOR_SCEVCHECK:.*]] -; CHECK: [[VECTOR_SCEVCHECK]]: -; CHECK-NEXT: [[TMP4:%.*]] = xor i16 [[START]], -1 -; CHECK-NEXT: [[TMP5:%.*]] = add i16 [[N]], [[TMP4]] -; CHECK-NEXT: [[TMP6:%.*]] = sext i16 [[V]] to i32 -; CHECK-NEXT: [[TMP7:%.*]] = zext i16 [[TMP5]] to i32 -; CHECK-NEXT: [[TMP8:%.*]] = xor i16 [[START]], -1 -; CHECK-NEXT: [[TMP9:%.*]] = sext i16 [[TMP8]] to i32 -; CHECK-NEXT: [[TMP10:%.*]] = add nsw i32 [[TMP9]], [[TMP6]] -; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 [[TMP10]], [[TMP7]] -; CHECK-NEXT: br i1 [[TMP11]], label %[[LOOP_BODY_PREHEADER3]], label %[[VECTOR_PH:.*]] -; CHECK: [[VECTOR_PH]]: -; CHECK-NEXT: [[N_VEC:%.*]] = and i32 [[TMP3]], 131068 -; CHECK-NEXT: [[TMP12:%.*]] = trunc i32 [[N_VEC]] to i16 -; CHECK-NEXT: [[TMP13:%.*]] = add i16 [[START]], [[TMP12]] -; CHECK-NEXT: [[INVARIANT_OP:%.*]] = sub i16 [[START]], [[V]] -; CHECK-NEXT: br label %[[VECTOR_BODY:.*]] -; CHECK: [[VECTOR_BODY]]: -; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ] -; CHECK-NEXT: [[TMP14:%.*]] = trunc i32 [[INDEX]] to i16 -; CHECK-NEXT: [[DOTREASS:%.*]] = add i16 [[TMP14]], [[INVARIANT_OP]] -; CHECK-NEXT: [[TMP15:%.*]] = sext i16 [[DOTREASS]] to i64 -; CHECK-NEXT: [[TMP16:%.*]] = and i64 [[TMP15]], 4294967295 -; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[TMP16]] -; CHECK-NEXT: store <4 x i8> splat (i8 1), ptr [[TMP17]], align 1 -; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 4 -; CHECK-NEXT: [[TMP18:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] -; CHECK-NEXT: br i1 [[TMP18]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP4:![0-9]+]] -; CHECK: [[MIDDLE_BLOCK]]: -; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP3]], [[N_VEC]] -; CHECK-NEXT: br i1 [[CMP_N]], label %[[EXIT]], label %[[LOOP_BODY_PREHEADER3]] -; CHECK: [[LOOP_BODY_PREHEADER3]]: -; CHECK-NEXT: [[IV2_PH:%.*]] = phi i16 [ [[START]], %[[VECTOR_SCEVCHECK]] ], [ [[START]], %[[LOOP_BODY_PREHEADER]] ], [ [[TMP13]], %[[MIDDLE_BLOCK]] ] -; CHECK-NEXT: br label %[[LOOP_BODY:.*]] -; CHECK: [[LOOP_BODY]]: -; CHECK-NEXT: [[IV2:%.*]] = phi i16 [ [[IV_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[IV2_PH]], %[[LOOP_BODY_PREHEADER3]] ] -; CHECK-NEXT: [[OFF:%.*]] = sub nsw i16 [[IV2]], [[V]] -; CHECK-NEXT: [[EXT:%.*]] = sext i16 [[OFF]] to i64 -; CHECK-NEXT: [[MASK:%.*]] = and i64 [[EXT]], 4294967295 -; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 [[MASK]] -; CHECK-NEXT: store i8 1, ptr [[GEP]], align 1 -; CHECK-NEXT: [[IV_NEXT]] = add nsw i16 [[IV2]], 1 -; CHECK-NEXT: [[EC_NOT:%.*]] = icmp eq i16 [[IV_NEXT]], [[N]] -; CHECK-NEXT: br i1 [[EC_NOT]], label %[[EXIT]], label %[[LOOP_BODY]], !llvm.loop [[LOOP5:![0-9]+]] -; CHECK: [[EXIT]]: -; CHECK-NEXT: ret void -; -entry: - %pre = icmp slt i16 %start, %v - br i1 %pre, label %exit, label %loop.header - -loop.header: - %iv = phi i16 [ %start, %entry ], [ %iv.next, %loop.latch ] - %ec = icmp ne i16 %iv, %n - br i1 %ec, label %loop.body, label %exit - -loop.body: - %off = sub nsw i16 %iv, %v - %ext = sext i16 %off to i64 - %mask = and i64 %ext, 4294967295 - %gep = getelementptr inbounds i8, ptr %A, i64 %mask - store i8 1, ptr %gep, align 1 - br label %loop.latch - -loop.latch: - %iv.next = add nsw i16 %iv, 1 - br label %loop.header - -exit: - ret void -} - -define void @index_not_widened1(ptr %A, i32 %start, i32 %v, i32 %n) { -; CHECK-LABEL: define void @index_not_widened1( +define void @index_not_widened(ptr %A, i32 %start, i32 %v, i32 %n) { +; CHECK-LABEL: define void @index_not_widened( ; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i32 [[START:%.*]], i32 [[V:%.*]], i32 [[N:%.*]]) local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[PRE:%.*]] = icmp slt i32 [[START]], [[V]] @@ -217,46 +131,68 @@ exit: ret void } -define void @index_not_widened2(ptr %A, i32 %start, i32 %v, i32 %n) { -; CHECK-LABEL: define void @index_not_widened2( -; CHECK-SAME: ptr nofree writeonly captures(none) [[A:%.*]], i32 [[START:%.*]], i32 [[V:%.*]], i32 [[N:%.*]]) local_unnamed_addr #[[ATTR0]] { +define void @iterator_distance_index(ptr %out, ptr %arr, i1 %skip) { +; CHECK-LABEL: define void @iterator_distance_index( +; CHECK-SAME: ptr nofree writeonly captures(none) [[OUT:%.*]], ptr [[ARR:%.*]], i1 [[SKIP:%.*]]) local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*]]: -; CHECK-NEXT: [[PRE:%.*]] = icmp slt i32 [[START]], [[V]] -; CHECK-NEXT: [[EC_NOT1:%.*]] = icmp eq i32 [[START]], [[N]] -; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[PRE]], i1 true, i1 [[EC_NOT1]] -; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[LOOP_BODY:.*]] -; CHECK: [[LOOP_BODY]]: -; CHECK-NEXT: [[IV2:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP_BODY]] ], [ [[START]], %[[ENTRY]] ] -; CHECK-NEXT: [[OFF:%.*]] = sub nsw i32 [[IV2]], [[V]] -; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[OFF]] to i64 -; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [4 x i8], ptr [[A]], i64 [[EXT]] -; CHECK-NEXT: store i32 [[IV2]], ptr [[GEP]], align 4 -; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV2]], 1 -; CHECK-NEXT: [[EC_NOT:%.*]] = icmp eq i32 [[IV_NEXT]], [[N]] -; CHECK-NEXT: br i1 [[EC_NOT]], label %[[EXIT]], label %[[LOOP_BODY]] -; CHECK: [[EXIT]]: +; CHECK-NEXT: br i1 [[SKIP]], label %[[EARLY_EXIT:.*]], label %[[INNER_PREHEADER:.*]] +; CHECK: [[INNER_PREHEADER]]: +; CHECK-NEXT: [[I2:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: br label %[[INNER_BODY_PREHEADER:.*]] +; CHECK: [[INNER_BODY_PREHEADER]]: +; CHECK-NEXT: [[J1:%.*]] = phi i32 [ 0, %[[INNER_PREHEADER]] ], [ [[J_NEXT:%.*]], %[[INNER_EXIT:.*]] ] +; CHECK-NEXT: [[K_LT_J_PEEL_NOT:%.*]] = icmp eq i32 [[J1]], 0 +; CHECK-NEXT: br i1 [[K_LT_J_PEEL_NOT]], label %[[INNER_EXIT]], label %[[INNER_BODY_PEEL_NEXT:.*]] +; CHECK: [[INNER_BODY_PEEL_NEXT]]: +; CHECK-NEXT: [[K_LT_J_NOT:%.*]] = icmp eq i32 [[J1]], 1 +; CHECK-NEXT: br label %[[INNER_BODY:.*]] +; CHECK: [[INNER_BODY]]: +; CHECK-NEXT: br i1 [[K_LT_J_NOT]], label %[[INNER_EXIT]], label %[[INNER_BODY]], !llvm.loop [[LOOP4:![0-9]+]] +; CHECK: [[INNER_EXIT]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[I2]], [[J1]] +; CHECK-NEXT: [[IDX:%.*]] = zext i32 [[SUB]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr [4 x i8], ptr [[ARR]], i64 [[IDX]] +; CHECK-NEXT: store ptr [[GEP]], ptr [[OUT]], align 8 +; CHECK-NEXT: store i32 0, ptr [[OUT]], align 8 +; CHECK-NEXT: [[J_NEXT]] = add nuw nsw i32 [[J1]], 1 +; CHECK-NEXT: [[J_GT_I_NOT:%.*]] = icmp samesign ult i32 [[J1]], [[I2]] +; CHECK-NEXT: br i1 [[J_GT_I_NOT]], label %[[INNER_BODY_PREHEADER]], label %[[OUTER_LATCH]] +; CHECK: [[OUTER_LATCH]]: +; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i32 [[I2]], 1 +; CHECK-NEXT: br label %[[INNER_PREHEADER]] +; CHECK: [[EARLY_EXIT]]: ; CHECK-NEXT: ret void ; entry: - %pre = icmp slt i32 %start, %v - br i1 %pre, label %exit, label %loop.header - -loop.header: - %iv = phi i32 [ %start, %entry ], [ %iv.next, %loop.latch ] - %ec = icmp ne i32 %iv, %n - br i1 %ec, label %loop.body, label %exit - -loop.body: - %off = sub nsw i32 %iv, %v - %ext = sext i32 %off to i64 - %gep = getelementptr inbounds [4 x i8], ptr %A, i64 %ext - store i32 %iv, ptr %gep, align 4 - br label %loop.latch - -loop.latch: - %iv.next = add nsw i32 %iv, 1 - br label %loop.header - -exit: + br label %outer + +outer: + %i = phi i32 [ 0, %entry ], [ %i.next, %outer.latch ] + br i1 %skip, label %early.exit, label %inner + +inner: + %j = phi i32 [ %j.next, %inner.exit ], [ 0, %outer ] + %j.gt.i = icmp ugt i32 %j, %i + br i1 %j.gt.i, label %outer.latch, label %inner.body + +inner.body: + %k = phi i32 [ 0, %inner ], [ 1, %inner.body ] + %k.lt.j = icmp ult i32 %k, %j + br i1 %k.lt.j, label %inner.body, label %inner.exit + +inner.exit: + %sub = sub i32 %i, %j + %idx = zext i32 %sub to i64 + %gep = getelementptr [4 x i8], ptr %arr, i64 %idx + store ptr %gep, ptr %out, align 8 + store i32 0, ptr %out, align 4 + %j.next = add i32 %j, 1 + br label %inner + +outer.latch: + %i.next = add i32 %i, 1 + br label %outer + +early.exit: ret void } >From 84848a822821e800da74f2c8c2edaebcecc5a440 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Mon, 17 Aug 2026 18:45:55 +0100 Subject: [PATCH 3/5] [ConstraintElim] Strengthen flags on sub. --- .../Scalar/ConstraintElimination.cpp | 49 +++++++++++++++++-- ...nduction-condition-in-loop-exit-postinc.ll | 2 +- .../materialize-flags.ll | 4 +- .../reproducer-remarks.ll | 2 +- .../Transforms/ConstraintElimination/sub.ll | 2 +- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index 054ab3b45e108..f3d5c3c6cb698 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -101,7 +101,7 @@ struct FactOrCheck { InstFact, /// A fact that holds after Inst executed (e.g. an assume or /// min/mix intrinsic. InstCheck, /// An instruction to simplify (e.g. an overflow math - /// intrinsics). + /// intrinsics) or whose flags may be strengthened. UseCheck /// An use of a compare instruction to simplify. }; @@ -146,8 +146,8 @@ struct FactOrCheck { return FactOrCheck(DTN, U); } - static FactOrCheck getCheck(DomTreeNode *DTN, CallInst *CI) { - return FactOrCheck(EntryTy::InstCheck, DTN, CI); + static FactOrCheck getCheck(DomTreeNode *DTN, Instruction *I) { + return FactOrCheck(EntryTy::InstCheck, DTN, I); } bool isCheck() const { @@ -1291,6 +1291,40 @@ static bool getConstraintFromMemoryAccess(GetElementPtrInst &GEP, return true; } +/// Returns true if \p I is a candidate whose poison-generating flags may be +/// strengthened using the constraint systems. +static bool canStrengthenFlags(Instruction *I) { + switch (I->getOpcode()) { + case Instruction::Sub: + // A - B does not wrap unsigned, if A >=u B. Constant operands are handled + // by CorrelatedValuePropagation using ranges. + return I->getType()->isIntegerTy() && !I->hasNoUnsignedWrap() && + !isa<Constant>(I->getOperand(1)); + default: + return false; + } +} + +/// Try to strengthen \p I's poison generating flags using \p Info. Returns +/// true if \p I was modified. +static bool tryToStrengthenFlags(Instruction *I, ConstraintInfo &Info, + SmallVectorImpl<Instruction *> &ToRemove) { + assert(canStrengthenFlags(I) && "not a candidate for flag strengthening"); + + switch (I->getOpcode()) { + case Instruction::Sub: { + // Op0 - Op1 does not wrap unsigned, if Op0 >=u Op1. + if (!Info.doesHold(CmpInst::ICMP_UGE, I->getOperand(0), I->getOperand(1))) + return false; + LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n"); + I->setHasNoUnsignedWrap(); + return true; + } + default: + return false; + } +} + void State::addInfoFor(BasicBlock &BB) { addBoundsForHeaderInductions(BB); addInfoForInductions(BB); @@ -1404,6 +1438,11 @@ void State::addInfoFor(BasicBlock &BB) { WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), BO)); } + // Queue instructions whose flags may be strengthened based on the facts + // that hold on entry to BB. + if (canStrengthenFlags(&I)) + WorkList.push_back(FactOrCheck::getCheck(DT.getNode(&BB), &I)); + GuaranteedToExecute &= isGuaranteedToTransferExecutionToSuccessor(&I); } @@ -2158,6 +2197,10 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, Instruction *Inst = CB.getInstructionToSimplify(); if (!Inst) continue; + if (canStrengthenFlags(Inst)) { + Changed |= tryToStrengthenFlags(Inst, Info, ToRemove); + continue; + } LLVM_DEBUG(dbgs() << "Processing condition to simplify: " << *Inst << "\n"); if (auto *II = dyn_cast<WithOverflowInst>(Inst)) { diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll index 3d509158c523f..1fa5344c9911d 100644 --- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll +++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll @@ -997,7 +997,7 @@ define i64 @latch_postdec_umin_clamp(ptr %s, i64 %n) { ; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 [[IV_NEXT]], 0 ; CHECK-NEXT: br i1 [[EC]], label %[[EXIT]], label %[[LOOP]] ; CHECK: [[IF_FOUND]]: -; CHECK-NEXT: [[IDX:%.*]] = sub i64 [[N]], [[IV]] +; CHECK-NEXT: [[IDX:%.*]] = sub nuw i64 [[N]], [[IV]] ; CHECK-NEXT: br label %[[EXIT]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: [[RES:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[N]], %[[LOOP_LATCH]] ], [ [[IDX]], %[[IF_FOUND]] ] diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll index 6dfa70e044638..d27ff1b5e3068 100644 --- a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll +++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll @@ -74,7 +74,7 @@ define i32 @sub_nuw_relational(i32 %a, i32 %b) { ; CHECK-NEXT: [[C:%.*]] = icmp uge i32 [[A]], [[B]] ; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]] ; CHECK: [[THEN]]: -; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A]], [[B]] +; CHECK-NEXT: [[SUB:%.*]] = sub nuw i32 [[A]], [[B]] ; CHECK-NEXT: ret i32 [[SUB]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret i32 0 @@ -101,7 +101,7 @@ define i32 @sub_nuw_transitive(i32 %a, i32 %b, i32 %c) { ; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[B]], [[C]] ; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]] ; CHECK: [[BB_2]]: -; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A]], [[C]] +; CHECK-NEXT: [[SUB:%.*]] = sub nuw i32 [[A]], [[C]] ; CHECK-NEXT: ret i32 [[SUB]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret i32 0 diff --git a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll index 44dc4e0f690cc..5fa4e2e9f8c06 100644 --- a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll +++ b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll @@ -271,7 +271,7 @@ define i1 @shared_operand_no_const(i8 %a, i8 %b) { ; CHECK-NEXT: entry: ; CHECK-NEXT: %0 = icmp ule i8 %b, %a ; CHECK-NEXT: call void @llvm.assume(i1 %0) -; CHECK-NEXT: %sub = sub i8 %a, %b +; CHECK-NEXT: %sub = sub nuw i8 %a, %b ; CHECK-NEXT: %sub.2 = sub nuw i8 %sub, 0 ; CHECK-NEXT: %c.5 = icmp ult i8 %sub.2, %sub ; CHECK-NEXT: ret i1 %c.5 diff --git a/llvm/test/Transforms/ConstraintElimination/sub.ll b/llvm/test/Transforms/ConstraintElimination/sub.ll index df759e30365ec..751a6d72b4e6b 100644 --- a/llvm/test/Transforms/ConstraintElimination/sub.ll +++ b/llvm/test/Transforms/ConstraintElimination/sub.ll @@ -253,7 +253,7 @@ define i1 @sub_no_wrap_flags_ule(i8 %a, i8 %b) { ; CHECK-NEXT: call void @use(i1 [[C_NEG]]) ; CHECK-NEXT: [[PRECOND:%.*]] = icmp ule i8 [[B]], [[A]] ; CHECK-NEXT: call void @llvm.assume(i1 [[PRECOND]]) -; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[A]], [[B]] +; CHECK-NEXT: [[SUB:%.*]] = sub nuw i8 [[A]], [[B]] ; CHECK-NEXT: ret i1 true ; entry: >From 4d2cc7cdd8cb72df74ea17c5e1a0611af3ad1a89 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Wed, 26 Aug 2026 12:47:24 +0100 Subject: [PATCH 4/5] !fixup update new phase-ordering test, address comments, thanks --- .../Transforms/Scalar/ConstraintElimination.cpp | 4 ++-- .../Transforms/ConstraintElimination/sub.ll | 17 +++++++++++++++++ .../constraint-elimination-materialize-flags.ll | 4 ++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index f3d5c3c6cb698..56ff0a543ae93 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -1296,8 +1296,8 @@ static bool getConstraintFromMemoryAccess(GetElementPtrInst &GEP, static bool canStrengthenFlags(Instruction *I) { switch (I->getOpcode()) { case Instruction::Sub: - // A - B does not wrap unsigned, if A >=u B. Constant operands are handled - // by CorrelatedValuePropagation using ranges. + // A - B does not wrap unsigned, if A >=u B. Subs with constant operands get + // canonicalized to Add. return I->getType()->isIntegerTy() && !I->hasNoUnsignedWrap() && !isa<Constant>(I->getOperand(1)); default: diff --git a/llvm/test/Transforms/ConstraintElimination/sub.ll b/llvm/test/Transforms/ConstraintElimination/sub.ll index 751a6d72b4e6b..4223343c84c71 100644 --- a/llvm/test/Transforms/ConstraintElimination/sub.ll +++ b/llvm/test/Transforms/ConstraintElimination/sub.ll @@ -266,3 +266,20 @@ entry: %c = icmp ule i8 %sub, %a ret i1 %c } + +define <4 x i8> @sub_vector_no_nuw(<4 x i8> %a, <4 x i8> %b) { +; CHECK-LABEL: @sub_vector_no_nuw( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C:%.*]] = icmp uge <4 x i8> [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[ALLC:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[C]]) +; CHECK-NEXT: call void @llvm.assume(i1 [[ALLC]]) +; CHECK-NEXT: [[SUB:%.*]] = sub <4 x i8> [[A]], [[B]] +; CHECK-NEXT: ret <4 x i8> [[SUB]] +; +entry: + %c = icmp uge <4 x i8> %a, %b + %allc = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> %c) + call void @llvm.assume(i1 %allc) + %sub = sub <4 x i8> %a, %b + ret <4 x i8> %sub +} diff --git a/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll b/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll index e499751615004..0086bad9bce74 100644 --- a/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll +++ b/llvm/test/Transforms/PhaseOrdering/constraint-elimination-materialize-flags.ll @@ -149,8 +149,8 @@ define void @iterator_distance_index(ptr %out, ptr %arr, i1 %skip) { ; CHECK: [[INNER_BODY]]: ; CHECK-NEXT: br i1 [[K_LT_J_NOT]], label %[[INNER_EXIT]], label %[[INNER_BODY]], !llvm.loop [[LOOP4:![0-9]+]] ; CHECK: [[INNER_EXIT]]: -; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[I2]], [[J1]] -; CHECK-NEXT: [[IDX:%.*]] = zext i32 [[SUB]] to i64 +; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 [[I2]], [[J1]] +; CHECK-NEXT: [[IDX:%.*]] = zext nneg i32 [[SUB]] to i64 ; CHECK-NEXT: [[GEP:%.*]] = getelementptr [4 x i8], ptr [[ARR]], i64 [[IDX]] ; CHECK-NEXT: store ptr [[GEP]], ptr [[OUT]], align 8 ; CHECK-NEXT: store i32 0, ptr [[OUT]], align 8 >From 297cc2ec9f68e7dddfee86013f38e3ac38a360b3 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Thu, 27 Aug 2026 10:44:27 +0100 Subject: [PATCH 5/5] !fixup update clang codegen tests --- clang/test/CodeGen/attr-counted-by-for-pointers.c | 8 ++++---- .../test/CodeGen/attr-counted-by-with-sanitizers.c | 14 +++++++------- clang/test/CodeGen/attr-sized-by-for-pointers.c | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clang/test/CodeGen/attr-counted-by-for-pointers.c b/clang/test/CodeGen/attr-counted-by-for-pointers.c index 1a4ff73ecb5c6..ef0f3ac23a67f 100644 --- a/clang/test/CodeGen/attr-counted-by-for-pointers.c +++ b/clang/test/CodeGen/attr-counted-by-for-pointers.c @@ -262,7 +262,7 @@ size_t test_bdos_of_pointer_through_cast(struct annotated_ptr *p, int index) { return __bdos((struct foo **)((char *)p->buf)); } -// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869168, 34359738361) i64 @test_bdos_of_element_address( +// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 17179869177) i64 @test_bdos_of_element_address( // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] { // SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] // SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 @@ -278,8 +278,8 @@ size_t test_bdos_of_pointer_through_cast(struct annotated_ptr *p, int index) { // SANITIZE-WITH-ATTR: [[CONT8]]: // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = zext nneg i32 [[DOTCOUNTED_BY_LOAD]] to i64 -// SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]] -// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = shl nsw i64 [[TMP4]], 3 +// SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = sub nuw nsw i64 [[COUNT]], [[IDXPROM]] +// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = shl nuw nsw i64 [[TMP4]], 3 // SANITIZE-WITH-ATTR-NEXT: ret i64 [[RESULT]] // // NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -34359738360, 34359738361) i64 @test_bdos_of_element_address( @@ -531,7 +531,7 @@ void test_pass_dynamic_object_size_of_pointer(struct annotated_volatile_ptr *p) // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDXPROM]] // SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = zext nneg i32 [[DOTCOUNTED_BY_LOAD]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nuw nsw i64 [[COUNT]], 2 -// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[ARRAY_SIZE]], [[IDXPROM]] +// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nuw nsw i64 [[ARRAY_SIZE]], [[IDXPROM]] // SANITIZE-WITH-ATTR-NEXT: tail call void @take_ptr_with_size(ptr noundef [[ARRAYIDX]], i64 noundef [[RESULT]]) #[[ATTR5]] // SANITIZE-WITH-ATTR-NEXT: ret void // diff --git a/clang/test/CodeGen/attr-counted-by-with-sanitizers.c b/clang/test/CodeGen/attr-counted-by-with-sanitizers.c index e840db632957e..3dbbc7807c691 100644 --- a/clang/test/CodeGen/attr-counted-by-with-sanitizers.c +++ b/clang/test/CodeGen/attr-counted-by-with-sanitizers.c @@ -303,7 +303,7 @@ void test_assign_size_of_pointer_into_fam(struct annotated *p, int index, int fa p->array[index + 2] = (unsigned char)__bdos(&(p->array[fam_idx])); } -// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -8589934584, 17179869181) i64 @test_return_bdos_of_pointer_into_fam( +// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 8589934589) i64 @test_return_bdos_of_pointer_into_fam( // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] { // SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] // SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 @@ -319,8 +319,8 @@ void test_assign_size_of_pointer_into_fam(struct annotated *p, int index, int fa // SANITIZE-WITH-ATTR: [[CONT1]]: // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = zext nneg i32 [[DOTCOUNTED_BY_LOAD]] to i64 -// SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]] -// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = shl nsw i64 [[TMP4]], 2 +// SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = sub nuw nsw i64 [[COUNT]], [[IDXPROM]] +// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = shl nuw nsw i64 [[TMP4]], 2 // SANITIZE-WITH-ATTR-NEXT: ret i64 [[RESULT]] // // SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test_return_bdos_of_pointer_into_fam( @@ -529,12 +529,12 @@ size_t test_return_bdos_of_anon_struct(struct union_of_fams *p) { // SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i8, ptr [[TMP0]], align 4 // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = zext i8 [[COUNTED_BY_LOAD]] to i32, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp ult i32 [[INDEX]], [[TMP1]], !nosanitize [[META6]] -// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label %[[CONT14:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] +// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label %[[CONT16:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR: [[HANDLER_OUT_OF_BOUNDS]]: // SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = zext i32 [[INDEX]] to i64, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB16:[0-9]+]], i64 [[TMP3]]) #[[ATTR7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META6]] -// SANITIZE-WITH-ATTR: [[CONT14]]: +// SANITIZE-WITH-ATTR: [[CONT16]]: // SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9 // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = zext nneg i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw i8, ptr [[INTS]], i64 [[IDXPROM]] @@ -612,12 +612,12 @@ void test_assign_bdos_of_struct_to_union_fam(struct union_of_fams *p, int index) // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i32 [[INDEX]], [[COUNTED_BY_LOAD]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], 0, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]], !nosanitize [[META6]] -// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP3]], label %[[CONT14:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] +// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP3]], label %[[CONT16:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !prof [[PROF7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR: [[HANDLER_OUT_OF_BOUNDS]]: // SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = zext i32 [[INDEX]] to i64, !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB19:[0-9]+]], i64 [[TMP4]]) #[[ATTR7]], !nosanitize [[META6]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META6]] -// SANITIZE-WITH-ATTR: [[CONT14]]: +// SANITIZE-WITH-ATTR: [[CONT16]]: // SANITIZE-WITH-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12 // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[BYTES]], i64 [[IDXPROM]] diff --git a/clang/test/CodeGen/attr-sized-by-for-pointers.c b/clang/test/CodeGen/attr-sized-by-for-pointers.c index 1fdbfd39d2628..709e2ac2af2af 100644 --- a/clang/test/CodeGen/attr-sized-by-for-pointers.c +++ b/clang/test/CodeGen/attr-sized-by-for-pointers.c @@ -58,7 +58,7 @@ size_t test_bdos_of_pointer(struct annotated_sized_ptr *p) { return __bdos(p->buf); } -// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -2147483646, 4294967296) i64 @test_bdos_of_element_address( +// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test_bdos_of_element_address( // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] { // SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] // SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 @@ -74,7 +74,7 @@ size_t test_bdos_of_pointer(struct annotated_sized_ptr *p) { // SANITIZE-WITH-ATTR: [[CONT8]]: // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = zext nneg i32 [[DOTCOUNTED_BY_LOAD]] to i64 -// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]] +// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nuw nsw i64 [[COUNT]], [[IDXPROM]] // SANITIZE-WITH-ATTR-NEXT: ret i64 [[RESULT]] // // NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test_bdos_of_element_address( @@ -276,7 +276,7 @@ size_t test_bdos_of_pointer_incomplete_pointee(struct annotated_sized_ptr_incomp return __bdos(p->buf); // byte_count } -// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -2147483646, 4294967296) i64 @test_bdos_of_element_address_incomplete_pointee( +// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test_bdos_of_element_address_incomplete_pointee( // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] { // SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] // SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 @@ -292,7 +292,7 @@ size_t test_bdos_of_pointer_incomplete_pointee(struct annotated_sized_ptr_incomp // SANITIZE-WITH-ATTR: [[CONT8]]: // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = zext nneg i32 [[DOTCOUNTED_BY_LOAD]] to i64 -// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]] +// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nuw nsw i64 [[COUNT]], [[IDXPROM]] // SANITIZE-WITH-ATTR-NEXT: ret i64 [[RESULT]] // // NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test_bdos_of_element_address_incomplete_pointee( @@ -426,7 +426,7 @@ void test_pass_dynamic_object_size_of_pointer(struct annotated_volatile_ptr *p) // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDXPROM]] // SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = zext nneg i32 [[DOTCOUNTED_BY_LOAD]] to i64 -// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]] +// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nuw nsw i64 [[COUNT]], [[IDXPROM]] // SANITIZE-WITH-ATTR-NEXT: tail call void @take_ptr_with_size(ptr noundef [[ARRAYIDX]], i64 noundef [[RESULT]]) #[[ATTR6]] // SANITIZE-WITH-ATTR-NEXT: ret void // _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
