[llvm-dev] Loop Unroll (original) (raw)
Florian Hahn via llvm-dev llvm-dev at lists.llvm.org
Sun May 24 05:36:10 PDT 2020
- Previous message: [llvm-dev] Loop Unroll
- Next message: [llvm-dev] Loop Unroll
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On May 23, 2020, at 17:15, legend xx <legendaryxx7slh at gmail.com> wrote:
This is my example (for.c): #include <stdio.h> int add(int a, int b) { return a + b; } int main() { int a, b, c, d; a = 5; b = 15; c = add(a, b); d = 0; for(int i=0;i<16;i++)_ _d = add(c, d);_ _}_ _I run:_ _$ clang -O0 -Xclang -disable-O0-optnone -emit-llvm for.c -S -o forO0.ll_ _$ opt -O0 -S --loop-unroll --unroll-count=4 -view-cfg forO0.ll -o for-opt00-unroll4.ll_ _And this is the LLVM IR code that I get:_ _; ModuleID = 'forO0.ll'_ _sourcefilename = "for.c"_ _target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"_ _target triple = "x8664-unknown-linux-gnu"_ _; Function Attrs: noinline nounwind uwtable_ _define dsolocal i32 @add(i32 %a, i32 %b) #0 {_ _entry:_ _%a.addr = alloca i32, align 4_ _%b.addr = alloca i32, align 4_ _store i32 %a, i32* %a.addr, align 4_ _store i32 %b, i32* %b.addr, align 4_ _%0 = load i32, i32* %a.addr, align 4_ _%1 = load i32, i32* %b.addr, align 4_ _%add = add nsw i32 %0, %1_ _ret i32 %add_ _}_ _; Function Attrs: noinline nounwind uwtable_ _define dsolocal i32 @main() #0 {_ _entry:_ _%retval = alloca i32, align 4_ _%a = alloca i32, align 4_ _%b = alloca i32, align 4_ _%c = alloca i32, align 4_ _%d = alloca i32, align 4_ _%i = alloca i32, align 4_ _store i32 0, i32* %retval, align 4_ _store i32 5, i32* %a, align 4_ _store i32 15, i32* %b, align 4_ _%0 = load i32, i32* %a, align 4_ _%1 = load i32, i32* %b, align 4_ _%call = call i32 @add(i32 %0, i32 %1)_ _store i32 %call, i32* %c, align 4_ _store i32 0, i32* %d, align 4_ _store i32 0, i32* %i, align 4_ _br label %for.cond_ _for.cond: ; preds = %for.inc.3, %entry_ _%2 = load i32, i32* %i, align 4_ _%cmp = icmp slt i32 %2, 16_ _br i1 %cmp, label %for.body, label %for.end_ _for.body: ; preds = %for.cond_ _%3 = load i32, i32* %c, align 4_ _%4 = load i32, i32* %d, align 4_ _%call1 = call i32 @add(i32 %3, i32 %4)_ _store i32 %call1, i32* %d, align 4_ _br label %for.inc_ _for.inc: ; preds = %for.body_ _%5 = load i32, i32* %i, align 4_ _%inc = add nsw i32 %5, 1_ _store i32 %inc, i32* %i, align 4_ _%6 = load i32, i32* %i, align 4_ _%cmp.1 = icmp slt i32 %6, 16_ _br i1 %cmp.1, label %for.body.1, label %for.end_ _for.end: ; preds = %for.inc.2, %for.inc.1, %for.inc, %for.cond_ _%7 = load i32, i32* %d, align 4_ _%call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str, i64 0, i64 0), i32 %7)_ _%8 = load i32, i32* %retval, align 4_ _ret i32 %8_ _for.body.1: ; preds = %for.inc_ _%9 = load i32, i32* %c, align 4_ _%10 = load i32, i32* %d, align 4_ _%call1.1 = call i32 @add(i32 %9, i32 %10)_ _store i32 %call1.1, i32* %d, align 4_ _br label %for.inc.1_ _for.inc.1: ; preds = %for.body.1_ _%11 = load i32, i32* %i, align 4_ _%inc.1 = add nsw i32 %11, 1_ _store i32 %inc.1, i32* %i, align 4_ _%12 = load i32, i32* %i, align 4_ _%cmp.2 = icmp slt i32 %12, 16_ _br i1 %cmp.2, label %for.body.2, label %for.end_ _for.body.2: ; preds = %for.inc.1_ _%13 = load i32, i32* %c, align 4_ _%14 = load i32, i32* %d, align 4_ _%call1.2 = call i32 @add(i32 %13, i32 %14)_ _store i32 %call1.2, i32* %d, align 4_ _br label %for.inc.2_ _for.inc.2: ; preds = %for.body.2_ _%15 = load i32, i32* %i, align 4_ _%inc.2 = add nsw i32 %15, 1_ _store i32 %inc.2, i32* %i, align 4_ _%16 = load i32, i32* %i, align 4_ _%cmp.3 = icmp slt i32 %16, 16_ _br i1 %cmp.3, label %for.body.3, label %for.end_ _for.body.3: ; preds = %for.inc.2_ _%17 = load i32, i32* %c, align 4_ _%18 = load i32, i32* %d, align 4_ _%call1.3 = call i32 @add(i32 %17, i32 %18)_ _store i32 %call1.3, i32* %d, align 4_ _br label %for.inc.3_ _for.inc.3: ; preds = %for.body.3_ _%19 = load i32, i32* %i, align 4_ _%inc.3 = add nsw i32 %19, 1_ _store i32 %inc.3, i32* %i, align 4_ _br label %for.cond, !llvm.loop !2_ _}_ _declare dsolocal i32 @printf(i8*, ...) #1_ _attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }_ _attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }_ _!llvm.module.flags = !{!0}_ _!llvm.ident = !{!1}_ _!0 = !{i32 1, !"wcharsize", i32 4}_ _!1 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git <https://github.com/llvm/llvm-project.git> a3485301d4870f57590d7b69eed7959134a694ab)"} !2 = distinct !{!2, !3} !3 = !{!"llvm.loop.unroll.disable"}
So my problem is: With unroll 4 on the loop with 16 bounds I should see one single block for the incrementation i=i+4, then 4 instructions for each previous one instruction, and the condition should check if i<16. This is the intuitive code. However, the incrementation that I get is i=i+1 and there are only 4 blocks. Do you know why this happen?
I think loop-unroll works as expected in your example, as you can see the copies of the unrolled loop blocks (for.body.X, for.inc.X). The reason this is not simplified to the single block you are expecting is the input for -loop-unroll: -loop-unroll gets the IR without any optimizations (-O0).
For the expected result, you need to run a few additional passes before -loop-unroll to promote some of the loads/stores to registers and simplify the CFG of the input. Running opt -mem2reg -simplifycfg -loop-unroll -unroll-count=4 forO0.ll -S
should give you something like
define i32 @main() #0 { entry: %call = call i32 @add(i32 5, i32 15) br label %for.cond
for.cond: ; preds = %for.body.3, %entry %d.0 = phi i32 [ 0, %entry ], [ %call1.3, %for.body.3 ] %i.0 = phi i32 [ 0, %entry ], [ %inc.3, %for.body.3 ] %cmp = icmp ult i32 %i.0, 16 br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond %call1 = call i32 @add(i32 %call, i32 %d.0) %inc = add nuw nsw i32 %i.0, 1 br label %for.body.1
for.end: ; preds = %for.cond ret i32 0
for.body.1: ; preds = %for.body %call1.1 = call i32 @add(i32 %call, i32 %call1) %inc.1 = add nuw nsw i32 %inc, 1 br label %for.body.2
for.body.2: ; preds = %for.body.1 %call1.2 = call i32 @add(i32 %call, i32 %call1.1) %inc.2 = add nuw nsw i32 %inc.1, 1 br label %for.body.3
for.body.3: ; preds = %for.body.2 %call1.3 = call i32 @add(i32 %call, i32 %call1.2) %inc.3 = add nuw nsw i32 %inc.2, 1 br label %for.cond, !llvm.loop !4 }
Note that there are still 4 copies of the body instead of a single one. Like many passes in LLVM, the loop-unroll pass focuses on performing one transformation (duplicating the loop body a number of times) and relies on other passes to clean-up/simplify the result. To fold the 4 copies of the body into a single block, you need another round of CFG simplifications. Running opt -mem2reg -simplifycfg -loop-unroll -unroll-count=4 -simplifycfg forO0.ll -S
produces the code below, which is what you are looking for IIUC.
define i32 @main() #0 { entry: %call = call i32 @add(i32 5, i32 15) br label %for.cond
for.cond: ; preds = %for.body, %entry %d.0 = phi i32 [ 0, %entry ], [ %call1.3, %for.body ] %i.0 = phi i32 [ 0, %entry ], [ %inc.3, %for.body ] %cmp = icmp ult i32 %i.0, 16 br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond %call1 = call i32 @add(i32 %call, i32 %d.0) %inc = add nuw nsw i32 %i.0, 1 %call1.1 = call i32 @add(i32 %call, i32 %call1) %inc.1 = add nuw nsw i32 %inc, 1 %call1.2 = call i32 @add(i32 %call, i32 %call1.1) %inc.2 = add nuw nsw i32 %inc.1, 1 %call1.3 = call i32 @add(i32 %call, i32 %call1.2) %inc.3 = add nuw nsw i32 %inc.2, 1 br label %for.cond, !llvm.loop !4
for.end: ; preds = %for.cond ret i32 0 }
-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200524/32b2c9ed/attachment-0001.html>
- Previous message: [llvm-dev] Loop Unroll
- Next message: [llvm-dev] Loop Unroll
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]