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

Reply via email to