|
| 1 | +# This file is a part of Julia. License is MIT: https://julialang.org/license |
| 2 | + |
| 3 | +# RUN: julia --startup-file=no %s %t && llvm-link -S %t/* -o %t/module.ll |
| 4 | +# RUN: cat %t/module.ll | FileCheck %s |
| 5 | +# RUN: cat %t/module.ll | opt -load libjulia%shlibext -LowerSIMDLoop -S - | FileCheck %s -check-prefix=LOWER |
| 6 | +# RUN: julia --startup-file=no %s %t -O && llvm-link -S %t/* -o %t/module.ll |
| 7 | +# RUN: cat %t/module.ll | FileCheck %s -check-prefix=FINAL |
| 8 | +using InteractiveUtils |
| 9 | +using Printf |
| 10 | + |
| 11 | +## Notes: |
| 12 | +# This script uses the `emit` function (defined at the end) to emit either |
| 13 | +# optimized or unoptimized LLVM IR. Each function is emitted individually and |
| 14 | +# `llvm-link` is used to create a single module that can be passed to opt. |
| 15 | +# The order in which files are emitted and linked is important since `lit` will |
| 16 | +# process the test cases in order. |
| 17 | +# |
| 18 | +# There are three different test prefixes defined: |
| 19 | +# - `CHECK`: Checks the result of codegen |
| 20 | +# - `LOWER`: Checks the result of -LowerSIMDLoop |
| 21 | +# - `FINAL`: Checks the result of running the entire pipeline |
| 22 | + |
| 23 | +# get a temporary directory |
| 24 | +dir = ARGS[1] |
| 25 | +rm(dir, force=true, recursive=true) |
| 26 | +mkdir(dir) |
| 27 | + |
| 28 | +# toggle between unoptimized (CHECK/LOWER) and optimized IR (FINAL). |
| 29 | +optimize=false |
| 30 | +if length(ARGS) >= 2 |
| 31 | + optimize = ARGS[2]=="-O" |
| 32 | +end |
| 33 | + |
| 34 | +# CHECK-LABEL: @julia_simdf_ |
| 35 | +# LOWER-LABEL: @julia_simdf_ |
| 36 | +# FINAL-LABEL: @julia_simdf_ |
| 37 | +function simdf(X) |
| 38 | + acc = zero(eltype(X)) |
| 39 | + @simd for x in X |
| 40 | + acc += x |
| 41 | +# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO:![0-9]+]] |
| 42 | +# LOWER-NOT: llvm.mem.parallel_loop_access |
| 43 | +# LOWER: fadd fast double |
| 44 | +# LOWER-NOT: call void @julia.loopinfo_marker() |
| 45 | +# LOWER: br {{.*}}, !llvm.loop [[LOOPID:![0-9]+]] |
| 46 | +# FINAL: fadd fast <{{[0-9]+}} x double> |
| 47 | + end |
| 48 | + acc |
| 49 | +end |
| 50 | + |
| 51 | +# CHECK-LABEL: @julia_simdf2_ |
| 52 | +# LOWER-LABEL: @julia_simdf2_ |
| 53 | +function simdf2(X) |
| 54 | + acc = zero(eltype(X)) |
| 55 | + @simd ivdep for x in X |
| 56 | + acc += x |
| 57 | +# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO2:![0-9]+]] |
| 58 | +# LOWER: llvm.mem.parallel_loop_access |
| 59 | +# LOWER-NOT: call void @julia.loopinfo_marker() |
| 60 | +# LOWER: fadd fast double |
| 61 | +# LOWER: br {{.*}}, !llvm.loop [[LOOPID2:![0-9]+]] |
| 62 | + end |
| 63 | + acc |
| 64 | +end |
| 65 | + |
| 66 | +@noinline iteration(i) = (@show(i); return nothing) |
| 67 | + |
| 68 | +# CHECK-LABEL: @julia_loop_unroll |
| 69 | +# LOWER-LABEL: @julia_loop_unroll |
| 70 | +# FINAL-LABEL: @julia_loop_unroll |
| 71 | +@eval function loop_unroll(N) |
| 72 | + for i in 1:N |
| 73 | + iteration(i) |
| 74 | + $(Expr(:loopinfo, (Symbol("llvm.loop.unroll.count"), 3))) |
| 75 | +# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO3:![0-9]+]] |
| 76 | +# LOWER-NOT: call void @julia.loopinfo_marker() |
| 77 | +# LOWER: br {{.*}}, !llvm.loop [[LOOPID3:![0-9]+]] |
| 78 | +# FINAL: call void @julia_iteration |
| 79 | +# FINAL: call void @julia_iteration |
| 80 | +# FINAL: call void @julia_iteration |
| 81 | +# FINAL-NOT: call void @julia_iteration |
| 82 | +# FINAL: br |
| 83 | + end |
| 84 | +end |
| 85 | + |
| 86 | +# Example from a GPU kernel where we want to unroll the outer loop |
| 87 | +# and the inner loop is a boundschecked single iteration loop. |
| 88 | +# The `@show` is used to bloat the loop and `FINAL-COUNT-10:` seems |
| 89 | +# not to be working so we duplicate the checks. |
| 90 | +# CHECK-LABEL: @julia_loop_unroll2 |
| 91 | +# LOWER-LABEL: @julia_loop_unroll2 |
| 92 | +# FINAL-LABEL: @julia_loop_unroll2 |
| 93 | +@eval function loop_unroll2(J, I) |
| 94 | + for i in 1:10 |
| 95 | + for j in J |
| 96 | + 1 <= j <= I && continue |
| 97 | + @show (i,j) |
| 98 | + iteration(i) |
| 99 | + end |
| 100 | + $(Expr(:loopinfo, (Symbol("llvm.loop.unroll.full"),))) |
| 101 | +# CHECK: call void @julia.loopinfo_marker(), {{.*}}, !julia.loopinfo [[LOOPINFO4:![0-9]+]] |
| 102 | +# LOWER-NOT: call void @julia.loopinfo_marker() |
| 103 | +# LOWER: br {{.*}}, !llvm.loop [[LOOPID4:![0-9]+]] |
| 104 | +# FINAL: call void @julia_iteration |
| 105 | +# FINAL: call void @julia_iteration |
| 106 | +# FINAL: call void @julia_iteration |
| 107 | +# FINAL: call void @julia_iteration |
| 108 | +# FINAL: call void @julia_iteration |
| 109 | +# FINAL: call void @julia_iteration |
| 110 | +# FINAL: call void @julia_iteration |
| 111 | +# FINAL: call void @julia_iteration |
| 112 | +# FINAL: call void @julia_iteration |
| 113 | +# FINAL: call void @julia_iteration |
| 114 | +# FINAL-NOT: call void @julia_iteration |
| 115 | + end |
| 116 | +end |
| 117 | + |
| 118 | +# FINAL-LABEL: @julia_notunroll |
| 119 | +function notunroll(J, I) |
| 120 | + for i in 1:10 |
| 121 | + for j in J |
| 122 | + 1 <= j <= I && continue |
| 123 | + @show (i,j) |
| 124 | + iteration(i) |
| 125 | +# FINAL: call void @julia_iteration |
| 126 | +# FINAL-NOT: call void @julia_iteration |
| 127 | + end |
| 128 | + end |
| 129 | +end |
| 130 | + |
| 131 | +## Check all the MD nodes |
| 132 | +# CHECK: [[LOOPINFO]] = !{!"julia.simdloop"} |
| 133 | +# CHECK: [[LOOPINFO2]] = !{!"julia.simdloop", !"julia.ivdep"} |
| 134 | +# CHECK: [[LOOPINFO3]] = !{[[LOOPUNROLL:![0-9]+]]} |
| 135 | +# CHECK: [[LOOPUNROLL]] = !{!"llvm.loop.unroll.count", i64 3} |
| 136 | +# CHECK: [[LOOPINFO4]] = !{[[LOOPUNROLL2:![0-9]+]]} |
| 137 | +# CHECK: [[LOOPUNROLL2]] = !{!"llvm.loop.unroll.full"} |
| 138 | +# LOWER: [[LOOPID]] = distinct !{[[LOOPID]]} |
| 139 | +# LOWER: [[LOOPID2]] = distinct !{[[LOOPID2]]} |
| 140 | +# LOWER: [[LOOPID3]] = distinct !{[[LOOPID3]], [[LOOPUNROLL:![0-9]+]]} |
| 141 | +# LOWER: [[LOOPUNROLL]] = !{!"llvm.loop.unroll.count", i64 3} |
| 142 | +# LOWER: [[LOOPID4]] = distinct !{[[LOOPID4]], [[LOOPUNROLL2:![0-9]+]]} |
| 143 | +# LOWER: [[LOOPUNROLL2]] = !{!"llvm.loop.unroll.full"} |
| 144 | + |
| 145 | +# Emit LLVM IR to dir |
| 146 | +counter = 0 |
| 147 | +function emit(f, tt...) |
| 148 | + global counter |
| 149 | + name = nameof(f) |
| 150 | + open(joinpath(dir, @sprintf("%05d-%s.ll", counter, name)), "w") do io |
| 151 | + code_llvm(io, f, tt, raw=true, optimize=optimize, dump_module=true, debuginfo=:none) |
| 152 | + end |
| 153 | + counter+=1 |
| 154 | +end |
| 155 | + |
| 156 | +# Maintaining the order is important |
| 157 | +emit(simdf, Vector{Float64}) |
| 158 | +emit(simdf2, Vector{Float64}) |
| 159 | +emit(loop_unroll, Int64) |
| 160 | +emit(loop_unroll2, Int64, Int64) |
| 161 | +emit(notunroll, Int64, Int64) |
0 commit comments