[llvm] [SCEV] Add option to request use-specific SCEV for a GEP expr, (PR #190677)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 10 08:24:55 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/190677
>From eb60d4194e834e517e6c0de832cff9fdb82cb2d5 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 6 Apr 2026 14:28:04 +0100
Subject: [PATCH 1/4] [SCEV] Add tests for use-specific SCEV flags.
---
.../ScalarEvolution/use-specific-flags-gep.ll | 594 ++++++++++++++++++
1 file changed, 594 insertions(+)
create mode 100644 llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
diff --git a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
new file mode 100644
index 0000000000000..8f8f57e5ca8be
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
@@ -0,0 +1,594 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution>" %s 2>&1 | FileCheck %s
+
+declare void @use(ptr)
+
+define void @gep_nonneg_offset(ptr %base, i32 %n) {
+; CHECK-LABEL: 'gep_nonneg_offset'
+; CHECK-NEXT: Classifying expressions for: @gep_nonneg_offset
+; CHECK-NEXT: %ext = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_nonneg_offset
+;
+ %ext = zext i32 %n to i64
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %ext
+ call void @use(ptr %gep.inbounds)
+ %gep.plain = getelementptr i8, ptr %base, i64 %ext
+ call void @use(ptr %gep.plain)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %ext
+ call void @use(ptr %gep.nuw)
+ ret void
+}
+
+; TODO: Use specific flags not propagated through multiplies yet.
+define void @gep_i32_nonneg_offset(ptr %base, i32 %n) {
+; CHECK-LABEL: 'gep_i32_nonneg_offset'
+; CHECK-NEXT: Classifying expressions for: @gep_i32_nonneg_offset
+; CHECK-NEXT: %ext = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i32, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_i32_nonneg_offset
+;
+ %ext = zext i32 %n to i64
+ %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %ext
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i32, ptr %base, i64 %ext
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i32, ptr %base, i64 %ext
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+
+
+define void @gep_maybe_neg_offset(ptr %base, i64 %n) {
+; CHECK-LABEL: 'gep_maybe_neg_offset'
+; CHECK-NEXT: Classifying expressions for: @gep_maybe_neg_offset
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %n
+; CHECK-NEXT: --> (%n + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %n
+; CHECK-NEXT: --> (%n + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %n
+; CHECK-NEXT: --> (%n + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_maybe_neg_offset
+;
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %n
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %n
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %n
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+; Chained inbounds GEPs with non-negative offsets: both get use-specific nuw.
+define ptr @chained_inbounds_geps(ptr %base, i32 %n, i32 %m) {
+; CHECK-LABEL: 'chained_inbounds_geps'
+; CHECK-NEXT: Classifying expressions for: @chained_inbounds_geps
+; CHECK-NEXT: %ext.n = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep1 = getelementptr inbounds i8, ptr %base, i64 %ext.n
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %ext.m = zext i32 %m to i64
+; CHECK-NEXT: --> (zext i32 %m to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ext.m
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @chained_inbounds_geps
+;
+ %ext.n = zext i32 %n to i64
+ %gep1 = getelementptr inbounds i8, ptr %base, i64 %ext.n
+ %ext.m = zext i32 %m to i64
+ %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ext.m
+ ret ptr %gep2
+}
+
+; TODO: Use specific flags not propagated through multiplies yet.
+define void @gep_i32_maybe_neg_offset(ptr %base, i64 %n) {
+; CHECK-LABEL: 'gep_i32_maybe_neg_offset'
+; CHECK-NEXT: Classifying expressions for: @gep_i32_maybe_neg_offset
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %n
+; CHECK-NEXT: --> ((4 * %n) + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %n
+; CHECK-NEXT: --> ((4 * %n) + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i32, ptr %base, i64 %n
+; CHECK-NEXT: --> ((4 * %n) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_i32_maybe_neg_offset
+;
+ %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %n
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i32, ptr %base, i64 %n
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i32, ptr %base, i64 %n
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+define void @gep_i64_nonneg_offset(ptr %base, i32 %n) {
+; CHECK-LABEL: 'gep_i64_nonneg_offset'
+; CHECK-NEXT: Classifying expressions for: @gep_i64_nonneg_offset
+; CHECK-NEXT: %ext = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i64, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i64, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i64, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_i64_nonneg_offset
+;
+ %ext = zext i32 %n to i64
+ %gep.inbounds = getelementptr inbounds i64, ptr %base, i64 %ext
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i64, ptr %base, i64 %ext
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i64, ptr %base, i64 %ext
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+define void @gep_i32_add_no_nuw_index(ptr %base, i64 %a, i64 %b) {
+; CHECK-LABEL: 'gep_i32_add_no_nuw_index'
+; CHECK-NEXT: Classifying expressions for: @gep_i32_add_no_nuw_index
+; CHECK-NEXT: %idx = add i64 %a, %b
+; CHECK-NEXT: --> (%a + %b) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %idx
+; CHECK-NEXT: --> ((4 * (%a + %b)) + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %idx
+; CHECK-NEXT: --> ((4 * (%a + %b)) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_i32_add_no_nuw_index
+;
+ %idx = add i64 %a, %b
+ %gep.nuw = getelementptr nuw i32, ptr %base, i64 %idx
+ call void @use(ptr %gep.nuw)
+ %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %idx
+ call void @use(ptr %gep.inbounds)
+ ret void
+}
+
+; Three chained GEPs: first and third inbounds, middle not. Only first get
+;use-specific nuw; the others don't.
+define ptr @three_chained_geps_middle_not_inbounds(ptr %base, i32 %n, i32 %m, i32 %k) {
+; CHECK-LABEL: 'three_chained_geps_middle_not_inbounds'
+; CHECK-NEXT: Classifying expressions for: @three_chained_geps_middle_not_inbounds
+; CHECK-NEXT: %ext.n = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep1 = getelementptr inbounds i8, ptr %base, i64 %ext.n
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %ext.m = zext i32 %m to i64
+; CHECK-NEXT: --> (zext i32 %m to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep2 = getelementptr i8, ptr %gep1, i64 %ext.m
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %ext.k = zext i32 %k to i64
+; CHECK-NEXT: --> (zext i32 %k to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep3 = getelementptr inbounds i8, ptr %gep2, i64 %ext.k
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + (zext i32 %k to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @three_chained_geps_middle_not_inbounds
+;
+ %ext.n = zext i32 %n to i64
+ %gep1 = getelementptr inbounds i8, ptr %base, i64 %ext.n
+ %ext.m = zext i32 %m to i64
+ %gep2 = getelementptr i8, ptr %gep1, i64 %ext.m
+ %ext.k = zext i32 %k to i64
+ %gep3 = getelementptr inbounds i8, ptr %gep2, i64 %ext.k
+ ret ptr %gep3
+}
+
+; Chained GEPs: outer inbounds, inner not inbounds. None get nuw.
+define ptr @chained_geps_outer_inbounds(ptr %base, i32 %n, i32 %m) {
+; CHECK-LABEL: 'chained_geps_outer_inbounds'
+; CHECK-NEXT: Classifying expressions for: @chained_geps_outer_inbounds
+; CHECK-NEXT: %ext.n = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep1 = getelementptr i8, ptr %base, i64 %ext.n
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %ext.m = zext i32 %m to i64
+; CHECK-NEXT: --> (zext i32 %m to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ext.m
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @chained_geps_outer_inbounds
+;
+ %ext.n = zext i32 %n to i64
+ %gep1 = getelementptr i8, ptr %base, i64 %ext.n
+ %ext.m = zext i32 %m to i64
+ %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ext.m
+ ret ptr %gep2
+}
+
+define i64 @gep_to_ptrtoint(ptr %base, i32 %n) {
+; CHECK-LABEL: 'gep_to_ptrtoint'
+; CHECK-NEXT: Classifying expressions for: @gep_to_ptrtoint
+; CHECK-NEXT: %ext = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %int = ptrtoint ptr %gep to i64
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (ptrtoint ptr %base to i64)) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @gep_to_ptrtoint
+;
+ %ext = zext i32 %n to i64
+ %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+ %int = ptrtoint ptr %gep to i64
+ ret i64 %int
+}
+
+define noundef ptr @inbounds_nuw_gep_poison_ub(ptr %base, i32 %n) {
+; CHECK-LABEL: 'inbounds_nuw_gep_poison_ub'
+; CHECK-NEXT: Classifying expressions for: @inbounds_nuw_gep_poison_ub
+; CHECK-NEXT: %ext = zext i32 %n to i64
+; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep = getelementptr inbounds nuw i8, ptr %base, i64 %ext
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base)<nuw> U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @inbounds_nuw_gep_poison_ub
+;
+ %ext = zext i32 %n to i64
+ %gep = getelementptr inbounds nuw i8, ptr %base, i64 %ext
+ ret ptr %gep
+}
+
+define void @inbounds_gep_add_no_nuw_index(ptr %base, i64 range(i64 0, 4294967296) %a, i64 range(i64 0, 4294967296) %b) {
+; CHECK-LABEL: 'inbounds_gep_add_no_nuw_index'
+; CHECK-NEXT: Classifying expressions for: @inbounds_gep_add_no_nuw_index
+; CHECK-NEXT: %idx = add i64 %a, %b
+; CHECK-NEXT: --> (%a + %b) U: [0,8589934591) S: [0,8589934591)
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @inbounds_gep_add_no_nuw_index
+;
+ %idx = add i64 %a, %b
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+define void @inbounds_gep_add_nuw_index(ptr %base, i64 range(i64 0, 4294967296) %a, i64 range(i64 0, 4294967296) %b) {
+; CHECK-LABEL: 'inbounds_gep_add_nuw_index'
+; CHECK-NEXT: Classifying expressions for: @inbounds_gep_add_nuw_index
+; CHECK-NEXT: %idx = add nuw i64 %a, %b
+; CHECK-NEXT: --> (%a + %b) U: [0,8589934591) S: [0,8589934591)
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @inbounds_gep_add_nuw_index
+;
+ %idx = add nuw i64 %a, %b
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+; Inbounds GEP where the index is an add that may be negative.
+define void @inbounds_gep_add_maybe_neg_index(ptr %base, i64 %a, i64 %b) {
+; CHECK-LABEL: 'inbounds_gep_add_maybe_neg_index'
+; CHECK-NEXT: Classifying expressions for: @inbounds_gep_add_maybe_neg_index
+; CHECK-NEXT: %idx = add nuw i64 %a, %b
+; CHECK-NEXT: --> (%a + %b) U: full-set S: full-set
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> (%a + %b + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @inbounds_gep_add_maybe_neg_index
+;
+ %idx = add nuw i64 %a, %b
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %idx
+ call void @use(ptr %gep.plain)
+ ret void
+}
+
+; Multi-index struct GEP with add for first dimension.
+define ptr @inbounds_struct_gep_add_index(ptr %base, i32 %a, i32 %b) {
+; CHECK-LABEL: 'inbounds_struct_gep_add_index'
+; CHECK-NEXT: Classifying expressions for: @inbounds_struct_gep_add_index
+; CHECK-NEXT: %ea = zext i32 %a to i64
+; CHECK-NEXT: --> (zext i32 %a to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %eb = zext i32 %b to i64
+; CHECK-NEXT: --> (zext i32 %b to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %idx = add nuw nsw i64 %ea, %eb
+; CHECK-NEXT: --> ((zext i32 %a to i64) + (zext i32 %b to i64)) U: [0,8589934591) S: [0,8589934591)
+; CHECK-NEXT: %gep = getelementptr inbounds [8 x i8], ptr %base, i64 %idx, i64 4
+; CHECK-NEXT: --> (4 + (8 * ((zext i32 %a to i64) + (zext i32 %b to i64)))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @inbounds_struct_gep_add_index
+;
+ %ea = zext i32 %a to i64
+ %eb = zext i32 %b to i64
+ %idx = add nuw nsw i64 %ea, %eb
+ %gep = getelementptr inbounds [8 x i8], ptr %base, i64 %idx, i64 4
+ ret ptr %gep
+}
+
+; Non-inbounds GEP with add index chained to inbounds GEP.
+define ptr @non_inbounds_add_then_inbounds(ptr %base, i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: 'non_inbounds_add_then_inbounds'
+; CHECK-NEXT: Classifying expressions for: @non_inbounds_add_then_inbounds
+; CHECK-NEXT: %ea = zext i32 %a to i64
+; CHECK-NEXT: --> (zext i32 %a to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %eb = zext i32 %b to i64
+; CHECK-NEXT: --> (zext i32 %b to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %idx = add nuw nsw i64 %ea, %eb
+; CHECK-NEXT: --> ((zext i32 %a to i64) + (zext i32 %b to i64)) U: [0,8589934591) S: [0,8589934591)
+; CHECK-NEXT: %gep1 = getelementptr i8, ptr %base, i64 %idx
+; CHECK-NEXT: --> ((zext i32 %a to i64) + (zext i32 %b to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: %ec = zext i32 %c to i64
+; CHECK-NEXT: --> (zext i32 %c to i64) U: [0,4294967296) S: [0,4294967296)
+; CHECK-NEXT: %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ec
+; CHECK-NEXT: --> ((zext i32 %a to i64) + (zext i32 %b to i64) + (zext i32 %c to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @non_inbounds_add_then_inbounds
+;
+ %ea = zext i32 %a to i64
+ %eb = zext i32 %b to i64
+ %idx = add nuw nsw i64 %ea, %eb
+ %gep1 = getelementptr i8, ptr %base, i64 %idx
+ %ec = zext i32 %c to i64
+ %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ec
+ ret ptr %gep2
+}
+
+; %iv may wrap, so we cannot preserve use-specific NUW after folding the GEP
+; into an AddRec.
+define void @loop_gep_flags(ptr %base, i64 %n) {
+; CHECK-LABEL: 'loop_gep_flags'
+; CHECK-NEXT: Classifying expressions for: @loop_gep_flags
+; CHECK-NEXT: %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.next = add i64 %iv, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @loop_gep_flags
+; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.nuw)
+ %gep.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.nusw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.plain)
+ %iv.next = add i64 %iv, 1
+ %cmp = icmp ne i64 %iv.next, %n
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @loop_inbounds_neg_step(ptr %base, i64 %n) {
+; CHECK-LABEL: 'loop_inbounds_neg_step'
+; CHECK-NEXT: Classifying expressions for: @loop_inbounds_neg_step
+; CHECK-NEXT: %iv = phi i64 [ %n, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {%n,+,-1}<nsw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)))<nsw> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.next = add nsw i64 %iv, -1
+; CHECK-NEXT: --> {(-1 + %n),+,-1}<nw><%loop> U: full-set S: full-set Exits: (0 smin (-1 + %n)) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @loop_inbounds_neg_step
+; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + (-1 * (0 smin (-1 + %n))) + %n)
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i64 9223372036854775807
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + (-1 * (0 smin (-1 + %n))) + %n)
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ %n, %entry ], [ %iv.next, %loop ]
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.nuw)
+ %gep.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.nusw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.plain)
+ %iv.next = add nsw i64 %iv, -1
+ %cmp = icmp sgt i64 %iv.next, 0
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; Nested loop with inbounds and nuw GEPs at both levels.
+define void @nested_loop_inbounds_addrecs(ptr %base, i64 %n, i64 %m) {
+; CHECK-LABEL: 'nested_loop_inbounds_addrecs'
+; CHECK-NEXT: Classifying expressions for: @nested_loop_inbounds_addrecs
+; CHECK-NEXT: %iv.outer = phi i64 [ 0, %entry ], [ %iv.outer.next, %outer.latch ]
+; CHECK-NEXT: --> {0,+,1}<nuw><%outer> U: full-set S: full-set Exits: (-1 + %n) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: %gep.outer = getelementptr inbounds i8, ptr %base, i64 %iv.outer
+; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: %gep.outer.nuw = getelementptr nuw i8, ptr %base, i64 %iv.outer
+; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: %gep.outer.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv.outer
+; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: %gep.outer.plain = getelementptr i8, ptr %base, i64 %iv.outer
+; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: %iv.inner = phi i64 [ 0, %outer ], [ %iv.inner.next, %inner ]
+; CHECK-NEXT: --> {0,+,1}<nuw><%inner> U: full-set S: full-set Exits: (-1 + %m) LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %gep.outer, i64 %iv.inner
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: %gep.inner.nuw = getelementptr nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: %gep.inner.nusw = getelementptr inbounds nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: %gep.inner.plain = getelementptr i8, ptr %gep.outer, i64 %iv.inner
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: %iv.inner.next = add nuw i64 %iv.inner, 1
+; CHECK-NEXT: --> {1,+,1}<nw><%inner> U: full-set S: full-set Exits: %m LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: %iv.outer.next = add nuw i64 %iv.outer, 1
+; CHECK-NEXT: --> {1,+,1}<nw><%outer> U: full-set S: full-set Exits: %n LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: Determining loop execution counts for: @nested_loop_inbounds_addrecs
+; CHECK-NEXT: Loop %inner: backedge-taken count is (-1 + %m)
+; CHECK-NEXT: Loop %inner: constant max backedge-taken count is i64 -1
+; CHECK-NEXT: Loop %inner: symbolic max backedge-taken count is (-1 + %m)
+; CHECK-NEXT: Loop %inner: Trip multiple is 1
+; CHECK-NEXT: Loop %outer: backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %outer: constant max backedge-taken count is i64 -1
+; CHECK-NEXT: Loop %outer: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %outer: Trip multiple is 1
+;
+entry:
+ br label %outer
+
+outer:
+ %iv.outer = phi i64 [ 0, %entry ], [ %iv.outer.next, %outer.latch ]
+ %gep.outer = getelementptr inbounds i8, ptr %base, i64 %iv.outer
+ call void @use(ptr %gep.outer)
+ %gep.outer.nuw = getelementptr nuw i8, ptr %base, i64 %iv.outer
+ call void @use(ptr %gep.outer.nuw)
+ %gep.outer.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv.outer
+ call void @use(ptr %gep.outer.nusw)
+ %gep.outer.plain = getelementptr i8, ptr %base, i64 %iv.outer
+ call void @use(ptr %gep.outer.plain)
+ br label %inner
+
+inner:
+ %iv.inner = phi i64 [ 0, %outer ], [ %iv.inner.next, %inner ]
+ %gep.inbounds = getelementptr inbounds i8, ptr %gep.outer, i64 %iv.inner
+ call void @use(ptr %gep.inbounds)
+ %gep.inner.nuw = getelementptr nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
+ call void @use(ptr %gep.inner.nuw)
+ %gep.inner.nusw = getelementptr inbounds nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
+ call void @use(ptr %gep.inner.nusw)
+ %gep.inner.plain = getelementptr i8, ptr %gep.outer, i64 %iv.inner
+ call void @use(ptr %gep.inner.plain)
+ %iv.inner.next = add nuw i64 %iv.inner, 1
+ %cmp.inner = icmp ne i64 %iv.inner.next, %m
+ br i1 %cmp.inner, label %inner, label %outer.latch
+
+outer.latch:
+ %iv.outer.next = add nuw i64 %iv.outer, 1
+ %cmp.outer = icmp ne i64 %iv.outer.next, %n
+ br i1 %cmp.outer, label %outer, label %exit
+
+exit:
+ ret void
+}
+
+define void @loop_gep_nuw_iv(ptr %base, i64 %n) {
+; CHECK-LABEL: 'loop_gep_nuw_iv'
+; CHECK-NEXT: Classifying expressions for: @loop_gep_nuw_iv
+; CHECK-NEXT: %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<nuw><%loop> U: full-set S: full-set Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<nw><%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<nw><%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,1}<nw><%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.next = add nuw i64 %iv, 1
+; CHECK-NEXT: --> {1,+,1}<nw><%loop> U: full-set S: full-set Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @loop_gep_nuw_iv
+; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.inbounds)
+ %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.nuw)
+ %gep.plain = getelementptr i8, ptr %base, i64 %iv
+ call void @use(ptr %gep.plain)
+ %iv.next = add nuw i64 %iv, 1
+ %cmp = icmp ne i64 %iv.next, %n
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; TODO: Use specific flags not propagated through multiplies yet.
+define void @loop_gep_i32_nuw_iv(ptr %base, i64 %n) {
+; CHECK-LABEL: 'loop_gep_i32_nuw_iv'
+; CHECK-NEXT: Classifying expressions for: @loop_gep_i32_nuw_iv
+; CHECK-NEXT: %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<nuw><%loop> U: full-set S: full-set Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,4}<%loop> U: full-set S: full-set Exits: (-4 + (4 * %n) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,4}<%loop> U: full-set S: full-set Exits: (-4 + (4 * %n) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %gep.plain = getelementptr i32, ptr %base, i64 %iv
+; CHECK-NEXT: --> {%base,+,4}<%loop> U: full-set S: full-set Exits: (-4 + (4 * %n) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.next = add nuw i64 %iv, 1
+; CHECK-NEXT: --> {1,+,1}<nw><%loop> U: full-set S: full-set Exits: %n LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @loop_gep_i32_nuw_iv
+; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + %n)
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %gep.nuw = getelementptr nuw i32, ptr %base, i64 %iv
+ call void @use(ptr %gep.nuw)
+ %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %iv
+ call void @use(ptr %gep.inbounds)
+ %gep.plain = getelementptr i32, ptr %base, i64 %iv
+ call void @use(ptr %gep.plain)
+ %iv.next = add nuw i64 %iv, 1
+ %cmp = icmp ne i64 %iv.next, %n
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret void
+}
>From 72401ddd9db090928ed21217400ab4aade99ba8b Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 6 Apr 2026 21:40:46 +0100
Subject: [PATCH 2/4] [SCEV] Add option to request use-specific SCEV for a GEP
expr
This patch is a first step to allow requesting use-specific SCEVs
building on SCEVUse from https://github.com/llvm/llvm-project/pull/91961.
The initial implementation supports adding NUW as use-specific flags for
GEPs, if the original GEP instruction has NUW/inbounds and all
sub-expressions are NUW or guarnateed to not be in-inlineable into the
larger SCEVAddExpr.
The flags are not yet propagated through to SCEVAddExpr and SCEVMulExpr
creation, so currently this is limited to GEPs with i8 source element
types. This will be extended in follow-ups, once we settled on how the
general API should look like.
Currently only used by SCEV printing for initial testing and to get the
initial infrastructure working.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 45 +++++---
llvm/lib/Analysis/IVDescriptors.cpp | 2 +-
llvm/lib/Analysis/ScalarEvolution.cpp | 103 ++++++++++++------
llvm/lib/Passes/PassRegistry.def | 11 +-
.../Scalar/StraightLineStrengthReduce.cpp | 2 +-
.../Utils/ScalarEvolutionExpander.cpp | 2 +-
.../Transforms/Vectorize/VPlanTransforms.cpp | 2 +-
.../use-specific-flags-cache.ll | 34 ++++++
.../ScalarEvolution/use-specific-flags-gep.ll | 28 ++---
.../Analysis/ScalarEvolutionTest.cpp | 8 +-
10 files changed, 161 insertions(+), 76 deletions(-)
create mode 100644 llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 5c01da0855f66..fffe7e47c903a 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -145,6 +145,10 @@ struct SCEVUseT : private PointerIntPair<SCEVPtrT, 2> {
SCEVNoWrapFlags
getNoWrapFlags(SCEVNoWrapFlags Mask = SCEVNoWrapFlags::NoWrapMask) const;
+ bool hasNoUnsignedWrap() const {
+ return any(getNoWrapFlags() & SCEVNoWrapFlags::FlagNUW);
+ }
+
/// Return only the use-specific no-wrap flags (NUW/NSW) without the
/// underlying SCEV's flags.
SCEVNoWrapFlags getUseNoWrapFlags() const {
@@ -730,11 +734,12 @@ class ScalarEvolution {
LLVM_ABI bool containsErasedValue(const SCEV *S) const;
/// Return a SCEV expression for the full generality of the specified
- /// expression.
- LLVM_ABI const SCEV *getSCEV(Value *V);
+ /// expression. If \p UseCtx is true, returns a SCEV with use-specific flags
+ /// valid only for existing uses of \p V.
+ LLVM_ABI SCEVUse getSCEV(Value *V, bool UseCtx = false);
/// Return an existing SCEV for V if there is one, otherwise return nullptr.
- LLVM_ABI const SCEV *getExistingSCEV(Value *V);
+ LLVM_ABI SCEVUse getExistingSCEV(Value *V);
LLVM_ABI const SCEV *getConstant(ConstantInt *V);
LLVM_ABI const SCEV *getConstant(const APInt &Val);
@@ -816,12 +821,12 @@ class ScalarEvolution {
/// \p GEP The GEP. The indices contained in the GEP itself are ignored,
/// instead we use IndexExprs.
/// \p IndexExprs The expressions for the indices.
- LLVM_ABI const SCEV *getGEPExpr(GEPOperator *GEP,
- ArrayRef<SCEVUse> IndexExprs);
- LLVM_ABI const SCEV *getGEPExpr(SCEVUse BaseExpr,
- ArrayRef<SCEVUse> IndexExprs,
- Type *SrcElementTy,
- GEPNoWrapFlags NW = GEPNoWrapFlags::none());
+ LLVM_ABI SCEVUse getGEPExpr(GEPOperator *GEP, ArrayRef<SCEVUse> IndexExprs,
+ bool UseCtx = false);
+ LLVM_ABI SCEVUse
+ getGEPExpr(SCEVUse BaseExpr, ArrayRef<SCEVUse> IndexExprs, Type *SrcElementTy,
+ GEPNoWrapFlags NW = GEPNoWrapFlags::none(),
+ GEPNoWrapFlags UseSpecificNW = GEPNoWrapFlags::none());
LLVM_ABI const SCEV *getAbsExpr(const SCEV *Op, bool IsNSW);
LLVM_ABI const SCEV *getMinMaxExpr(SCEVTypes Kind,
SmallVectorImpl<SCEVUse> &Operands);
@@ -1483,7 +1488,7 @@ class ScalarEvolution {
/// Return the size of an element read or written by Inst.
LLVM_ABI const SCEV *getElementSize(Instruction *Inst);
- LLVM_ABI void print(raw_ostream &OS) const;
+ LLVM_ABI void print(raw_ostream &OS, bool UseCtx = false) const;
LLVM_ABI void verify() const;
LLVM_ABI bool invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv);
@@ -1677,7 +1682,7 @@ class ScalarEvolution {
/// The type for ValueExprMap.
using ValueExprMapType =
- DenseMap<SCEVCallbackVH, const SCEV *, DenseMapInfo<Value *>>;
+ DenseMap<SCEVCallbackVH, SCEVUse, DenseMapInfo<Value *>>;
/// This is a cache of the values we have analyzed so far.
ValueExprMapType ValueExprMap;
@@ -1987,12 +1992,14 @@ class ScalarEvolution {
ConstantRange getRangeForUnknownRecurrence(const SCEVUnknown *U);
/// We know that there is no SCEV for the specified value. Analyze the
- /// expression recursively.
- const SCEV *createSCEV(Value *V);
+ /// expression recursively. If \p UseCtx is true, create a SCEV with
+ /// use-specific flags valid only for existing uses of \p V.
+ SCEVUse createSCEV(Value *V, bool UseCtx = false);
/// We know that there is no SCEV for the specified value. Create a new SCEV
- /// for \p V iteratively.
- const SCEV *createSCEVIter(Value *V);
+ /// for \p V iteratively. If \p UseCtx is true, create a SCEV with
+ /// use-specific flags valid only for existing uses of \p V.
+ SCEVUse createSCEVIter(Value *V, bool UseCtx = false);
/// Collect operands of \p V for which SCEV expressions should be constructed
/// first. Returns a SCEV directly if it can be constructed trivially for \p
/// V.
@@ -2036,7 +2043,7 @@ class ScalarEvolution {
Value *FalseVal);
/// Provide the special handling we need to analyze GEP SCEVs.
- const SCEV *createNodeForGEP(GEPOperator *GEP);
+ SCEVUse createNodeForGEP(GEPOperator *GEP, bool UseCtx = false);
/// Implementation code for getSCEVAtScope; called at most once for each
/// SCEV+Loop pair.
@@ -2349,7 +2356,7 @@ class ScalarEvolution {
void eraseValueFromMap(Value *V);
/// Insert V to S mapping into ValueExprMap and ExprValueMap.
- void insertValueToMap(Value *V, const SCEV *S);
+ void insertValueToMap(Value *V, SCEVUse S);
/// Return false iff given SCEV contains a SCEVUnknown with NULL value-
/// pointer.
@@ -2557,9 +2564,11 @@ class ScalarEvolutionVerifierPass
class ScalarEvolutionPrinterPass
: public PassInfoMixin<ScalarEvolutionPrinterPass> {
raw_ostream &OS;
+ bool UseCtx;
public:
- explicit ScalarEvolutionPrinterPass(raw_ostream &OS) : OS(OS) {}
+ explicit ScalarEvolutionPrinterPass(raw_ostream &OS, bool UseCtx = false)
+ : OS(OS), UseCtx(UseCtx) {}
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
diff --git a/llvm/lib/Analysis/IVDescriptors.cpp b/llvm/lib/Analysis/IVDescriptors.cpp
index 185943e0a9d2e..27bebfe9489ea 100644
--- a/llvm/lib/Analysis/IVDescriptors.cpp
+++ b/llvm/lib/Analysis/IVDescriptors.cpp
@@ -1606,7 +1606,7 @@ bool InductionDescriptor::isInductionPHI(
return false;
// Check that the PHI is consecutive.
- const SCEV *PhiScev = Expr ? Expr : SE->getSCEV(Phi);
+ const SCEV *PhiScev = Expr ? Expr : SE->getSCEV(Phi).getPointer();
const SCEV *Step;
// FIXME: We are currently matching the specific loop TheLoop; if it doesn't
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 2862acfedb91d..62d01ed758d68 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3887,9 +3887,9 @@ const SCEV *ScalarEvolution::getAddRecExpr(SmallVectorImpl<SCEVUse> &Operands,
return getOrCreateAddRecExpr(Operands, L, Flags);
}
-const SCEV *ScalarEvolution::getGEPExpr(GEPOperator *GEP,
- ArrayRef<SCEVUse> IndexExprs) {
- const SCEV *BaseExpr = getSCEV(GEP->getPointerOperand());
+SCEVUse ScalarEvolution::getGEPExpr(GEPOperator *GEP,
+ ArrayRef<SCEVUse> IndexExprs, bool UseCtx) {
+ SCEVUse BaseExpr = getSCEV(GEP->getPointerOperand(), UseCtx);
// getSCEV(Base)->getType() has the same address space as Base->getType()
// because SCEV::getType() preserves the address space.
GEPNoWrapFlags NW = GEP->getNoWrapFlags();
@@ -3904,12 +3904,15 @@ const SCEV *ScalarEvolution::getGEPExpr(GEPOperator *GEP,
NW = GEPNoWrapFlags::none();
}
- return getGEPExpr(BaseExpr, IndexExprs, GEP->getSourceElementType(), NW);
+ return getGEPExpr(BaseExpr, IndexExprs, GEP->getSourceElementType(), NW,
+ /*UseSpecificNW=*/
+ UseCtx ? GEP->getNoWrapFlags() : GEPNoWrapFlags::none());
}
-const SCEV *ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
- ArrayRef<SCEVUse> IndexExprs,
- Type *SrcElementTy, GEPNoWrapFlags NW) {
+SCEVUse ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
+ ArrayRef<SCEVUse> IndexExprs,
+ Type *SrcElementTy, GEPNoWrapFlags NW,
+ GEPNoWrapFlags UseSpecificNW) {
SCEV::NoWrapFlags OffsetWrap = SCEV::FlagAnyWrap;
if (NW.hasNoUnsignedSignedWrap())
OffsetWrap = setFlags(OffsetWrap, SCEV::FlagNSW);
@@ -3967,6 +3970,30 @@ const SCEV *ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
auto *GEPExpr = getAddExpr(BaseExpr, Offset, BaseWrap);
assert(BaseExpr->getType() == GEPExpr->getType() &&
"GEP should not change type mid-flight.");
+ if (!NUW) {
+ if (UseSpecificNW.hasNoUnsignedWrap() ||
+ (UseSpecificNW.isInBounds() && isKnownNonNegative(Offset))) {
+ // Check if it is safe to annotate the expression with use-specific NUW.
+ // Don't apply it if Base or Offset contain potentially wrapping
+ // sub-expressions that could be flattened into a larger Add expression.
+ auto IsSafeForUseNUW = [](SCEVUse S) {
+ if (any(S.getUseNoWrapFlags() & SCEV::FlagNUW))
+ return true;
+ // Add and AddRec expressions could be flattened into a wider
+ // add, so they need NUW to be safe.
+ if (isa<SCEVAddExpr, SCEVAddRecExpr>(S))
+ return S.hasNoUnsignedWrap();
+ // Conservatively allow a small set of expression forms that are
+ // known safe: they won't be flattened into a wider add.
+ const SCEVUnknown *U;
+ return match(
+ S, m_CombineOr(m_scev_ZExt(m_SCEVUnknown(U)), m_SCEVUnknown(U)));
+ match(S, m_SCEVConstant());
+ };
+ if (IsSafeForUseNUW(BaseExpr) && IsSafeForUseNUW(Offset))
+ return SCEVUse(&*GEPExpr, SCEV::FlagNUW);
+ }
+ }
return GEPExpr;
}
@@ -4705,7 +4732,7 @@ void ScalarEvolution::eraseValueFromMap(Value *V) {
}
}
-void ScalarEvolution::insertValueToMap(Value *V, const SCEV *S) {
+void ScalarEvolution::insertValueToMap(Value *V, SCEVUse S) {
// A recursive query may have already computed the SCEV. It should be
// equivalent, but may not necessarily be exactly the same, e.g. due to lazily
// inferred nowrap flags.
@@ -4718,20 +4745,26 @@ void ScalarEvolution::insertValueToMap(Value *V, const SCEV *S) {
/// Return an existing SCEV if it exists, otherwise analyze the expression and
/// create a new one.
-const SCEV *ScalarEvolution::getSCEV(Value *V) {
+SCEVUse ScalarEvolution::getSCEV(Value *V, bool UseCtx) {
assert(isSCEVable(V->getType()) && "Value is not SCEVable!");
- if (const SCEV *S = getExistingSCEV(V))
+ if (SCEVUse S = getExistingSCEV(V)) {
+ // When not using context-specific flags, return the canonical SCEV
+ // without any use-specific flags.
+ if (!UseCtx)
+ return S.getPointer();
return S;
- return createSCEVIter(V);
+ }
+ // TODO: Should we always create use-specific SCEVs?
+ return createSCEVIter(V, UseCtx);
}
-const SCEV *ScalarEvolution::getExistingSCEV(Value *V) {
+SCEVUse ScalarEvolution::getExistingSCEV(Value *V) {
assert(isSCEVable(V->getType()) && "Value is not SCEVable!");
ValueExprMapType::iterator I = ValueExprMap.find_as(V);
if (I != ValueExprMap.end()) {
- const SCEV *S = I->second;
+ SCEVUse S = I->second;
assert(checkValidity(S) &&
"existing SCEV has not been properly invalidated");
return S;
@@ -6412,9 +6445,9 @@ createNodeForSelectViaUMinSeq(ScalarEvolution *SE, Value *Cond, Value *TrueVal,
if (!isa<ConstantInt>(TrueVal) && !isa<ConstantInt>(FalseVal))
return std::nullopt;
- const auto *SECond = SE->getSCEV(Cond);
- const auto *SETrue = SE->getSCEV(TrueVal);
- const auto *SEFalse = SE->getSCEV(FalseVal);
+ const SCEV *SECond = SE->getSCEV(Cond);
+ const SCEV *SETrue = SE->getSCEV(TrueVal);
+ const SCEV *SEFalse = SE->getSCEV(FalseVal);
return createNodeForSelectViaUMinSeq(SE, SECond, SETrue, SEFalse);
}
@@ -6458,14 +6491,14 @@ const SCEV *ScalarEvolution::createNodeForSelectOrPHI(Value *V, Value *Cond,
/// Expand GEP instructions into add and multiply operations. This allows them
/// to be analyzed by regular SCEV code.
-const SCEV *ScalarEvolution::createNodeForGEP(GEPOperator *GEP) {
+SCEVUse ScalarEvolution::createNodeForGEP(GEPOperator *GEP, bool UseCtx) {
assert(GEP->getSourceElementType()->isSized() &&
"GEP source element type must be sized");
SmallVector<SCEVUse, 4> IndexExprs;
for (Value *Index : GEP->indices())
IndexExprs.push_back(getSCEV(Index));
- return getGEPExpr(GEP, IndexExprs);
+ return getGEPExpr(GEP, IndexExprs, UseCtx);
}
APInt ScalarEvolution::getConstantMultipleImpl(const SCEV *S,
@@ -7689,7 +7722,7 @@ bool ScalarEvolution::loopIsFiniteByAssumption(const Loop *L) {
return isFinite(L) || (isMustProgress(L) && loopHasNoSideEffects(L));
}
-const SCEV *ScalarEvolution::createSCEVIter(Value *V) {
+SCEVUse ScalarEvolution::createSCEVIter(Value *V, bool UseCtx) {
// Worklist item with a Value and a bool indicating whether all operands have
// been visited already.
using PointerTy = PointerIntPair<Value *, 1, bool>;
@@ -7705,10 +7738,10 @@ const SCEV *ScalarEvolution::createSCEVIter(Value *V) {
continue;
SmallVector<Value *> Ops;
- const SCEV *CreatedSCEV = nullptr;
+ SCEVUse CreatedSCEV;
// If all operands have been visited already, create the SCEV.
if (E.getInt()) {
- CreatedSCEV = createSCEV(CurV);
+ CreatedSCEV = createSCEV(CurV, UseCtx);
} else {
// Otherwise get the operands we need to create SCEV's for before creating
// the SCEV for CurV. If the SCEV for CurV can be constructed trivially,
@@ -7964,7 +7997,7 @@ ScalarEvolution::getOperandsToCreate(Value *V, SmallVectorImpl<Value *> &Ops) {
return nullptr;
}
-const SCEV *ScalarEvolution::createSCEV(Value *V) {
+SCEVUse ScalarEvolution::createSCEV(Value *V, bool UseCtx) {
if (!isSCEVable(V->getType()))
return getUnknown(V);
@@ -7999,7 +8032,7 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) {
SmallVector<SCEVUse, 4> AddOps;
do {
if (BO->Op) {
- if (auto *OpSCEV = getExistingSCEV(BO->Op)) {
+ if (auto OpSCEV = getExistingSCEV(BO->Op)) {
AddOps.push_back(OpSCEV);
break;
}
@@ -8045,7 +8078,7 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) {
SmallVector<SCEVUse, 4> MulOps;
do {
if (BO->Op) {
- if (auto *OpSCEV = getExistingSCEV(BO->Op)) {
+ if (auto OpSCEV = getExistingSCEV(BO->Op)) {
MulOps.push_back(OpSCEV);
break;
}
@@ -8381,7 +8414,7 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) {
break;
case Instruction::GetElementPtr:
- return createNodeForGEP(cast<GEPOperator>(U));
+ return createNodeForGEP(cast<GEPOperator>(U), UseCtx);
case Instruction::PHI:
return createNodeForPHI(cast<PHINode>(U));
@@ -9342,7 +9375,7 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromCondImpl(
NWR.getEquivalentICmp(Pred, NewRHSC, Offset);
if (!ExitIfTrue)
Pred = ICmpInst::getInversePredicate(Pred);
- auto *LHS = getSCEV(WO->getLHS());
+ const SCEV *LHS = getSCEV(WO->getLHS());
if (Offset != 0)
LHS = getAddExpr(LHS, getConstant(Offset));
auto EL = computeExitLimitFromICmp(L, Pred, LHS, getConstant(NewRHSC),
@@ -12783,7 +12816,7 @@ bool ScalarEvolution::isImpliedCondOperandsViaShift(CmpPredicate Pred,
using namespace PatternMatch;
if (match(SUFoundRHS->getValue(),
m_LShr(m_Value(Shiftee), m_Value(ShiftValue)))) {
- auto *ShifteeS = getSCEV(Shiftee);
+ const SCEV *ShifteeS = getSCEV(Shiftee);
// Prove one of the following:
// LHS <u (shiftee >> shiftvalue) && shiftee <=u RHS ---> LHS <u RHS
// LHS <=u (shiftee >> shiftvalue) && shiftee <=u RHS ---> LHS <=u RHS
@@ -13000,7 +13033,7 @@ bool ScalarEvolution::isImpliedViaOperations(CmpPredicate Pred, const SCEV *LHS,
// We want to make sure that LHS = FoundLHS / Denominator. If it is so,
// then a SCEV for the numerator already exists and matches with FoundLHS.
- auto *Numerator = getExistingSCEV(LL);
+ auto Numerator = getExistingSCEV(LL);
if (!Numerator || Numerator->getType() != FoundLHS->getType())
return false;
@@ -14258,7 +14291,7 @@ static raw_ostream &operator<<(raw_ostream &OS,
}
} // namespace llvm
-void ScalarEvolution::print(raw_ostream &OS) const {
+void ScalarEvolution::print(raw_ostream &OS, bool UseCtx) const {
// ScalarEvolution's implementation of the print method is to print
// out SCEV values of all instructions that are interesting. Doing
// this potentially causes it to create new SCEV objects though,
@@ -14275,8 +14308,8 @@ void ScalarEvolution::print(raw_ostream &OS) const {
if (isSCEVable(I.getType()) && !isa<CmpInst>(I)) {
OS << I << '\n';
OS << " --> ";
- const SCEV *SV = SE.getSCEV(&I);
- SV->print(OS);
+ SCEVUse SV = SE.getSCEV(&I, UseCtx);
+ SV.print(OS);
if (!isa<SCEVCouldNotCompute>(SV)) {
OS << " U: ";
SE.getUnsignedRange(SV).print(OS);
@@ -15010,7 +15043,7 @@ ScalarEvolutionPrinterPass::run(Function &F, FunctionAnalysisManager &AM) {
// update_analyze_test_checks.py working.
OS << "Printing analysis 'Scalar Evolution Analysis' for function '"
<< F.getName() << "':\n";
- AM.getResult<ScalarEvolutionAnalysis>(F).print(OS);
+ AM.getResult<ScalarEvolutionAnalysis>(F).print(OS, UseCtx);
return PreservedAnalyses::all();
}
@@ -15621,7 +15654,7 @@ void PredicatedScalarEvolution::print(raw_ostream &OS, unsigned Depth) const {
if (!SE.isSCEVable(I.getType()))
continue;
- auto *Expr = SE.getSCEV(&I);
+ const SCEV *Expr = SE.getSCEV(&I);
auto II = RewriteMap.find(Expr);
if (II == RewriteMap.end())
@@ -16104,8 +16137,8 @@ void ScalarEvolution::LoopGuards::collectFromBlock(
if (auto *Cmp = dyn_cast<ICmpInst>(Cond)) {
auto Predicate =
EnterIfTrue ? Cmp->getPredicate() : Cmp->getInversePredicate();
- const auto *LHS = SE.getSCEV(Cmp->getOperand(0));
- const auto *RHS = SE.getSCEV(Cmp->getOperand(1));
+ const SCEV *LHS = SE.getSCEV(Cmp->getOperand(0));
+ const SCEV *RHS = SE.getSCEV(Cmp->getOperand(1));
// If LHS is a constant, apply information to the other expression.
// TODO: If LHS is not a constant, check if using CompareSCEVComplexity
// can improve results.
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index c92d93d7ae396..9479d9f1fd38c 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -522,7 +522,6 @@ FUNCTION_PASS("print<memoryssa-walker>", MemorySSAWalkerPrinterPass(errs()))
FUNCTION_PASS("print<phi-values>", PhiValuesPrinterPass(errs()))
FUNCTION_PASS("print<postdomtree>", PostDominatorTreePrinterPass(errs()))
FUNCTION_PASS("print<regions>", RegionInfoPrinterPass(errs()))
-FUNCTION_PASS("print<scalar-evolution>", ScalarEvolutionPrinterPass(errs()))
FUNCTION_PASS("print<scev-division>", SCEVDivisionPrinterPass(errs()))
FUNCTION_PASS("print<stack-safety-local>", StackSafetyPrinterPass(errs()))
FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(errs()))
@@ -671,6 +670,16 @@ FUNCTION_PASS_WITH_PARAMS(
return MemorySSAPrinterPass(errs(), !NoEnsureOptimizedUses);
},
parseMemorySSAPrinterPassOptions, "no-ensure-optimized-uses")
+FUNCTION_PASS_WITH_PARAMS(
+ "print<scalar-evolution>", "ScalarEvolutionPrinterPass",
+ [](bool UseCtx) {
+ return ScalarEvolutionPrinterPass(errs(), UseCtx);
+ },
+ [](StringRef Params) {
+ return PassBuilder::parseSinglePassOption(
+ Params, "use-context", "ScalarEvolutionPrinterPass");
+ },
+ "use-context")
FUNCTION_PASS_WITH_PARAMS(
"print<stack-lifetime>", "StackLifetimePrinterPass",
[](StackLifetime::LivenessType Type) {
diff --git a/llvm/lib/Transforms/Scalar/StraightLineStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/StraightLineStrengthReduce.cpp
index ed43fb4b63f87..cd8f4436cb447 100644
--- a/llvm/lib/Transforms/Scalar/StraightLineStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/StraightLineStrengthReduce.cpp
@@ -492,7 +492,7 @@ class StraightLineStrengthReduce {
} CandidateDict;
const SCEV *getAndRecordSCEV(Value *V) {
- auto *S = SE->getSCEV(V);
+ const SCEV *S = SE->getSCEV(V);
if (isa<Instruction>(V) && !(isa<SCEVCouldNotCompute>(S) ||
isa<SCEVUnknown>(S) || isa<SCEVConstant>(S)))
SCEVToInsts[S].insert(cast<Instruction>(V));
diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
index d54e4de81d995..439e9fa03b281 100644
--- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
+++ b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
@@ -1275,7 +1275,7 @@ Value *SCEVExpander::tryToReuseLCSSAPhi(SCEVUseT<const SCEVAddRecExpr *> S) {
for (auto &PN : EB->phis()) {
if (!SE.isSCEVable(PN.getType()))
continue;
- auto *ExitSCEV = SE.getSCEV(&PN);
+ const SCEV *ExitSCEV = SE.getSCEV(&PN);
if (!isa<SCEVAddRecExpr>(ExitSCEV))
continue;
Type *PhiTy = PN.getType();
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 094eab7ca9246..87f701567e108 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -4992,7 +4992,7 @@ void VPlanTransforms::materializeConstantVectorTripCount(
// TODO: Compute vector trip counts for loops requiring a scalar epilogue and
// tail-folded loops.
ScalarEvolution &SE = *PSE.getSE();
- auto *TCScev = SE.getSCEV(TC->getLiveInIRValue());
+ const SCEV *TCScev = SE.getSCEV(TC->getLiveInIRValue());
if (!isa<SCEVConstant>(TCScev))
return;
const SCEV *VFxUF = SE.getElementCount(TCScev->getType(), BestVF * BestUF);
diff --git a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll
new file mode 100644
index 0000000000000..2ad722258bb67
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll
@@ -0,0 +1,34 @@
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution><use-context>,print<scalar-evolution>" %s 2>&1 | FileCheck %s --check-prefix=CTX-THEN-NOCTX
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution><use-context>,print<scalar-evolution><use-context>" %s 2>&1 | FileCheck %s --check-prefix=CTX-THEN-CTX
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution>,print<scalar-evolution><use-context>" %s 2>&1 | FileCheck %s --check-prefix=NOCTX-FIRST
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution>,print<scalar-evolution>" %s 2>&1 | FileCheck %s --check-prefix=NOCTX-FIRST
+
+define void @f(ptr %base, i32 %n) {
+ %ext = zext i32 %n to i64
+ %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+ ret void
+}
+
+; use-context first: shows (u nuw). Without context second: strips it.
+; CTX-THEN-NOCTX-LABEL: 'f'
+; CTX-THEN-NOCTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; CTX-THEN-NOCTX-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U:
+; CTX-THEN-NOCTX: 'f'
+; CTX-THEN-NOCTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; CTX-THEN-NOCTX-NEXT: --> ((zext i32 %n to i64) + %base) U:
+
+; use-context both times: caches with flags, second returns them from cache.
+; CTX-THEN-CTX-LABEL: 'f'
+; CTX-THEN-CTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; CTX-THEN-CTX-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U:
+; CTX-THEN-CTX: 'f'
+; CTX-THEN-CTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; CTX-THEN-CTX-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U:
+
+; Without context first: no use-specific flags in either print.
+; NOCTX-FIRST-LABEL: 'f'
+; NOCTX-FIRST: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; NOCTX-FIRST-NEXT: --> ((zext i32 %n to i64) + %base) U:
+; NOCTX-FIRST: 'f'
+; NOCTX-FIRST: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; NOCTX-FIRST-NEXT: --> ((zext i32 %n to i64) + %base) U:
diff --git a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
index 8f8f57e5ca8be..a43a045a757c8 100644
--- a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
+++ b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
-; RUN: opt -S -disable-output "-passes=print<scalar-evolution>" %s 2>&1 | FileCheck %s
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution><use-context>" %s 2>&1 | FileCheck %s
declare void @use(ptr)
@@ -9,11 +9,11 @@ define void @gep_nonneg_offset(ptr %base, i32 %n) {
; CHECK-NEXT: %ext = zext i32 %n to i64
; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %ext
; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_nonneg_offset
;
%ext = zext i32 %n to i64
@@ -58,7 +58,7 @@ define void @gep_maybe_neg_offset(ptr %base, i64 %n) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %n
; CHECK-NEXT: --> (%n + %base) U: full-set S: full-set
; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %n
-; CHECK-NEXT: --> (%n + %base) U: full-set S: full-set
+; CHECK-NEXT: --> (%n + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %n
; CHECK-NEXT: --> (%n + %base) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_maybe_neg_offset
@@ -79,11 +79,11 @@ define ptr @chained_inbounds_geps(ptr %base, i32 %n, i32 %m) {
; CHECK-NEXT: %ext.n = zext i32 %n to i64
; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep1 = getelementptr inbounds i8, ptr %base, i64 %ext.n
-; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %ext.m = zext i32 %m to i64
; CHECK-NEXT: --> (zext i32 %m to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ext.m
-; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @chained_inbounds_geps
;
%ext.n = zext i32 %n to i64
@@ -164,7 +164,7 @@ define ptr @three_chained_geps_middle_not_inbounds(ptr %base, i32 %n, i32 %m, i3
; CHECK-NEXT: %ext.n = zext i32 %n to i64
; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep1 = getelementptr inbounds i8, ptr %base, i64 %ext.n
-; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %ext.m = zext i32 %m to i64
; CHECK-NEXT: --> (zext i32 %m to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep2 = getelementptr i8, ptr %gep1, i64 %ext.m
@@ -211,7 +211,7 @@ define i64 @gep_to_ptrtoint(ptr %base, i32 %n) {
; CHECK-NEXT: %ext = zext i32 %n to i64
; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((zext i32 %n to i64) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %int = ptrtoint ptr %gep to i64
; CHECK-NEXT: --> ((zext i32 %n to i64) + (ptrtoint ptr %base to i64)) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_to_ptrtoint
@@ -317,7 +317,7 @@ define ptr @inbounds_struct_gep_add_index(ptr %base, i32 %a, i32 %b) {
; CHECK-NEXT: %idx = add nuw nsw i64 %ea, %eb
; CHECK-NEXT: --> ((zext i32 %a to i64) + (zext i32 %b to i64)) U: [0,8589934591) S: [0,8589934591)
; CHECK-NEXT: %gep = getelementptr inbounds [8 x i8], ptr %base, i64 %idx, i64 4
-; CHECK-NEXT: --> (4 + (8 * ((zext i32 %a to i64) + (zext i32 %b to i64)))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: --> (4 + (8 * ((zext i32 %a to i64) + (zext i32 %b to i64)))<nuw><nsw> + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @inbounds_struct_gep_add_index
;
%ea = zext i32 %a to i64
@@ -449,9 +449,9 @@ define void @nested_loop_inbounds_addrecs(ptr %base, i64 %n, i64 %m) {
; CHECK-NEXT: %gep.outer = getelementptr inbounds i8, ptr %base, i64 %iv.outer
; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
; CHECK-NEXT: %gep.outer.nuw = getelementptr nuw i8, ptr %base, i64 %iv.outer
-; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: --> {%base,+,1}<nw><%outer>(u nuw) U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
; CHECK-NEXT: %gep.outer.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv.outer
-; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
+; CHECK-NEXT: --> {%base,+,1}<nw><%outer>(u nuw) U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
; CHECK-NEXT: %gep.outer.plain = getelementptr i8, ptr %base, i64 %iv.outer
; CHECK-NEXT: --> {%base,+,1}<nw><%outer> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %outer: Computable, %inner: Invariant }
; CHECK-NEXT: %iv.inner = phi i64 [ 0, %outer ], [ %iv.inner.next, %inner ]
@@ -459,9 +459,9 @@ define void @nested_loop_inbounds_addrecs(ptr %base, i64 %n, i64 %m) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %gep.outer, i64 %iv.inner
; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.nuw = getelementptr nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
-; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner>(u nuw) U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.nusw = getelementptr inbounds nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
-; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner>(u nuw) U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.plain = getelementptr i8, ptr %gep.outer, i64 %iv.inner
; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %iv.inner.next = add nuw i64 %iv.inner, 1
@@ -524,7 +524,7 @@ define void @loop_gep_nuw_iv(ptr %base, i64 %n) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
; CHECK-NEXT: --> {%base,+,1}<nw><%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
-; CHECK-NEXT: --> {%base,+,1}<nw><%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {%base,+,1}<nw><%loop>(u nuw) U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %iv
; CHECK-NEXT: --> {%base,+,1}<nw><%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %iv.next = add nuw i64 %iv, 1
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 8504534693281..0412ddb655b29 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1520,7 +1520,7 @@ TEST_F(ScalarEvolutionsTest, MatchURem) {
runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
for (auto *N : {"rem1", "rem2", "rem3", "rem5"}) {
auto *URemI = getInstructionByName(F, N);
- auto *S = SE.getSCEV(URemI);
+ SCEVUse S = SE.getSCEV(URemI);
const SCEV *LHS, *RHS;
EXPECT_TRUE(match(S, m_scev_URem(m_SCEV(LHS), m_SCEV(RHS), SE)));
EXPECT_EQ(LHS, SE.getSCEV(URemI->getOperand(0)));
@@ -1533,7 +1533,7 @@ TEST_F(ScalarEvolutionsTest, MatchURem) {
// match results are extended to the size of the input expression.
auto *Ext = getInstructionByName(F, "ext");
auto *URem1 = getInstructionByName(F, "rem4");
- auto *S = SE.getSCEV(Ext);
+ SCEVUse S = SE.getSCEV(Ext);
const SCEV *LHS, *RHS;
EXPECT_TRUE(match(S, m_scev_URem(m_SCEV(LHS), m_SCEV(RHS), SE)));
EXPECT_NE(LHS, SE.getSCEV(URem1->getOperand(0)));
@@ -1660,11 +1660,11 @@ TEST_F(ScalarEvolutionsTest, ForgetValueWithOverflowInst) {
auto *ExtractValue = getInstructionByName(F, "extractvalue");
auto *IV = getInstructionByName(F, "iv");
- auto *ExtractValueScev = SE.getSCEV(ExtractValue);
+ SCEVUse ExtractValueScev = SE.getSCEV(ExtractValue);
EXPECT_NE(ExtractValueScev, nullptr);
SE.forgetValue(IV);
- auto *ExtractValueScevForgotten = SE.getExistingSCEV(ExtractValue);
+ SCEVUse ExtractValueScevForgotten = SE.getExistingSCEV(ExtractValue);
EXPECT_EQ(ExtractValueScevForgotten, nullptr);
});
}
>From 1c4a9d2f597d3a9367dc43faa04a611a3c912b76 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 10 Apr 2026 12:27:21 +0100
Subject: [PATCH 3/4] !fixup Always create with use context.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 15 +++----
llvm/lib/Analysis/ScalarEvolution.cpp | 43 +++++++++----------
.../use-specific-flags-cache.ll | 34 +++++++--------
.../ScalarEvolution/use-specific-flags-gep.ll | 6 +--
4 files changed, 47 insertions(+), 51 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index fffe7e47c903a..03b68e3698245 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -821,8 +821,7 @@ class ScalarEvolution {
/// \p GEP The GEP. The indices contained in the GEP itself are ignored,
/// instead we use IndexExprs.
/// \p IndexExprs The expressions for the indices.
- LLVM_ABI SCEVUse getGEPExpr(GEPOperator *GEP, ArrayRef<SCEVUse> IndexExprs,
- bool UseCtx = false);
+ LLVM_ABI SCEVUse getGEPExpr(GEPOperator *GEP, ArrayRef<SCEVUse> IndexExprs);
LLVM_ABI SCEVUse
getGEPExpr(SCEVUse BaseExpr, ArrayRef<SCEVUse> IndexExprs, Type *SrcElementTy,
GEPNoWrapFlags NW = GEPNoWrapFlags::none(),
@@ -1992,14 +1991,12 @@ class ScalarEvolution {
ConstantRange getRangeForUnknownRecurrence(const SCEVUnknown *U);
/// We know that there is no SCEV for the specified value. Analyze the
- /// expression recursively. If \p UseCtx is true, create a SCEV with
- /// use-specific flags valid only for existing uses of \p V.
- SCEVUse createSCEV(Value *V, bool UseCtx = false);
+ /// expression recursively.
+ SCEVUse createSCEV(Value *V);
/// We know that there is no SCEV for the specified value. Create a new SCEV
- /// for \p V iteratively. If \p UseCtx is true, create a SCEV with
- /// use-specific flags valid only for existing uses of \p V.
- SCEVUse createSCEVIter(Value *V, bool UseCtx = false);
+ /// for \p V iteratively.
+ SCEVUse createSCEVIter(Value *V);
/// Collect operands of \p V for which SCEV expressions should be constructed
/// first. Returns a SCEV directly if it can be constructed trivially for \p
/// V.
@@ -2043,7 +2040,7 @@ class ScalarEvolution {
Value *FalseVal);
/// Provide the special handling we need to analyze GEP SCEVs.
- SCEVUse createNodeForGEP(GEPOperator *GEP, bool UseCtx = false);
+ SCEVUse createNodeForGEP(GEPOperator *GEP);
/// Implementation code for getSCEVAtScope; called at most once for each
/// SCEV+Loop pair.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 62d01ed758d68..4e6c31f768f4f 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3888,8 +3888,8 @@ const SCEV *ScalarEvolution::getAddRecExpr(SmallVectorImpl<SCEVUse> &Operands,
}
SCEVUse ScalarEvolution::getGEPExpr(GEPOperator *GEP,
- ArrayRef<SCEVUse> IndexExprs, bool UseCtx) {
- SCEVUse BaseExpr = getSCEV(GEP->getPointerOperand(), UseCtx);
+ ArrayRef<SCEVUse> IndexExprs) {
+ SCEVUse BaseExpr = getSCEV(GEP->getPointerOperand());
// getSCEV(Base)->getType() has the same address space as Base->getType()
// because SCEV::getType() preserves the address space.
GEPNoWrapFlags NW = GEP->getNoWrapFlags();
@@ -3905,8 +3905,7 @@ SCEVUse ScalarEvolution::getGEPExpr(GEPOperator *GEP,
}
return getGEPExpr(BaseExpr, IndexExprs, GEP->getSourceElementType(), NW,
- /*UseSpecificNW=*/
- UseCtx ? GEP->getNoWrapFlags() : GEPNoWrapFlags::none());
+ /*UseSpecificNW=*/GEP->getNoWrapFlags());
}
SCEVUse ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
@@ -3972,7 +3971,7 @@ SCEVUse ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
"GEP should not change type mid-flight.");
if (!NUW) {
if (UseSpecificNW.hasNoUnsignedWrap() ||
- (UseSpecificNW.isInBounds() && isKnownNonNegative(Offset))) {
+ (UseSpecificNW.hasNoUnsignedSignedWrap() && isKnownNonNegative(Offset))) {
// Check if it is safe to annotate the expression with use-specific NUW.
// Don't apply it if Base or Offset contain potentially wrapping
// sub-expressions that could be flattened into a larger Add expression.
@@ -3987,8 +3986,8 @@ SCEVUse ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
// known safe: they won't be flattened into a wider add.
const SCEVUnknown *U;
return match(
- S, m_CombineOr(m_scev_ZExt(m_SCEVUnknown(U)), m_SCEVUnknown(U)));
- match(S, m_SCEVConstant());
+ S, m_CombineOr(m_scev_ZExt(m_SCEVUnknown(U)),
+ m_CombineOr(m_SCEVUnknown(U), m_SCEVConstant())));
};
if (IsSafeForUseNUW(BaseExpr) && IsSafeForUseNUW(Offset))
return SCEVUse(&*GEPExpr, SCEV::FlagNUW);
@@ -4748,15 +4747,15 @@ void ScalarEvolution::insertValueToMap(Value *V, SCEVUse S) {
SCEVUse ScalarEvolution::getSCEV(Value *V, bool UseCtx) {
assert(isSCEVable(V->getType()) && "Value is not SCEVable!");
- if (SCEVUse S = getExistingSCEV(V)) {
- // When not using context-specific flags, return the canonical SCEV
- // without any use-specific flags.
- if (!UseCtx)
- return S.getPointer();
- return S;
- }
- // TODO: Should we always create use-specific SCEVs?
- return createSCEVIter(V, UseCtx);
+ SCEVUse S = getExistingSCEV(V);
+ if (!S)
+ S = createSCEVIter(V);
+
+ // When not using context-specific flags, return the canonical SCEV
+ // without any use-specific flags.
+ if (!UseCtx)
+ return S.getPointer();
+ return S;
}
SCEVUse ScalarEvolution::getExistingSCEV(Value *V) {
@@ -6491,14 +6490,14 @@ const SCEV *ScalarEvolution::createNodeForSelectOrPHI(Value *V, Value *Cond,
/// Expand GEP instructions into add and multiply operations. This allows them
/// to be analyzed by regular SCEV code.
-SCEVUse ScalarEvolution::createNodeForGEP(GEPOperator *GEP, bool UseCtx) {
+SCEVUse ScalarEvolution::createNodeForGEP(GEPOperator *GEP) {
assert(GEP->getSourceElementType()->isSized() &&
"GEP source element type must be sized");
SmallVector<SCEVUse, 4> IndexExprs;
for (Value *Index : GEP->indices())
IndexExprs.push_back(getSCEV(Index));
- return getGEPExpr(GEP, IndexExprs, UseCtx);
+ return getGEPExpr(GEP, IndexExprs);
}
APInt ScalarEvolution::getConstantMultipleImpl(const SCEV *S,
@@ -7722,7 +7721,7 @@ bool ScalarEvolution::loopIsFiniteByAssumption(const Loop *L) {
return isFinite(L) || (isMustProgress(L) && loopHasNoSideEffects(L));
}
-SCEVUse ScalarEvolution::createSCEVIter(Value *V, bool UseCtx) {
+SCEVUse ScalarEvolution::createSCEVIter(Value *V) {
// Worklist item with a Value and a bool indicating whether all operands have
// been visited already.
using PointerTy = PointerIntPair<Value *, 1, bool>;
@@ -7741,7 +7740,7 @@ SCEVUse ScalarEvolution::createSCEVIter(Value *V, bool UseCtx) {
SCEVUse CreatedSCEV;
// If all operands have been visited already, create the SCEV.
if (E.getInt()) {
- CreatedSCEV = createSCEV(CurV, UseCtx);
+ CreatedSCEV = createSCEV(CurV);
} else {
// Otherwise get the operands we need to create SCEV's for before creating
// the SCEV for CurV. If the SCEV for CurV can be constructed trivially,
@@ -7997,7 +7996,7 @@ ScalarEvolution::getOperandsToCreate(Value *V, SmallVectorImpl<Value *> &Ops) {
return nullptr;
}
-SCEVUse ScalarEvolution::createSCEV(Value *V, bool UseCtx) {
+SCEVUse ScalarEvolution::createSCEV(Value *V) {
if (!isSCEVable(V->getType()))
return getUnknown(V);
@@ -8414,7 +8413,7 @@ SCEVUse ScalarEvolution::createSCEV(Value *V, bool UseCtx) {
break;
case Instruction::GetElementPtr:
- return createNodeForGEP(cast<GEPOperator>(U), UseCtx);
+ return createNodeForGEP(cast<GEPOperator>(U));
case Instruction::PHI:
return createNodeForPHI(cast<PHINode>(U));
diff --git a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll
index 2ad722258bb67..a785ed3e4629c 100644
--- a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll
+++ b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-cache.ll
@@ -1,7 +1,6 @@
; RUN: opt -S -disable-output "-passes=print<scalar-evolution><use-context>,print<scalar-evolution>" %s 2>&1 | FileCheck %s --check-prefix=CTX-THEN-NOCTX
-; RUN: opt -S -disable-output "-passes=print<scalar-evolution><use-context>,print<scalar-evolution><use-context>" %s 2>&1 | FileCheck %s --check-prefix=CTX-THEN-CTX
-; RUN: opt -S -disable-output "-passes=print<scalar-evolution>,print<scalar-evolution><use-context>" %s 2>&1 | FileCheck %s --check-prefix=NOCTX-FIRST
-; RUN: opt -S -disable-output "-passes=print<scalar-evolution>,print<scalar-evolution>" %s 2>&1 | FileCheck %s --check-prefix=NOCTX-FIRST
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution>,print<scalar-evolution><use-context>" %s 2>&1 | FileCheck %s --check-prefix=NOCTX-THEN-CTX
+; RUN: opt -S -disable-output "-passes=print<scalar-evolution>,print<scalar-evolution>" %s 2>&1 | FileCheck %s --check-prefix=NOCTX-BOTH
define void @f(ptr %base, i32 %n) {
%ext = zext i32 %n to i64
@@ -17,18 +16,19 @@ define void @f(ptr %base, i32 %n) {
; CTX-THEN-NOCTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
; CTX-THEN-NOCTX-NEXT: --> ((zext i32 %n to i64) + %base) U:
-; use-context both times: caches with flags, second returns them from cache.
-; CTX-THEN-CTX-LABEL: 'f'
-; CTX-THEN-CTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
-; CTX-THEN-CTX-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U:
-; CTX-THEN-CTX: 'f'
-; CTX-THEN-CTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
-; CTX-THEN-CTX-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U:
+; Without context first, use-context second: use-specific flags are still
+; available from cache because they are always computed.
+; NOCTX-THEN-CTX-LABEL: 'f'
+; NOCTX-THEN-CTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; NOCTX-THEN-CTX-NEXT: --> ((zext i32 %n to i64) + %base) U:
+; NOCTX-THEN-CTX: 'f'
+; NOCTX-THEN-CTX: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; NOCTX-THEN-CTX-NEXT: --> ((zext i32 %n to i64) + %base)(u nuw) U:
-; Without context first: no use-specific flags in either print.
-; NOCTX-FIRST-LABEL: 'f'
-; NOCTX-FIRST: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
-; NOCTX-FIRST-NEXT: --> ((zext i32 %n to i64) + %base) U:
-; NOCTX-FIRST: 'f'
-; NOCTX-FIRST: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
-; NOCTX-FIRST-NEXT: --> ((zext i32 %n to i64) + %base) U:
+; Without context both times: no use-specific flags shown.
+; NOCTX-BOTH-LABEL: 'f'
+; NOCTX-BOTH: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; NOCTX-BOTH-NEXT: --> ((zext i32 %n to i64) + %base) U:
+; NOCTX-BOTH: 'f'
+; NOCTX-BOTH: %gep = getelementptr inbounds i8, ptr %base, i64 %ext
+; NOCTX-BOTH-NEXT: --> ((zext i32 %n to i64) + %base) U:
diff --git a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
index a43a045a757c8..9e2e32a38cfbd 100644
--- a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
+++ b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
@@ -83,7 +83,7 @@ define ptr @chained_inbounds_geps(ptr %base, i32 %n, i32 %m) {
; CHECK-NEXT: %ext.m = zext i32 %m to i64
; CHECK-NEXT: --> (zext i32 %m to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep2 = getelementptr inbounds i8, ptr %gep1, i64 %ext.m
-; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base)(u nuw) U: full-set S: full-set
+; CHECK-NEXT: --> ((zext i32 %n to i64) + (zext i32 %m to i64) + %base) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @chained_inbounds_geps
;
%ext.n = zext i32 %n to i64
@@ -459,9 +459,9 @@ define void @nested_loop_inbounds_addrecs(ptr %base, i64 %n, i64 %m) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %gep.outer, i64 %iv.inner
; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.nuw = getelementptr nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
-; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner>(u nuw) U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.nusw = getelementptr inbounds nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
-; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner>(u nuw) U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.plain = getelementptr i8, ptr %gep.outer, i64 %iv.inner
; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %iv.inner.next = add nuw i64 %iv.inner, 1
>From 67a3535e9c511b368548faf5415035964eda9a38 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 10 Apr 2026 14:27:01 +0100
Subject: [PATCH 4/4] !fixup support use flags in getAddExpr.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 7 +--
llvm/lib/Analysis/LoopAccessAnalysis.cpp | 3 +-
llvm/lib/Analysis/ScalarEvolution.cpp | 54 ++++++++-----------
.../Scalar/InductiveRangeCheckElimination.cpp | 2 +-
.../Transforms/Scalar/LoopStrengthReduce.cpp | 5 +-
.../ScalarEvolution/use-specific-flags-gep.ll | 26 ++++-----
6 files changed, 43 insertions(+), 54 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 03b68e3698245..41644873a54fb 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -765,9 +765,10 @@ class ScalarEvolution {
LLVM_ABI const SCEV *getCastExpr(SCEVTypes Kind, const SCEV *Op, Type *Ty);
LLVM_ABI const SCEV *getAnyExtendExpr(const SCEV *Op, Type *Ty);
- LLVM_ABI const SCEV *getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
- SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap,
- unsigned Depth = 0);
+ LLVM_ABI SCEVUse getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
+ SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap,
+ unsigned Depth = 0,
+ SCEV::NoWrapFlags UseFlags = SCEV::FlagAnyWrap);
const SCEV *getAddExpr(SCEVUse LHS, SCEVUse RHS,
SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap,
unsigned Depth = 0) {
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 2b9efd22131c6..bff3ce44cc077 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -1139,7 +1139,8 @@ static void findForkedSCEVs(
return get<1>(S);
};
- auto GetBinOpExpr = [&SE](unsigned Opcode, const SCEV *L, const SCEV *R) {
+ auto GetBinOpExpr = [&SE](unsigned Opcode, const SCEV *L,
+ const SCEV *R) -> const SCEV * {
switch (Opcode) {
case Instruction::Add:
return SE->getAddExpr(L, R);
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 4e6c31f768f4f..def9ca870d64f 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2624,9 +2624,9 @@ bool ScalarEvolution::isAvailableAtLoopEntry(const SCEV *S, const Loop *L) {
}
/// Get a canonical add expression, or something simpler if possible.
-const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
- SCEV::NoWrapFlags OrigFlags,
- unsigned Depth) {
+SCEVUse ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
+ SCEV::NoWrapFlags OrigFlags, unsigned Depth,
+ SCEV::NoWrapFlags UseFlags) {
assert(!(OrigFlags & ~(SCEV::FlagNUW | SCEV::FlagNSW)) &&
"only nuw or nsw allowed");
assert(!Ops.empty() && "Cannot get empty add!");
@@ -2658,14 +2658,14 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
// Limit recursion calls depth.
if (Depth > MaxArithDepth || hasHugeExpression(Ops))
- return getOrCreateAddExpr(Ops, ComputeFlags(Ops));
+ return SCEVUse(getOrCreateAddExpr(Ops, ComputeFlags(Ops)), UseFlags);
if (SCEV *S = findExistingSCEVInCache(scAddExpr, Ops)) {
// Don't strengthen flags if we have no new information.
SCEVAddExpr *Add = static_cast<SCEVAddExpr *>(S);
if (Add->getNoWrapFlags(OrigFlags) != OrigFlags)
Add->setNoWrapFlags(ComputeFlags(Ops));
- return S;
+ return SCEVUse(S, UseFlags);
}
// Okay, check to see if the same value occurs in the operand list more than
@@ -2829,6 +2829,9 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
// common NUW flag for expression after inlining. Other flags cannot be
// preserved, because they may depend on the original order of operations.
SCEV::NoWrapFlags CommonFlags = maskFlags(OrigFlags, SCEV::FlagNUW);
+ SCEV::NoWrapFlags CommonUseFlags = maskFlags(UseFlags, SCEV::FlagNUW);
+ // Track whether all inlined adds have NUW for use-specific flag
+ // preservation.
while (const SCEVAddExpr *Add = dyn_cast<SCEVAddExpr>(Ops[Idx])) {
if (Ops.size() > AddOpsInlineThreshold ||
Add->getNumOperands() > AddOpsInlineThreshold)
@@ -2839,13 +2842,14 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
append_range(Ops, Add->operands());
DeletedAdd = true;
CommonFlags = maskFlags(CommonFlags, Add->getNoWrapFlags());
+ CommonUseFlags = maskFlags(CommonUseFlags, Add->getNoWrapFlags());
}
// If we deleted at least one add, we added operands to the end of the list,
// and they are not necessarily sorted. Recurse to resort and resimplify
// any operands we just acquired.
if (DeletedAdd)
- return getAddExpr(Ops, CommonFlags, Depth + 1);
+ return getAddExpr(Ops, CommonFlags, Depth + 1, CommonUseFlags);
}
// Skip over the add expression until we get to a multiply.
@@ -3029,7 +3033,8 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
const SCEV *NewRec = getAddRecExpr(AddRecOps, AddRecLoop, Flags);
// If all of the other operands were loop invariant, we are done.
- if (Ops.size() == 1) return NewRec;
+ if (Ops.size() == 1)
+ return SCEVUse(NewRec, UseFlags);
// Otherwise, add the folded AddRec by the non-invariant parts.
for (unsigned i = 0;; ++i)
@@ -3084,7 +3089,7 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<SCEVUse> &Ops,
// Okay, it looks like we really DO need an add expr. Check to see if we
// already have one, otherwise create a new one.
- return getOrCreateAddExpr(Ops, ComputeFlags(Ops));
+ return SCEVUse(getOrCreateAddExpr(Ops, ComputeFlags(Ops)), UseFlags);
}
const SCEV *ScalarEvolution::getOrCreateAddExpr(ArrayRef<SCEVUse> Ops,
@@ -3966,33 +3971,16 @@ SCEVUse ScalarEvolution::getGEPExpr(SCEVUse BaseExpr,
bool NUW = NW.hasNoUnsignedWrap() ||
(NW.hasNoUnsignedSignedWrap() && isKnownNonNegative(Offset));
SCEV::NoWrapFlags BaseWrap = NUW ? SCEV::FlagNUW : SCEV::FlagAnyWrap;
- auto *GEPExpr = getAddExpr(BaseExpr, Offset, BaseWrap);
- assert(BaseExpr->getType() == GEPExpr->getType() &&
- "GEP should not change type mid-flight.");
+ SCEV::NoWrapFlags UseFlags = SCEV::FlagAnyWrap;
if (!NUW) {
if (UseSpecificNW.hasNoUnsignedWrap() ||
- (UseSpecificNW.hasNoUnsignedSignedWrap() && isKnownNonNegative(Offset))) {
- // Check if it is safe to annotate the expression with use-specific NUW.
- // Don't apply it if Base or Offset contain potentially wrapping
- // sub-expressions that could be flattened into a larger Add expression.
- auto IsSafeForUseNUW = [](SCEVUse S) {
- if (any(S.getUseNoWrapFlags() & SCEV::FlagNUW))
- return true;
- // Add and AddRec expressions could be flattened into a wider
- // add, so they need NUW to be safe.
- if (isa<SCEVAddExpr, SCEVAddRecExpr>(S))
- return S.hasNoUnsignedWrap();
- // Conservatively allow a small set of expression forms that are
- // known safe: they won't be flattened into a wider add.
- const SCEVUnknown *U;
- return match(
- S, m_CombineOr(m_scev_ZExt(m_SCEVUnknown(U)),
- m_CombineOr(m_SCEVUnknown(U), m_SCEVConstant())));
- };
- if (IsSafeForUseNUW(BaseExpr) && IsSafeForUseNUW(Offset))
- return SCEVUse(&*GEPExpr, SCEV::FlagNUW);
- }
+ (UseSpecificNW.hasNoUnsignedSignedWrap() && isKnownNonNegative(Offset)))
+ UseFlags = SCEV::FlagNUW;
}
+ SmallVector<SCEVUse, 2> AddOps = {BaseExpr, Offset};
+ auto GEPExpr = getAddExpr(AddOps, BaseWrap, /*Depth=*/0, UseFlags);
+ assert(BaseExpr->getType() == GEPExpr->getType() &&
+ "GEP should not change type mid-flight.");
return GEPExpr;
}
@@ -13612,7 +13600,7 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
//
// FIXME: Should isLoopEntryGuardedByCond do this for us?
auto CondGT = IsSigned ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT;
- auto *StartMinusOne =
+ auto StartMinusOne =
getAddExpr(OrigStart, getMinusOne(OrigStart->getType()));
return isLoopEntryGuardedByCond(L, CondGT, OrigRHS, StartMinusOne);
};
diff --git a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
index 98da1e9225172..5d5b3b48dd555 100644
--- a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
@@ -765,7 +765,7 @@ InductiveRangeCheck::computeSafeIterationSpace(ScalarEvolution &SE,
const SCEV *Zero = SE.getZero(M->getType());
// This function returns SCEV equal to 1 if X is non-negative 0 otherwise.
- auto SCEVCheckNonNegative = [&](const SCEV *X) {
+ auto SCEVCheckNonNegative = [&](const SCEV *X) -> const SCEV * {
const Loop *L = IndVar->getLoop();
const SCEV *Zero = SE.getZero(X->getType());
const SCEV *One = SE.getOne(X->getType());
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 5421cad31c3ba..83b866f411103 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -5820,9 +5820,8 @@ Value *LSRInstance::Expand(const LSRUse &LU, const LSRFixup &LF,
}
// Emit instructions summing all the operands.
- const SCEV *FullS = Ops.empty() ?
- SE.getConstant(IntTy, 0) :
- SE.getAddExpr(Ops);
+ const SCEV *FullS =
+ Ops.empty() ? SE.getConstant(IntTy, 0) : (const SCEV *)SE.getAddExpr(Ops);
Value *FullV = Rewriter.expandCodeFor(FullS, Ty);
// We're done expanding now, so reset the rewriter.
diff --git a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
index 9e2e32a38cfbd..e27d1b7b39752 100644
--- a/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
+++ b/llvm/test/Analysis/ScalarEvolution/use-specific-flags-gep.ll
@@ -33,9 +33,9 @@ define void @gep_i32_nonneg_offset(ptr %base, i32 %n) {
; CHECK-NEXT: %ext = zext i32 %n to i64
; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.plain = getelementptr i32, ptr %base, i64 %ext
; CHECK-NEXT: --> ((4 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_i32_nonneg_offset
@@ -100,7 +100,7 @@ define void @gep_i32_maybe_neg_offset(ptr %base, i64 %n) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %n
; CHECK-NEXT: --> ((4 * %n) + %base) U: full-set S: full-set
; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %n
-; CHECK-NEXT: --> ((4 * %n) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((4 * %n) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.plain = getelementptr i32, ptr %base, i64 %n
; CHECK-NEXT: --> ((4 * %n) + %base) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_i32_maybe_neg_offset
@@ -120,9 +120,9 @@ define void @gep_i64_nonneg_offset(ptr %base, i32 %n) {
; CHECK-NEXT: %ext = zext i32 %n to i64
; CHECK-NEXT: --> (zext i32 %n to i64) U: [0,4294967296) S: [0,4294967296)
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i64, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.nuw = getelementptr nuw i64, ptr %base, i64 %ext
-; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.plain = getelementptr i64, ptr %base, i64 %ext
; CHECK-NEXT: --> ((8 * (zext i32 %n to i64))<nuw><nsw> + %base) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_i64_nonneg_offset
@@ -143,7 +143,7 @@ define void @gep_i32_add_no_nuw_index(ptr %base, i64 %a, i64 %b) {
; CHECK-NEXT: %idx = add i64 %a, %b
; CHECK-NEXT: --> (%a + %b) U: full-set S: full-set
; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %idx
-; CHECK-NEXT: --> ((4 * (%a + %b)) + %base) U: full-set S: full-set
+; CHECK-NEXT: --> ((4 * (%a + %b)) + %base)(u nuw) U: full-set S: full-set
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %idx
; CHECK-NEXT: --> ((4 * (%a + %b)) + %base) U: full-set S: full-set
; CHECK-NEXT: Determining loop execution counts for: @gep_i32_add_no_nuw_index
@@ -364,9 +364,9 @@ define void @loop_gep_flags(ptr %base, i64 %n) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
-; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {%base,+,1}<%loop>(u nuw) U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv
-; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {%base,+,1}<%loop>(u nuw) U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %iv
; CHECK-NEXT: --> {%base,+,1}<%loop> U: full-set S: full-set Exits: (-1 + %n + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %iv.next = add i64 %iv, 1
@@ -406,9 +406,9 @@ define void @loop_inbounds_neg_step(ptr %base, i64 %n) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %base, i64 %iv
; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.nuw = getelementptr nuw i8, ptr %base, i64 %iv
-; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop>(u nuw) U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.nusw = getelementptr inbounds nuw i8, ptr %base, i64 %iv
-; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop>(u nuw) U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.plain = getelementptr i8, ptr %base, i64 %iv
; CHECK-NEXT: --> {(%n + %base),+,-1}<nw><%loop> U: full-set S: full-set Exits: (1 + (0 smin (-1 + %n)) + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %iv.next = add nsw i64 %iv, -1
@@ -459,9 +459,9 @@ define void @nested_loop_inbounds_addrecs(ptr %base, i64 %n, i64 %m) {
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i8, ptr %gep.outer, i64 %iv.inner
; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.nuw = getelementptr nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
-; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner>(u nuw) U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.nusw = getelementptr inbounds nuw i8, ptr %gep.outer.nuw, i64 %iv.inner
-; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
+; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner>(u nuw) U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %gep.inner.plain = getelementptr i8, ptr %gep.outer, i64 %iv.inner
; CHECK-NEXT: --> {{\{\{}}%base,+,1}<nw><%outer>,+,1}<nw><%inner> U: full-set S: full-set Exits: {(-1 + %m + %base),+,1}<nw><%outer> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %iv.inner.next = add nuw i64 %iv.inner, 1
@@ -561,7 +561,7 @@ define void @loop_gep_i32_nuw_iv(ptr %base, i64 %n) {
; CHECK-NEXT: %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
; CHECK-NEXT: --> {0,+,1}<nuw><%loop> U: full-set S: full-set Exits: (-1 + %n) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.nuw = getelementptr nuw i32, ptr %base, i64 %iv
-; CHECK-NEXT: --> {%base,+,4}<%loop> U: full-set S: full-set Exits: (-4 + (4 * %n) + %base) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {%base,+,4}<%loop>(u nuw) U: full-set S: full-set Exits: (-4 + (4 * %n) + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.inbounds = getelementptr inbounds i32, ptr %base, i64 %iv
; CHECK-NEXT: --> {%base,+,4}<%loop> U: full-set S: full-set Exits: (-4 + (4 * %n) + %base) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.plain = getelementptr i32, ptr %base, i64 %iv
More information about the llvm-commits
mailing list