Skip to content

Commit d222248

Browse files
committed
add experimental aliasscopes API
1 parent 50ec39c commit d222248

File tree

5 files changed

+143
-25
lines changed

5 files changed

+143
-25
lines changed

base/Base.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,9 @@ include("util.jl")
349349

350350
include("asyncmap.jl")
351351

352+
# experimental API's
353+
include("experimental.jl")
354+
352355
# deprecated functions
353356
include("deprecated.jl")
354357

base/experimental.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
"""
4+
Experimental
5+
6+
!!! warning
7+
Types, methods, or macros defined in this module are experimental and subject
8+
to change and will not have deprecations. Caveat emptor.
9+
"""
10+
module Experimental
11+
12+
"""
13+
Const(A::Array)
14+
15+
Mark an Array as constant/read-only. The invariant guaranteed is that you will not
16+
modify an Array (through another reference) within an `@aliasscope` scope.
17+
18+
!!! warning
19+
Experimental API. Subject to change without deprecation.
20+
"""
21+
struct Const{T,N} <: DenseArray{T,N}
22+
a::Array{T,N}
23+
end
24+
25+
Base.IndexStyle(::Type{<:Const}) = IndexLinear()
26+
Base.size(C::Const) = size(C.a)
27+
Base.axes(C::Const) = axes(C.a)
28+
@eval Base.getindex(A::Const, i1::Int) =
29+
(Base.@_inline_meta; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1))
30+
@eval Base.getindex(A::Const, i1::Int, i2::Int, I::Int...) =
31+
(Base.@_inline_meta; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1, i2, I...))
32+
33+
"""
34+
@aliasscope expr
35+
36+
Allows the compiler to assume that all `Const`s are not being modified through stores
37+
within this scope, even if the compiler can't prove this to be the case.
38+
39+
!!! warning
40+
Experimental API. Subject to change without deprecation.
41+
"""
42+
macro aliasscope(body)
43+
sym = gensym()
44+
quote
45+
$(Expr(:aliasscope))
46+
$sym = $(esc(body))
47+
$(Expr(:popaliasscope))
48+
$sym
49+
end
50+
end
51+
52+
end

test/llvmpasses/aliasscopes.jl

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
6+
## Notes:
7+
# This script uses the `emit` function (defined llvmpasses.jl) to emit either
8+
# optimized or unoptimized LLVM IR. Each function is emitted individually and
9+
# `llvm-link` is used to create a single module that can be passed to opt.
10+
# The order in which files are emitted and linked is important since `lit` will
11+
# process the test cases in order.
12+
13+
include(joinpath("..", "testhelpers", "llvmpasses.jl"))
14+
15+
import Base.Experimental: Const, @aliasscope
16+
17+
# CHECK-LABEL: @julia_simple
18+
function simple(A, B)
19+
@aliasscope @inbounds for I in eachindex(A, B)
20+
A[I] = Const(B)[I]
21+
# CHECK: load double, {{.*}} !alias.scope [[SCOPE:![0-9]+]]
22+
# CHECK: store double {{.*}} !noalias [[SCOPE]]
23+
end
24+
return 0 # return nothing causes japi1
25+
end
26+
27+
# CHECK-LABEL: @julia_constargs
28+
function constargs(A, B::Const)
29+
@aliasscope @inbounds for I in eachindex(A, B)
30+
A[I] = B[I]
31+
# CHECK: load double, {{.*}} !alias.scope [[SCOPE2:![0-9]+]]
32+
# CHECK: store double {{.*}} !noalias [[SCOPE2]]
33+
end
34+
return 0
35+
end
36+
37+
# CHECK-LABEL: @"julia_micro_ker!
38+
function micro_ker!(AB, Ac, Bc, kc, offSetA, offSetB)
39+
MR = 8; NR = 6;
40+
@inbounds @aliasscope for k in 1:kc
41+
for j in 1:NR, i in 1:MR
42+
AB[i+(j-1)*MR] = muladd(Const(Ac)[offSetA+i], Const(Bc)[offSetB+j], Const(AB)[i+(j-1)*MR])
43+
# CHECK: load double, {{.*}} !alias.scope [[SCOPE3:![0-9]+]]
44+
# CHECK: load double, {{.*}} !alias.scope [[SCOPE3]]
45+
# CHECK: load double, {{.*}} !alias.scope [[SCOPE3]]
46+
# CHECK: store double {{.*}} !noalias [[SCOPE3]]
47+
end
48+
offSetA += MR
49+
offSetB += NR
50+
end
51+
return
52+
end
53+
54+
# CHECK: [[SCOPE]] = !{[[ALIASSCOPE:![0-9]+]]}
55+
# CHECK: [[ALIASSCOPE]] = !{!"aliasscope", [[MDNODE:![0-9]+]]}
56+
# CHECK: [[MDNODE]] = !{!"simple"}
57+
58+
emit(simple, Vector{Float64}, Vector{Float64})
59+
emit(constargs, Vector{Float64}, Const{Float64, 1})
60+
emit(micro_ker!, Matrix{Float64}, Vector{Float64}, Vector{Float64}, Int64, Int64, Int64)
61+

test/llvmpasses/loopinfo.jl

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
# RUN: cat %t/module.ll | opt -load libjulia%shlibext -LowerSIMDLoop -S - | FileCheck %s -check-prefix=LOWER
66
# RUN: julia --startup-file=no %s %t -O && llvm-link -S %t/* -o %t/module.ll
77
# RUN: cat %t/module.ll | FileCheck %s -check-prefix=FINAL
8-
using InteractiveUtils
9-
using Printf
108

119
## Notes:
12-
# This script uses the `emit` function (defined at the end) to emit either
10+
# This script uses the `emit` function (defined llvmpasses.jl) to emit either
1311
# optimized or unoptimized LLVM IR. Each function is emitted individually and
1412
# `llvm-link` is used to create a single module that can be passed to opt.
1513
# The order in which files are emitted and linked is important since `lit` will
@@ -19,17 +17,7 @@ using Printf
1917
# - `CHECK`: Checks the result of codegen
2018
# - `LOWER`: Checks the result of -LowerSIMDLoop
2119
# - `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
20+
include(joinpath("..", "testhelpers", "llvmpasses.jl"))
3321

3422
# CHECK-LABEL: @julia_simdf_
3523
# LOWER-LABEL: @julia_simdf_
@@ -142,17 +130,6 @@ end
142130
# LOWER: [[LOOPID4]] = distinct !{[[LOOPID4]], [[LOOPUNROLL2:![0-9]+]]}
143131
# LOWER: [[LOOPUNROLL2]] = !{!"llvm.loop.unroll.full"}
144132

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-
156133
# Maintaining the order is important
157134
emit(simdf, Vector{Float64})
158135
emit(simdf2, Vector{Float64})

test/testhelpers/llvmpasses.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using InteractiveUtils
2+
using Printf
3+
4+
# get a temporary directory
5+
dir = ARGS[1]
6+
rm(dir, force=true, recursive=true)
7+
mkdir(dir)
8+
9+
# toggle between unoptimized (CHECK/LOWER) and optimized IR (FINAL).
10+
optimize=false
11+
if length(ARGS) >= 2
12+
optimize = ARGS[2]=="-O"
13+
end
14+
15+
# Emit LLVM IR to dir
16+
counter = 0
17+
function emit(f, tt...)
18+
global counter
19+
name = nameof(f)
20+
open(joinpath(dir, @sprintf("%05d-%s.ll", counter, name)), "w") do io
21+
code_llvm(io, f, tt, raw=true, optimize=optimize, dump_module=true, debuginfo=:none)
22+
end
23+
counter+=1
24+
end
25+

0 commit comments

Comments
 (0)