[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
Mon Apr 6 13:42:10 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/190677
None
>From 45b89452295bad40877f882cb5277b7f339e2eba 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/2] [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 91394c49fadec694b0bb64e208d6216c197dbb2a 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/2] [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 ++---
9 files changed, 157 insertions(+), 72 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 a560c324b4f1e..520bd048807cd 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 207099422905d..4e5dc1ee98679 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -4981,7 +4981,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
More information about the llvm-commits
mailing list