[polly] 00cc5ac - [Polly] Narrow IV to lower type when possible (#212708)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 12 23:25:43 PDT 2026


Author: anilkund
Date: 2026-08-13T11:55:37+05:30
New Revision: 00cc5ac48769908583c25ae85f86b31b1696da6a

URL: https://github.com/llvm/llvm-project/commit/00cc5ac48769908583c25ae85f86b31b1696da6a
DIFF: https://github.com/llvm/llvm-project/commit/00cc5ac48769908583c25ae85f86b31b1696da6a.diff

LOG: [Polly] Narrow IV to lower type when possible (#212708)

This patch tries to lower the LLVM-IR type of IVs which are set to i64
by default during polly codegen. This is specially beneficial for
Hexagon as it uses Hardware Loops which requires 32 bit wide IVs.

---------

Co-authored-by: Anilava Kundu <anilkund at hu-anilkund-blr.qualcomm.com>
Co-authored-by: Santanu Das <santdas at qti.qualcomm.com>

Added: 
    polly/test/CodeGen/iv_narrow_32bit_target.ll

Modified: 
    polly/lib/CodeGen/IslNodeBuilder.cpp
    polly/test/CodeGen/MemAccess/codegen_simple_md.ll
    polly/test/CodeGen/MemAccess/codegen_simple_md_float.ll
    polly/test/ScopInfo/int2ptr_ptr2int.ll
    polly/test/ScopInfo/int2ptr_ptr2int_2.ll

Removed: 
    


################################################################################
diff  --git a/polly/lib/CodeGen/IslNodeBuilder.cpp b/polly/lib/CodeGen/IslNodeBuilder.cpp
index 93822e5c61615..50de49b37a6c2 100644
--- a/polly/lib/CodeGen/IslNodeBuilder.cpp
+++ b/polly/lib/CodeGen/IslNodeBuilder.cpp
@@ -464,6 +464,27 @@ static bool hasLoopCarriedDependence(isl::ast_node_for For, const Scop &S) {
   return false;
 }
 
+/// Sign-extend or truncate V to Ty.
+///
+/// Returns V unchanged if it already has type Ty, sign-extends it if
+/// Ty is wider, or truncates it if Ty is narrower.
+static Value *castToType(IRBuilderBase &Builder, Value *V, Type *Ty) {
+  if (V->getType() == Ty)
+    return V;
+  return Builder.CreateSExtOrTrunc(V, Ty);
+}
+
+/// Returns true when V is known to fit in IntPtrTy without data loss.
+/// Accepts i64 constants such as 0 and 1 that ISL materialises as i64 even on
+/// 32-bit targets.
+static bool fitsInTy(Value *V, IntegerType *IntTy) {
+  if (V->getType()->getIntegerBitWidth() <= IntTy->getBitWidth())
+    return true;
+  if (auto *CI = dyn_cast<ConstantInt>(V))
+    return CI->getValue().isSignedIntN(IntTy->getBitWidth());
+  return false;
+}
+
 void IslNodeBuilder::createForSequential(isl::ast_node_for For,
                                          bool MarkParallel) {
   Value *ValueLB, *ValueUB, *ValueInc;
@@ -497,12 +518,23 @@ void IslNodeBuilder::createForSequential(isl::ast_node_for For,
   MaxType = ExprBuilder.getWidestType(MaxType, ValueUB->getType());
   MaxType = ExprBuilder.getWidestType(MaxType, ValueInc->getType());
 
-  if (MaxType != ValueLB->getType())
-    ValueLB = Builder.CreateSExt(ValueLB, MaxType);
-  if (MaxType != ValueUB->getType())
-    ValueUB = Builder.CreateSExt(ValueUB, MaxType);
-  if (MaxType != ValueInc->getType())
-    ValueInc = Builder.CreateSExt(ValueInc, MaxType);
+  // Narrow the IV type to pointer size when all three bounds are known to fit.
+  // On 32-bit targets (e.g. Hexagon) this avoids i64 IVs and the truncations
+  // they cause in loop bodies. This also allows Hexagon to represent loops as
+  // Hardware loops. ISL materializes constants (e.g. LB=0, Inc=1)
+  // as i64 even when they fit in i32, so we accept those via isSignedIntN.
+  // Non-constant variables with a type wider than PtrBits are left unchanged
+  // to avoid an unsafe truncation.
+  IntegerType *IntPtrTy = Builder.getIntPtrTy(DL);
+  if (MaxType->getIntegerBitWidth() > IntPtrTy->getBitWidth() &&
+      fitsInTy(ValueLB, IntPtrTy) && fitsInTy(ValueUB, IntPtrTy) &&
+      fitsInTy(ValueInc, IntPtrTy))
+    MaxType = IntPtrTy;
+
+  // Coerce each bound to MaxType, using trunc when MaxType was narrowed.
+  ValueLB = castToType(Builder, ValueLB, MaxType);
+  ValueUB = castToType(Builder, ValueUB, MaxType);
+  ValueInc = castToType(Builder, ValueInc, MaxType);
 
   // If we can show that LB <Predicate> UB holds at least once, we can
   // omit the GuardBB in front of the loop.
@@ -577,12 +609,22 @@ void IslNodeBuilder::createForParallel(__isl_take isl_ast_node *For) {
   MaxType = ExprBuilder.getWidestType(MaxType, ValueUB->getType());
   MaxType = ExprBuilder.getWidestType(MaxType, ValueInc->getType());
 
-  if (MaxType != ValueLB->getType())
-    ValueLB = Builder.CreateSExt(ValueLB, MaxType);
-  if (MaxType != ValueUB->getType())
-    ValueUB = Builder.CreateSExt(ValueUB, MaxType);
-  if (MaxType != ValueInc->getType())
-    ValueInc = Builder.CreateSExt(ValueInc, MaxType);
+  // Narrow the IV type to pointer size when all three bounds are known to fit.
+  // On 32-bit targets (e.g. Hexagon) this avoids i64 IVs and the truncations
+  // they cause in loop bodies. ISL materializes constants (e.g. LB=0, Inc=1)
+  // as i64 even when they fit in i32, so we accept those via isSignedIntN.
+  // Non-constant variables with a type wider than PtrBits are left unchanged
+  // to avoid an unsafe truncation.
+  IntegerType *IntPtrTy = Builder.getIntPtrTy(DL);
+  if (MaxType->getIntegerBitWidth() > IntPtrTy->getBitWidth() &&
+      fitsInTy(ValueLB, IntPtrTy) && fitsInTy(ValueUB, IntPtrTy) &&
+      fitsInTy(ValueInc, IntPtrTy))
+    MaxType = IntPtrTy;
+
+  // Coerce each bound to MaxType, using trunc when MaxType was narrowed.
+  ValueLB = castToType(Builder, ValueLB, MaxType);
+  ValueUB = castToType(Builder, ValueUB, MaxType);
+  ValueInc = castToType(Builder, ValueInc, MaxType);
 
   BasicBlock::iterator LoopBody;
 

diff  --git a/polly/test/CodeGen/MemAccess/codegen_simple_md.ll b/polly/test/CodeGen/MemAccess/codegen_simple_md.ll
index a6d9969286fc7..681e3e01790d4 100644
--- a/polly/test/CodeGen/MemAccess/codegen_simple_md.ll
+++ b/polly/test/CodeGen/MemAccess/codegen_simple_md.ll
@@ -54,19 +54,23 @@ for.end6:                                         ; preds = %for.cond
   ret i32 0
 }
 
-; WITHCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[IVOut]]
-; WITHCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]]
+; WITHCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHCONST:  %[[EXTOut:[._a-zA-Z0-9]+]] = sext i32 %[[IVOut]] to i64
+; WITHCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[EXTOut]]
+; WITHCONST:  %[[EXTIn:[._a-zA-Z0-9]+]] = sext i32 %[[IVIn]] to i64
+; WITHCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[EXTIn]]
 ; WITHCONST:  %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]]
 ; WITHCONST:  %[[SUM2:[._a-zA-Z0-9]+]] = add nsw i64 %[[SUM1]], 5
 ; WITHCONST:  %[[ACC:[._a-zA-Z0-9]*]] = getelementptr i32, ptr @A, i64 %[[SUM2]]
 ; WITHCONST:  store i32 100, ptr %[[ACC]]
 
-; WITHOUTCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHOUTCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHOUTCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[IVOut]]
-; WITHOUTCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]]
+; WITHOUTCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHOUTCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHOUTCONST:  %[[EXTOut:[._a-zA-Z0-9]+]] = sext i32 %[[IVOut]] to i64
+; WITHOUTCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[EXTOut]]
+; WITHOUTCONST:  %[[EXTIn:[._a-zA-Z0-9]+]] = sext i32 %[[IVIn]] to i64
+; WITHOUTCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[EXTIn]]
 ; WITHOUTCONST:  %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]]
 ; WITHOUTCONST:  %[[ACC:[._a-zA-Z0-9]*]] = getelementptr i32, ptr @A, i64 %[[SUM1]]
 ; WITHOUTCONST:  store i32 100, ptr %[[ACC]]

diff  --git a/polly/test/CodeGen/MemAccess/codegen_simple_md_float.ll b/polly/test/CodeGen/MemAccess/codegen_simple_md_float.ll
index 568b0ff4ae20a..26babfbbbedc2 100644
--- a/polly/test/CodeGen/MemAccess/codegen_simple_md_float.ll
+++ b/polly/test/CodeGen/MemAccess/codegen_simple_md_float.ll
@@ -51,19 +51,23 @@ for.end6:                                         ; preds = %for.cond
   ret void
 }
 
-; WITHCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[IVOut]]
-; WITHCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]]
+; WITHCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHCONST:  %[[EXTOut:[._a-zA-Z0-9]+]] = sext i32 %[[IVOut]] to i64
+; WITHCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[EXTOut]]
+; WITHCONST:  %[[EXTIn:[._a-zA-Z0-9]+]] = sext i32 %[[IVIn]] to i64
+; WITHCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[EXTIn]]
 ; WITHCONST:  %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]]
 ; WITHCONST:  %[[SUM2:[._a-zA-Z0-9]+]] = add nsw i64 %[[SUM1]], 5
 ; WITHCONST:  %[[ACC:[._a-zA-Z0-9]*]] = getelementptr float, ptr @A, i64 %[[SUM2]]
 ; WITHCONST:  store float 1.000000e+02, ptr %[[ACC]]
 
-; WITHOUTCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHOUTCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
-; WITHOUTCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[IVOut]]
-; WITHOUTCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]]
+; WITHOUTCONST:  %[[IVOut:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHOUTCONST:  %[[IVIn:polly.indvar[0-9]*]] = phi i32 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ]
+; WITHOUTCONST:  %[[EXTOut:[._a-zA-Z0-9]+]] = sext i32 %[[IVOut]] to i64
+; WITHOUTCONST:  %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[EXTOut]]
+; WITHOUTCONST:  %[[EXTIn:[._a-zA-Z0-9]+]] = sext i32 %[[IVIn]] to i64
+; WITHOUTCONST:  %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[EXTIn]]
 ; WITHOUTCONST:  %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]]
 ; WITHOUTCONST:  %[[ACC:[._a-zA-Z0-9]*]] = getelementptr float, ptr @A, i64 %[[SUM1]]
 ; WITHOUTCONST:  store float 1.000000e+02, ptr %[[ACC]]

diff  --git a/polly/test/CodeGen/iv_narrow_32bit_target.ll b/polly/test/CodeGen/iv_narrow_32bit_target.ll
new file mode 100644
index 0000000000000..25401bd80aa1e
--- /dev/null
+++ b/polly/test/CodeGen/iv_narrow_32bit_target.ll
@@ -0,0 +1,77 @@
+; RUN: opt %loadNPMPolly '-passes=polly<no-default-opts>' -S < %s | FileCheck %s
+
+; Verify that Polly narrows the loop induction variable to i32 on a 32-bit
+; target (e.g. Hexagon) when all loop bounds fit in 32 bits, and that it does
+; NOT narrow when the lower bound is a wide non-constant i64 variable.
+;
+;   void narrow(int *A, int n) {
+;     for (int i = 0; i < n; i++)
+;       A[i] = i;
+;   }
+
+; 32-bit Hexagon-like target: pointer size = 32 bits.
+target datalayout = "e-m:e-p:32:32:32-i64:64:64-i128:128:128-n32-S128"
+target triple = "hexagon-unknown-linux-musl"
+
+; CHECK-LABEL: @narrow
+define void @narrow(ptr noalias %A, i32 %n) {
+entry:
+  br label %for.header
+
+for.header:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %for.body ]
+  %exitcond = icmp slt i32 %i, %n
+  br i1 %exitcond, label %for.body, label %exit
+
+for.body:
+  %gep = getelementptr inbounds i32, ptr %A, i32 %i
+  store i32 %i, ptr %gep
+  %i.next = add nsw i32 %i, 1
+  br label %for.header
+
+exit:
+  ret void
+}
+
+; The IV must be i32 and all loop arithmetic must stay in i32.
+; CHECK:      polly.loop_header:
+; CHECK-NEXT:   %polly.indvar = phi i32
+; CHECK:        %polly.indvar_next = add nsw i32 %polly.indvar
+; CHECK:        %polly.loop_cond = icmp slt i32 %polly.indvar_next
+
+
+; When the lower bound is a non-constant i64 variable the guard must block
+; narrowing to avoid an unsafe truncation of %start.
+;
+;   void no_narrow_wide_lb(int *A, long start, int n) {
+;     for (long i = start; i < n; i++)
+;       A[i] = (int)i;
+;   }
+
+; CHECK-LABEL: @no_narrow_wide_lb
+define void @no_narrow_wide_lb(ptr noalias %A, i64 %start, i32 %n) {
+entry:
+  br label %for.header
+
+for.header:
+  %i = phi i64 [ %start, %entry ], [ %i.next, %for.body ]
+  %n64 = sext i32 %n to i64
+  %exitcond = icmp slt i64 %i, %n64
+  br i1 %exitcond, label %for.body, label %exit
+
+for.body:
+  %gep = getelementptr inbounds i32, ptr %A, i64 %i
+  %ival = trunc i64 %i to i32
+  store i32 %ival, ptr %gep
+  %i.next = add nsw i64 %i, 1
+  br label %for.header
+
+exit:
+  ret void
+}
+
+; The IV must remain i64 because %start is a wide non-constant variable.
+; Polly normalises the loop to start from 0, but keeps the type as i64.
+; CHECK:      polly.loop_header:
+; CHECK-NEXT:   %polly.indvar = phi i64
+; CHECK:        %polly.indvar_next = add nsw i64 %polly.indvar

diff  --git a/polly/test/ScopInfo/int2ptr_ptr2int.ll b/polly/test/ScopInfo/int2ptr_ptr2int.ll
index 7c3e4b060b662..47b5c25f69a63 100644
--- a/polly/test/ScopInfo/int2ptr_ptr2int.ll
+++ b/polly/test/ScopInfo/int2ptr_ptr2int.ll
@@ -26,8 +26,8 @@
 ; IR-NEXT:   %tmp4_p_scalar_ = load i64, ptr %scevgep, align 8, !alias.scope !2, !noalias !5
 ; IR-NEXT:   %p_add4 = add nsw i64 %tmp4_p_scalar_, %tmp3_p_scalar_
 ; IR-NEXT:   store i64 %p_add4, ptr %scevgep, align 8, !alias.scope !2, !noalias !5
-; IR-NEXT:   %polly.indvar_next = add nsw i64 %polly.indvar, 1
-; IR-NEXT:   %polly.loop_cond = icmp sle i64 %polly.indvar_next, 99
+; IR-NEXT:   %polly.indvar_next = add nsw i32 %polly.indvar, 1
+; IR-NEXT:   %polly.loop_cond = icmp sle i32 %polly.indvar_next, 99
 ; IR-NEXT:   br i1 %polly.loop_cond, label %polly.loop_header, label %polly.loop_exit
 ;
 ; IR:      polly.loop_preheader:

diff  --git a/polly/test/ScopInfo/int2ptr_ptr2int_2.ll b/polly/test/ScopInfo/int2ptr_ptr2int_2.ll
index 77293529e9cf7..ee0dc7c124f96 100644
--- a/polly/test/ScopInfo/int2ptr_ptr2int_2.ll
+++ b/polly/test/ScopInfo/int2ptr_ptr2int_2.ll
@@ -22,8 +22,8 @@
 ; IR-NEXT:   %tmp4_p_scalar_ = load i64, ptr %scevgep, align 8, !alias.scope !5, !noalias !2
 ; IR-NEXT:   %p_add4 = add nsw i64 %tmp4_p_scalar_, %polly.preload.tmp3.merge
 ; IR-NEXT:   store i64 %p_add4, ptr %scevgep, align 8, !alias.scope !5, !noalias !2
-; IR-NEXT:   %polly.indvar_next = add nsw i64 %polly.indvar, 1
-; IR-NEXT:   %polly.loop_cond = icmp sle i64 %polly.indvar_next, 99
+; IR-NEXT:   %polly.indvar_next = add nsw i16 %polly.indvar, 1
+; IR-NEXT:   %polly.loop_cond = icmp sle i16 %polly.indvar_next, 99
 ; IR-NEXT:   br i1 %polly.loop_cond, label %polly.loop_header, label %polly.loop_exit
 
 ; IR:      polly.loop_preheader:


        


More information about the llvm-commits mailing list