diff --git a/test/choosetests.jl b/test/choosetests.jl index ad3027f08d9a8..996dd5cc08e23 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -2,16 +2,20 @@ @doc """ -`tests, net_on = choosetests(choices)` selects a set of tests to be -run. `choices` should be a vector of test names; if empty or set to -`["all"]`, all tests are selected. +`tests, codegen_tests, net_on = choosetests(choices)` selects a set of +tests to be run. `choices` should be a vector of test names; if empty +or set to `["all"]`, all tests are selected. This function also supports "test collections": specifically, "linalg" - refers to collections of tests in the correspondingly-named -directories. +refers to collections of tests in the correspondingly-named directories. -Upon return, `tests` is a vector of fully-expanded test names, and -`net_on` is true if networking is available (required for some tests). +Upon return, `tests` and `codegen_tests` are vectors of fully-expanded +test names, and `net_on` is true if networking is available (required +for some tests). + +`tests` contains general-purpose tests, while `codegen-tests` contains +tests that require special exeflags to run, eg. inlining, optimisations +etc. should not be disabled. """ -> function choosetests(choices = []) testnames = [ @@ -78,7 +82,11 @@ function choosetests(choices = []) prepend!(tests, linalgtests) end - net_required_for = ["socket", "parallel"] + codegen_testnames = ["simdloop"] + + # net is required for networking tests and for tests that must be run in a worker process + # with different exeflags (eg. codegen tests) + net_required_for = ["socket", "parallel", codegen_testnames...] net_on = true try getipaddr() @@ -98,5 +106,15 @@ function choosetests(choices = []) filter!(x -> !(x in skip_tests), tests) - tests, net_on + # Separate the code generation tests, they need different exeflags + codegen_tests = [] + filter!(tests) do t + if t in codegen_testnames + push!(codegen_tests, t) + return false + end + return true + end + + tests, codegen_tests, net_on end diff --git a/test/runtests.jl b/test/runtests.jl index 969c72ee53182..9943fae508b73 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,9 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license include("choosetests.jl") -tests, net_on = choosetests(ARGS) +tests, codegen_tests, net_on = choosetests(ARGS) tests = unique(tests) +codegen_tests = unique(codegen_tests) # Base.compile only works from node 1, so compile test is handled specially compile_test = "compile" in tests @@ -10,14 +11,27 @@ if compile_test splice!(tests, findfirst(tests, "compile")) end -cd(dirname(@__FILE__)) do +function spawn_tests(tests, exeflags) + if isempty(tests) + return + end + n = 1 + procs = workers() if net_on n = min(8, CPU_CORES, length(tests)) - n > 1 && addprocs(n; exeflags=`--check-bounds=yes --depwarn=error`) + procs = addprocs(n; exeflags=exeflags) + println("Started workers $procs with flags $exeflags") blas_set_num_threads(1) end + for p in procs + remotecall_fetch(p) do + opts = Base.JLOptions() + println("Worker $p: $opts") + end + end + @everywhere include("testdefs.jl") results=[] @@ -27,7 +41,7 @@ cd(dirname(@__FILE__)) do max_worker_rss = typemax(Csize_t) end @sync begin - for p in workers() + for p in procs @async begin while length(tests) > 0 test = shift!(tests) @@ -42,7 +56,7 @@ cd(dirname(@__FILE__)) do if (isa(resp, Integer) && (resp > max_worker_rss)) || isa(resp, Exception) if n > 1 rmprocs(p, waitfor=0.5) - p = addprocs(1; exeflags=`--check-bounds=yes --depwarn=error`)[1] + p = addprocs(1; exeflags=exeflags)[1] remotecall_fetch(()->include("testdefs.jl"), p) else # single process testing, bail if mem limit reached, or, on an exception. @@ -64,11 +78,19 @@ cd(dirname(@__FILE__)) do error("Some tests exited with errors.") end +# @unix_only n > 1 && rmprocs(workers(), waitfor=5.0) + rmprocs(procs..., waitfor=5.0) + if compile_test n > 1 && print("\tFrom worker 1:\t") runtests("compile") end - @unix_only n > 1 && rmprocs(workers(), waitfor=5.0) +end + +cd(dirname(@__FILE__)) do + spawn_tests(tests, `--check-bounds=yes --depwarn=error`) + spawn_tests(codegen_tests, `--check-bounds=no --depwarn=error`) + println(" \033[32;1mSUCCESS\033[0m") end diff --git a/test/simdloop.jl b/test/simdloop.jl index c7d99fd15e73f..ae4fb166ba536 100644 --- a/test/simdloop.jl +++ b/test/simdloop.jl @@ -1,5 +1,10 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license +# Check that the julia process is running with options that will allow vectorization: +@test Base.JLOptions().check_bounds != 1 +@test Base.JLOptions().can_inline == 1 +@test Base.JLOptions().fast_math <= 1 + function simd_loop_example_from_manual(x, y, z) s = zero(eltype(z)) n = min(length(x),length(y),length(z)) @@ -125,3 +130,127 @@ crng = CartesianRange(CartesianIndex{0}(), CartesianIndex{0}()) indexes = simd_cartesian_range!(Array(eltype(crng), 0), crng) @test indexes == collect(crng) + +# ================================================================== +# Codegen tests: check that vectorized LLVM code is actually emitted +# https://github.com/JuliaLang/julia/issues/13686 + +function check_llvm_vector(func, argtypes...) + # look for "vector.body:" in the generated LLVM code: + if contains(Base._dump_function(func, argtypes, false, false, true, false), "vector.body:") + return true + else + println("\n===========") + code_warntype(func, argtypes) + println("===========") + code_llvm(func, argtypes) + println("===========") + error("Not vectorized: $func$argtypes") + end +end + +function simd_loop_long_expr(x, y, z) + # SIMD loop with a longer expression + @simd for i=1:length(x) + @inbounds begin + x[i] = y[i] * (z[i] > y[i]) * (z[i] < y[i]) * (z[i] >= y[i]) * (z[i] <= y[i]) + end + end +end + +function simd_loop_local_arrays() + # SIMD loop on local arrays declared without type annotations + x = Array(Float32,1000) + y = Array(Float32,1000) + z = Array(Float32,1000) + @simd for i = 1:length(x) + @inbounds x[i] = y[i] * z[i] + end +end + +immutable ImmutableFields + x::Array{Float32, 1} + y::Array{Float32, 1} + z::Array{Float32, 1} +end + +type MutableFields + x::Array{Float32, 1} + y::Array{Float32, 1} + z::Array{Float32, 1} +end + +function simd_loop_fields(obj) + # SIMD loop with field access + @simd for i = 1:length(obj.x) + @inbounds obj.x[i] = obj.y[i] * obj.z[i] + end +end + +@generated function simd_loop_call{func}(::Type{Val{func}}, argtuple) + # SIMD loop calling a configurable function on local arrays + arrays = [] + decls = [] + for (T, sym) in zip(argtuple.types, "abcdefghij") + arr = Symbol(string(sym)) + push!(arrays, :($arr[idx])) + # add type annotations to avoid conflating those failures due to + # function calls in the loop, with those due to type inference: + push!(decls, :($arr::Array{$T,1} = Array($T,1000))) + end + code = quote + $(Expr(:meta, :fastmath)) + $(decls...) + @simd for idx=1:length(a) + @inbounds a[idx] = $func($(arrays[2:end]...)) + end + end + code +end + +# Check that the basic SIMD examples above actually generated vectorized LLVM code. +for T in [Int32,Int64,Float32,Float64] + AR = Array{T,1} + @test check_llvm_vector(simd_loop_example_from_manual, AR, AR, AR) + @test check_llvm_vector(simd_loop_long_expr, AR, AR, AR) + + # TODO: uncomment the following tests + # @test check_llvm_vector(simd_loop_with_multiple_reductions, AR, AR, AR) + # TODO: uncomment the above tests +end + +# Test for vectorization of intrinsic functions that LLVM supports: +# cf. http://llvm.org/docs/Vectorizers.html#vectorization-of-function-calls +for T in [Float32,Float64] + # sanity check or "meta-test" for the @generated function we use: + # this should not fail if the basic tests above passed + @test check_llvm_vector(simd_loop_call, Type{Val{:+}}, Tuple{T, T, T}) + + # Functions that LLVM should be able to vectorize: + @test check_llvm_vector(simd_loop_call, Type{Val{:muladd}}, Tuple{T, T, T, T}) + @test check_llvm_vector(simd_loop_call, Type{Val{:abs}}, Tuple{T, T}) + # TODO: uncomment the following tests + # @test check_llvm_vector(simd_loop_call, Type{Val{:sqrt}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:sin}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:cos}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:exp}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:log}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:log2}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:log10}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:floor}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:ceil}}, Tuple{T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:^}}, Tuple{T, T, T}) + # @test check_llvm_vector(simd_loop_call, Type{Val{:fma}}, Tuple{T, T, T, T}) + # TODO: uncomment the above tests +end + +# Test for vectorization of local arrays without type annotations: +# TODO: uncomment the following tests +# @test check_llvm_vector(simd_loop_local_arrays) +# TODO: uncomment the above tests + +# Test for vectorization of arrays accessed through fields: +@test check_llvm_vector(simd_loop_fields, ImmutableFields) +# TODO: uncomment the following tests +# @test check_llvm_vector(simd_loop_fields, MutableFields) +# TODO: uncomment the above tests