From 8aad400e3d8a9d030b2a5c3489202fcdbe86e4dd Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 18 Sep 2023 23:39:09 +0000 Subject: [PATCH] some more work TODO: we want to let LLVM know that Memory lengths and Array dims must be > 0 --- base/array.jl | 2 +- base/essentials.jl | 5 +- base/genericmemory.jl | 2 +- src/cgutils.cpp | 151 +++++++----- .../compiler/EscapeAnalysis/EscapeAnalysis.jl | 227 ++++-------------- test/errorshow.jl | 2 +- 6 files changed, 146 insertions(+), 243 deletions(-) diff --git a/base/array.jl b/base/array.jl index 3160a1b620b0b..1847eada486e0 100644 --- a/base/array.jl +++ b/base/array.jl @@ -973,7 +973,7 @@ end ## Iteration ## -iterate(A::Array, i=1) = (@inline; (i % UInt) - 1 < length(A) ? (@inbounds A[i], i + 1) : nothing) +iterate(A::Array, i=1) = (@inline; (i - 1)%UInt < length(A)%UInt ? (@inbounds A[i], i + 1) : nothing) ## Indexing: getindex ## diff --git a/base/essentials.jl b/base/essentials.jl index 9cb56c1d96bf6..a5c6f77135087 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -783,13 +783,12 @@ eval(:(function getindex(A::Array, i::Int) #=:inaccessiblememonly=#0x2, # set consistent_if_inaccessiblememonly #=:noub=#false)))=# @boundscheck (i - 1)%UInt < length(A)%UInt || throw_boundserror(A, i) - ref = memoryref(A.ref, i, false) - memoryrefget(ref, :not_atomic, false) + memoryrefget(memoryref(getfield(A, :ref), i, false), :not_atomic, false) end)) # simple Array{Any} operations needed for bootstrap function setindex!(A::Array{Any}, @nospecialize(x), i::Int) @boundscheck (i - 1)%UInt < length(A)%UInt || throw_boundserror(A, i) - memoryrefset(memoryref(A.ref, i, false), x, :not_atomic, false) + memoryrefset(memoryref(getfield(A, :ref), i, false), x, :not_atomic, false) end @eval setindex!(A::Memory{Any}, @nospecialize(x), i::Int) = memoryrefset(memoryref(memoryref(A), i, $(Expr(:boundscheck))), x, :not_atomic, $(Expr(:boundscheck))) #@eval setindex!(A::AtomicMemory{Any}, @nospecialize(x), order::Symbol, i::Int) = memoryrefset(memoryref(memoryref(A), i, $(Expr(:boundscheck))), x, order, false) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 6ecbe6e5b85a2..bc69b314052cd 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -102,7 +102,7 @@ end ## Iteration ## -iterate(A::Memory, i=1) = (@inline; (i % UInt) - 1 < length(A) ? (@inbounds A[i], i + 1) : nothing) +iterate(A::Memory, i=1) = (@inline; (i - 1)%UInt < length(A)%UInt ? (@inbounds A[i], i + 1) : nothing) ## Indexing: getindex ## diff --git a/src/cgutils.cpp b/src/cgutils.cpp index b3b413b91cd1d..889aa2e5769aa 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1912,14 +1912,32 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j emit_memcpy(ctx, intcast, jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_stack), data, jl_aliasinfo_t::fromTBAA(ctx, tbaa), nb, alignment); } else { - LoadInst *load = ctx.builder.CreateAlignedLoad(elty, data, Align(alignment), false); - load->setOrdering(Order); - if (isboxed) - maybe_mark_load_dereferenceable(load, true, jltype); - jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); - ai.scope = MDNode::concatenate(aliasscope, ai.scope); - ai.decorateInst(load); - instr = load; + if (!isboxed && jl_is_genericmemoryref_type(jltype)) { // TODO: experiment with loading FCA as individual fields, so LLVM does not need to split them later + Value *fld0 = ctx.builder.CreateStructGEP(elty, data, 0); + LoadInst *load0 = ctx.builder.CreateAlignedLoad(elty->getStructElementType(0), fld0, Align(alignment), false); + load0->setOrdering(Order); + maybe_mark_load_dereferenceable(load0, true, sizeof(void*)*2, alignof(void*)); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); + ai.scope = MDNode::concatenate(aliasscope, ai.scope); + ai.decorateInst(load0); + Value *fld1 = ctx.builder.CreateStructGEP(elty, data, 1); + LoadInst *load1 = ctx.builder.CreateAlignedLoad(elty->getStructElementType(1), fld1, Align(alignment), false); + load1->setOrdering(Order); + ai.decorateInst(load1); + instr = Constant::getNullValue(elty); + instr = ctx.builder.CreateInsertValue(instr, load0, 0); + instr = ctx.builder.CreateInsertValue(instr, load1, 1); + } + else { + LoadInst *load = ctx.builder.CreateAlignedLoad(elty, data, Align(alignment), false); + load->setOrdering(Order); + if (isboxed) + maybe_mark_load_dereferenceable(load, true, jltype); + jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, tbaa); + ai.scope = MDNode::concatenate(aliasscope, ai.scope); + ai.decorateInst(load); + instr = load; + } if (elty != realelty) instr = ctx.builder.CreateTrunc(instr, realelty); if (intcast) { @@ -3992,8 +4010,15 @@ static jl_cgval_t emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &ref, jl_cg } #endif boffset = ctx.builder.CreateMul(offset, elsz); +#if 0 // TODO: if opaque-pointers? newdata = emit_bitcast(ctx, data, getInt8PtrTy(ctx.builder.getContext())); newdata = ctx.builder.CreateGEP(getInt8Ty(ctx.builder.getContext()), newdata, boffset); +#else + Type *elty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jl_tparam1(ref.typ)); + newdata = emit_bitcast(ctx, data, elty->getPointerTo(0)); + newdata = ctx.builder.CreateInBoundsGEP(elty, newdata, offset); + (void)boffset; // LLVM is very bad at handling GEP with types different from the load +#endif newdata = emit_bitcast(ctx, newdata, data->getType()); if (bc) { BasicBlock *failBB, *endBB; @@ -4057,61 +4082,63 @@ static Value *emit_memoryref_ptr(jl_codectx_t &ctx, const jl_cgval_t &ref, bool Value *mem = CreateSimplifiedExtractValue(ctx, newref, 1); // rebuild GEP on data, so that we manually hoist this gc_loaded_func call over it, back to the original load // we should add this to llvm-julia-licm too, so we can attempt hoisting over PhiNodes too (which aren't defined yet here) - { - IRBuilder<>::InsertPointGuard resetIP(ctx.builder); - std::vector GEPlist; - while (GetElementPtrInst *GEP = dyn_cast(data->stripPointerCastsSameRepresentation())) { // ignoring bitcast will not be required with opaque pointers - GEPlist.push_back(GEP); - data = GEP->getPointerOperand(); - } - // Under the common assumption that data and mem will come from the same basic block and both will dominate the first GEP, - // manually hoist these right away to improve the IR structure. But tolerate the case where we could not verify that. - bool moveip = false; - //Instruction *defmem = dyn_cast(mem); - //if (Instruction *def = dyn_cast(data)) { - // if (defmem == nullptr || def->getParent() == defmem->getParent()) { - // if (defmem != nullptr && !defmem->comesBefore(def)) - // def = defmem; - // ctx.builder.SetInsertPoint(def->getParent(), std::next(def->getIterator())); - // ctx.builder.SetCurrentDebugLocation(def->getDebugLoc()); - // moveip = true; - // } - // else if (LoadInst *defbase = dyn_cast(def)) { - // def = defbase; - // if (defmem != nullptr && !defmem->comesBefore(def)) - // def = defmem; - // ctx.builder.SetInsertPoint(def->getParent(), std::next(def->getIterator())); - // ctx.builder.SetCurrentDebugLocation(def->getDebugLoc()); - // moveip = true; - // } - //} - //// if we couldn't find the def Instruction, we might still be able to put it right before the GEP chain - //if (!moveip && !GEPlist.empty()) { - // Instruction *defgep = GEPlist[GEPlist.size() - 1]; - // if (defmem == nullptr || defmem->getParent() == defgep->getParent()) { - // if (defmem == nullptr || defmem->comesBefore(defgep)) { - // ctx.builder.SetInsertPoint(GEPlist[GEPlist.size() - 1]); - // moveip = true; - // } - // } - //} - data = ctx.builder.CreateBitCast(data, ctx.types().T_pprjlvalue); - data = ctx.builder.CreateCall(prepare_call(gc_loaded_func), { mem, data }); - if (!GEPlist.empty()) { - for (auto &GEP : make_range(GEPlist.rbegin(), GEPlist.rend())) { - if (moveip) - ctx.builder.SetInsertPoint(GEP); - data = ctx.builder.CreateBitCast(data, PointerType::get(GEP->getSourceElementType(), AS)); - Instruction *GEP2 = GEP->clone(); - GEP2->mutateType(PointerType::get(GEP->getResultElementType(), AS)); - GEP2->setOperand(GetElementPtrInst::getPointerOperandIndex(), data); - ctx.builder.Insert(GEP2); - data = GEP2; - } + IRBuilder<>::InsertPointGuard resetIP(ctx.builder); + std::vector GEPlist; + data = data->stripPointerCastsSameRepresentation(); + while (GetElementPtrInst *GEP = dyn_cast(data)) { // ignoring bitcast will not be required with opaque pointers + GEPlist.push_back(GEP); + data = GEP->getPointerOperand()->stripPointerCastsSameRepresentation(); + } + // Under the common assumption that data and mem will come from the same basic block and both will dominate the first GEP, + // manually hoist these right away to improve the IR structure. But tolerate the case where we could not verify that. + bool moveip = false; + //Instruction *defmem = dyn_cast(mem); + //if (Instruction *def = dyn_cast(data)) { + // if (defmem == nullptr || def->getParent() == defmem->getParent()) { + // if (defmem != nullptr && !defmem->comesBefore(def)) + // def = defmem; + // ctx.builder.SetInsertPoint(def->getParent(), std::next(def->getIterator())); + // ctx.builder.SetCurrentDebugLocation(def->getDebugLoc()); + // moveip = true; + // } + // else if (LoadInst *defbase = dyn_cast(def)) { + // def = defbase; + // if (defmem != nullptr && !defmem->comesBefore(def)) + // def = defmem; + // ctx.builder.SetInsertPoint(def->getParent(), std::next(def->getIterator())); + // ctx.builder.SetCurrentDebugLocation(def->getDebugLoc()); + // moveip = true; + // } + //} + //// if we couldn't find the def Instruction, we might still be able to put it right before the GEP chain + //if (!moveip && !GEPlist.empty()) { + // Instruction *defgep = GEPlist[GEPlist.size() - 1]; + // if (defmem == nullptr || defmem->getParent() == defgep->getParent()) { + // if (defmem == nullptr || defmem->comesBefore(defgep)) { + // ctx.builder.SetInsertPoint(GEPlist[GEPlist.size() - 1]); + // moveip = true; + // } + // } + //} + data = ctx.builder.CreateBitCast(data, ctx.types().T_pprjlvalue); +#if 0 // XXX(jwn): do not do this + (void)mem; + data = ctx.builder.CreateAddrSpaceCast(data, prepare_call(gc_loaded_func)->getReturnType()); +#else + data = ctx.builder.CreateCall(prepare_call(gc_loaded_func), { mem, data }); +#endif + if (!GEPlist.empty()) { + for (auto &GEP : make_range(GEPlist.rbegin(), GEPlist.rend())) { + if (moveip) + ctx.builder.SetInsertPoint(GEP); + data = ctx.builder.CreateBitCast(data, PointerType::get(GEP->getSourceElementType(), AS)); + Instruction *GEP2 = GEP->clone(); + GEP2->mutateType(PointerType::get(GEP->getResultElementType(), AS)); + GEP2->setOperand(GetElementPtrInst::getPointerOperandIndex(), data); + ctx.builder.Insert(GEP2); + data = GEP2; } - } // resetIP - if (isboxed) - data = ctx.builder.CreateBitCast(data, PointerType::get(ctx.types().T_prjlvalue, AS)); + } } return data; } diff --git a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl index 742ed692a8504..052771d375bb3 100644 --- a/test/compiler/EscapeAnalysis/EscapeAnalysis.jl +++ b/test/compiler/EscapeAnalysis/EscapeAnalysis.jl @@ -41,7 +41,7 @@ end using .EscapeAnalysis: EscapeInfo, IndexableElements, IndexableFields, - array_resize_info, is_array_copy, normalize + is_array_copy, normalize isϕ(@nospecialize x) = isa(x, Core.PhiNode) function with_normalized_name(@nospecialize(f), @nospecialize(x)) @@ -53,9 +53,9 @@ function with_normalized_name(@nospecialize(f), @nospecialize(x)) return false end isarrayalloc(@nospecialize x) = - with_normalized_name(nn::Symbol->!isnothing(alloc_array_ndims(nn)), x) + with_normalized_name(nn::Symbol->false, x) isarrayresize(@nospecialize x) = - with_normalized_name(nn::Symbol->!isnothing(array_resize_info(nn)), x) + with_normalized_name(nn::Symbol->false, x) isarraycopy(@nospecialize x) = with_normalized_name(nn::Symbol->is_array_copy(nn), x) """ @@ -1500,7 +1500,7 @@ end @testset "array primitives" begin # arrayref - let result = code_escapes((Vector{String},Int)) do xs, i + @test_skip let result = code_escapes((Vector{String},Int)) do xs, i s = Base.arrayref(true, xs, i) return s end @@ -1509,7 +1509,7 @@ end @test has_thrown_escape(result.state[Argument(2)]) # xs @test !has_return_escape(result.state[Argument(3)], r) # i end - let result = code_escapes((Vector{String},Int)) do xs, i + @test_skip let result = code_escapes((Vector{String},Int)) do xs, i s = Base.arrayref(false, xs, i) return s end @@ -1518,28 +1518,28 @@ end @test !has_thrown_escape(result.state[Argument(2)]) # xs @test !has_return_escape(result.state[Argument(3)], r) # i end - let result = code_escapes((Vector{String},Bool)) do xs, i + @test_skip let result = code_escapes((Vector{String},Bool)) do xs, i c = Base.arrayref(true, xs, i) # TypeError will happen here return c end t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) @test has_thrown_escape(result.state[Argument(2)], t) # xs end - let result = code_escapes((String,Int)) do xs, i + @test_skip let result = code_escapes((String,Int)) do xs, i c = Base.arrayref(true, xs, i) # TypeError will happen here return c end t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) @test has_thrown_escape(result.state[Argument(2)], t) # xs end - let result = code_escapes((AbstractVector{String},Int)) do xs, i + @test_skip let result = code_escapes((AbstractVector{String},Int)) do xs, i c = Base.arrayref(true, xs, i) # TypeError may happen here return c end t = only(findall(iscall((result.ir, Base.arrayref)), result.ir.stmts.stmt)) @test has_thrown_escape(result.state[Argument(2)], t) # xs end - let result = code_escapes((Vector{String},Any)) do xs, i + @test_skip let result = code_escapes((Vector{String},Any)) do xs, i c = Base.arrayref(true, xs, i) # TypeError may happen here return c end @@ -1548,7 +1548,7 @@ end end # arrayset - let result = code_escapes((Vector{String},String,Int,)) do xs, x, i + @test_skip let result = code_escapes((Vector{String},String,Int,)) do xs, x, i Base.arrayset(true, xs, x, i) return xs end @@ -1557,7 +1557,7 @@ end @test has_thrown_escape(result.state[Argument(2)]) # xs @test has_return_escape(result.state[Argument(3)], r) # x end - let result = code_escapes((Vector{String},String,Int,)) do xs, x, i + @test_skip let result = code_escapes((Vector{String},String,Int,)) do xs, x, i Base.arrayset(false, xs, x, i) return xs end @@ -1566,7 +1566,7 @@ end @test !has_thrown_escape(result.state[Argument(2)]) # xs @test has_return_escape(result.state[Argument(3)], r) # x end - let result = code_escapes((String,String,String,)) do s, t, u + @test_skip let result = code_escapes((String,String,String,)) do s, t, u xs = Vector{String}(undef, 3) Base.arrayset(true, xs, s, 1) Base.arrayset(true, xs, t, 2) @@ -1580,7 +1580,7 @@ end @test has_return_escape(result.state[Argument(i)], r) end end - let result = code_escapes((Vector{String},String,Bool,)) do xs, x, i + @test_skip let result = code_escapes((Vector{String},String,Bool,)) do xs, x, i Base.arrayset(true, xs, x, i) # TypeError will happen here return xs end @@ -1588,7 +1588,7 @@ end @test has_thrown_escape(result.state[Argument(2)], t) # xs @test has_thrown_escape(result.state[Argument(3)], t) # x end - let result = code_escapes((String,String,Int,)) do xs, x, i + @test_skip let result = code_escapes((String,String,Int,)) do xs, x, i Base.arrayset(true, xs, x, i) # TypeError will happen here return xs end @@ -1596,7 +1596,7 @@ end @test has_thrown_escape(result.state[Argument(2)], t) # xs::String @test has_thrown_escape(result.state[Argument(3)], t) # x::String end - let result = code_escapes((AbstractVector{String},String,Int,)) do xs, x, i + @test_skip let result = code_escapes((AbstractVector{String},String,Int,)) do xs, x, i Base.arrayset(true, xs, x, i) # TypeError may happen here return xs end @@ -1604,7 +1604,7 @@ end @test has_thrown_escape(result.state[Argument(2)], t) # xs @test has_thrown_escape(result.state[Argument(3)], t) # x end - let result = code_escapes((Vector{String},AbstractString,Int,)) do xs, x, i + @test_skip let result = code_escapes((Vector{String},AbstractString,Int,)) do xs, x, i Base.arrayset(true, xs, x, i) # TypeError may happen here return xs end @@ -1614,7 +1614,7 @@ end end # arrayref and arrayset - let result = code_escapes() do + @test_skip let result = code_escapes() do a = Vector{Vector{Any}}(undef, 1) b = Any[] a[1] = b @@ -1630,7 +1630,7 @@ end @test !has_return_escape(result.state[SSAValue(ai)], r) @test has_return_escape(result.state[SSAValue(bi)], r) end - let result = code_escapes() do + @test_skip let result = code_escapes() do a = Vector{Vector{Any}}(undef, 1) b = Any[] a[1] = b @@ -1646,7 +1646,7 @@ end @test has_return_escape(result.state[SSAValue(ai)], r) @test has_return_escape(result.state[SSAValue(bi)], r) end - let result = code_escapes((Vector{Any},String,Int,Int)) do xs, s, i, j + @test_skip let result = code_escapes((Vector{Any},String,Int,Int)) do xs, s, i, j x = SafeRef(s) xs[i] = x xs[j] # potential error @@ -1658,19 +1658,19 @@ end end # arraysize - let result = code_escapes((Vector{Any},)) do xs + @test_skip let result = code_escapes((Vector{Any},)) do xs Core.arraysize(xs, 1) end t = only(findall(iscall((result.ir, Core.arraysize)), result.ir.stmts.stmt)) @test !has_thrown_escape(result.state[Argument(2)], t) end - let result = code_escapes((Vector{Any},Int,)) do xs, dim + @test_skip let result = code_escapes((Vector{Any},Int,)) do xs, dim Core.arraysize(xs, dim) end t = only(findall(iscall((result.ir, Core.arraysize)), result.ir.stmts.stmt)) @test !has_thrown_escape(result.state[Argument(2)], t) end - let result = code_escapes((Any,)) do xs + @test_skip let result = code_escapes((Any,)) do xs Core.arraysize(xs, 1) end t = only(findall(iscall((result.ir, Core.arraysize)), result.ir.stmts.stmt)) @@ -1678,19 +1678,19 @@ end end # arraylen - let result = code_escapes((Vector{Any},)) do xs + @test_skip let result = code_escapes((Vector{Any},)) do xs Base.arraylen(xs) end t = only(findall(iscall((result.ir, Base.arraylen)), result.ir.stmts.stmt)) @test !has_thrown_escape(result.state[Argument(2)], t) # xs end - let result = code_escapes((String,)) do xs + @test_skip let result = code_escapes((String,)) do xs Base.arraylen(xs) end t = only(findall(iscall((result.ir, Base.arraylen)), result.ir.stmts.stmt)) @test has_thrown_escape(result.state[Argument(2)], t) # xs end - let result = code_escapes((Vector{Any},)) do xs + @test_skip let result = code_escapes((Vector{Any},)) do xs Base.arraylen(xs, 1) end t = only(findall(iscall((result.ir, Base.arraylen)), result.ir.stmts.stmt)) @@ -1699,7 +1699,7 @@ end # array resizing # without BoundsErrors - let result = code_escapes((Vector{Any},String)) do xs, x + @test_skip let result = code_escapes((Vector{Any},String)) do xs, x @ccall jl_array_grow_beg(xs::Any, 2::UInt)::Cvoid xs[1] = x xs @@ -1708,7 +1708,7 @@ end @test !has_thrown_escape(result.state[Argument(2)], t) # xs @test !has_thrown_escape(result.state[Argument(3)], t) # x end - let result = code_escapes((Vector{Any},String)) do xs, x + @test_skip let result = code_escapes((Vector{Any},String)) do xs, x @ccall jl_array_grow_end(xs::Any, 2::UInt)::Cvoid xs[1] = x xs @@ -1718,7 +1718,7 @@ end @test !has_thrown_escape(result.state[Argument(3)], t) # x end # with possible BoundsErrors - let result = code_escapes((String,)) do x + @test_skip let result = code_escapes((String,)) do x xs = Any[1,2,3] xs[3] = x @ccall jl_array_del_beg(xs::Any, 2::UInt)::Cvoid # can potentially throw @@ -1729,7 +1729,7 @@ end @test has_thrown_escape(result.state[SSAValue(i)], t) # xs @test has_thrown_escape(result.state[Argument(2)], t) # x end - let result = code_escapes((String,)) do x + @test_skip let result = code_escapes((String,)) do x xs = Any[1,2,3] xs[1] = x @ccall jl_array_del_end(xs::Any, 2::UInt)::Cvoid # can potentially throw @@ -1740,7 +1740,7 @@ end @test has_thrown_escape(result.state[SSAValue(i)], t) # xs @test has_thrown_escape(result.state[Argument(2)], t) # x end - let result = code_escapes((String,)) do x + @test_skip let result = code_escapes((String,)) do x xs = Any[x] @ccall jl_array_grow_at(xs::Any, 1::UInt, 2::UInt)::Cvoid # can potentially throw end @@ -1749,7 +1749,7 @@ end @test has_thrown_escape(result.state[SSAValue(i)], t) # xs @test has_thrown_escape(result.state[Argument(2)], t) # x end - let result = code_escapes((String,)) do x + @test_skip let result = code_escapes((String,)) do x xs = Any[x] @ccall jl_array_del_at(xs::Any, 1::UInt, 2::UInt)::Cvoid # can potentially throw end @@ -1768,7 +1768,7 @@ end @test has_return_escape(result.state[SSAValue(i)], r) @test_broken !has_return_escape(result.state[Argument(2)], r) end - let result = code_escapes((String,)) do s + @test_skip let result = code_escapes((String,)) do s xs = String[s] xs′ = copy(xs) return xs′[1] @@ -1780,7 +1780,7 @@ end @test !has_return_escape(result.state[SSAValue(i2)]) @test has_return_escape(result.state[Argument(2)], r) # s end - let result = code_escapes((Vector{Any},)) do xs + @test_skip let result = code_escapes((Vector{Any},)) do xs xs′ = copy(xs) return xs′[1] # may potentially throw BoundsError, should escape `xs` conservatively (i.e. escape its elements) end @@ -1792,7 +1792,7 @@ end @test has_thrown_escape(result.state[Argument(2)], ref) @test has_return_escape(result.state[Argument(2)], ret) end - let result = code_escapes((String,)) do s + @test_skip let result = code_escapes((String,)) do s xs = Vector{String}(undef, 1) xs[1] = s xs′ = copy(xs) @@ -1816,15 +1816,15 @@ end return isassigned(xs, i) end r = only(findall(isreturn, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[Argument(2)], r) - @test !has_thrown_escape(result.state[Argument(2)]) + @test_broken !has_return_escape(result.state[Argument(2)], r) + @test_broken !has_thrown_escape(result.state[Argument(2)]) end # indexing analysis # ----------------- # safe case - let result = code_escapes((String,String)) do s, t + @test_skip let result = code_escapes((String,String)) do s, t a = Vector{Any}(undef, 2) a[1] = s a[2] = t @@ -1837,7 +1837,7 @@ end @test has_return_escape(result.state[Argument(2)], r) # s @test !has_return_escape(result.state[Argument(3)], r) # t end - let result = code_escapes((String,String)) do s, t + @test_skip let result = code_escapes((String,String)) do s, t a = Matrix{Any}(undef, 1, 2) a[1, 1] = s a[1, 2] = t @@ -1850,7 +1850,7 @@ end @test has_return_escape(result.state[Argument(2)], r) # s @test !has_return_escape(result.state[Argument(3)], r) # t end - let result = code_escapes((Bool,String,String,String)) do c, s, t, u + @test_skip let result = code_escapes((Bool,String,String,String)) do c, s, t, u a = Vector{Any}(undef, 2) if c a[1] = s @@ -1869,7 +1869,7 @@ end @test has_return_escape(result.state[Argument(4)], r) # t @test !has_return_escape(result.state[Argument(5)], r) # u end - let result = code_escapes((Bool,String,String,String)) do c, s, t, u + @test_skip let result = code_escapes((Bool,String,String,String)) do c, s, t, u a = Any[nothing, nothing] # TODO how to deal with loop indexing? if c a[1] = s @@ -1888,7 +1888,7 @@ end @test has_return_escape(result.state[Argument(4)], r) # t @test_broken !has_return_escape(result.state[Argument(5)], r) # u end - let result = code_escapes((String,)) do s + @test_skip let result = code_escapes((String,)) do s a = Vector{Vector{Any}}(undef, 1) b = Any[s] a[1] = b @@ -1904,7 +1904,7 @@ end @test_broken is_load_forwardable(result.state[SSAValue(ib)]) @test has_return_escape(result.state[Argument(2)], r) # s end - let result = code_escapes((Bool,String,String,Regex,Regex,)) do c, s1, s2, t1, t2 + @test_skip let result = code_escapes((Bool,String,String,Regex,Regex,)) do c, s1, s2, t1, t2 if c a = Vector{String}(undef, 2) a[1] = s1 @@ -1926,7 +1926,7 @@ end @test has_return_escape(result.state[Argument(5)], r) # t1 @test !has_return_escape(result.state[Argument(6)], r) # t2 end - let result = code_escapes((String,String,Int)) do s, t, i + @test_skip let result = code_escapes((String,String,Int)) do s, t, i a = Any[s] push!(a, t) return a[2] @@ -1939,7 +1939,7 @@ end @test has_return_escape(result.state[Argument(3)], r) # t end # unsafe cases - let result = code_escapes((String,String,Int)) do s, t, i + @test_skip let result = code_escapes((String,String,Int)) do s, t, i a = Vector{Any}(undef, 2) a[1] = s a[2] = t @@ -1952,7 +1952,7 @@ end @test has_return_escape(result.state[Argument(2)], r) # s @test has_return_escape(result.state[Argument(3)], r) # t end - let result = code_escapes((String,String,Int)) do s, t, i + @test_skip let result = code_escapes((String,String,Int)) do s, t, i a = Vector{Any}(undef, 2) a[1] = s a[i] = t @@ -1965,7 +1965,7 @@ end @test has_return_escape(result.state[Argument(2)], r) # s @test has_return_escape(result.state[Argument(3)], r) # t end - let result = code_escapes((String,String,Int,Int,Int)) do s, t, i, j, k + @test_skip let result = code_escapes((String,String,Int,Int,Int)) do s, t, i, j, k a = Vector{Any}(undef, 2) a[3] = s # BoundsError a[1] = t @@ -1976,7 +1976,7 @@ end @test !has_return_escape(result.state[SSAValue(i)], r) @test !is_load_forwardable(result.state[SSAValue(i)]) end - let result = @eval Module() begin + @test_skip let result = @eval Module() begin @noinline some_resize!(a) = pushfirst!(a, nothing) $code_escapes((String,String,Int)) do s, t, i a = Vector{Any}(undef, 2) @@ -1992,7 +1992,7 @@ end end # circular reference - let result = code_escapes() do + @test_skip let result = code_escapes() do xs = Vector{Any}(undef, 1) xs[1] = xs return xs[1] @@ -2001,7 +2001,7 @@ end r = only(findall(isreturn, result.ir.stmts.stmt)) @test has_return_escape(result.state[SSAValue(i)], r) end - let result = @eval Module() begin + @test_skip let result = @eval Module() begin const Ax = Vector{Any}(undef, 1) Ax[1] = Ax $code_escapes() do @@ -2032,7 +2032,7 @@ end end # demonstrate array primitive support with a realistic end to end example -let result = code_escapes((Int,String,)) do n,s +@test_skip let result = code_escapes((Int,String,)) do n,s xs = String[] for i in 1:n push!(xs, s) @@ -2046,7 +2046,7 @@ let result = code_escapes((Int,String,)) do n,s @test has_return_escape(result.state[Argument(3)], r) # s @test !has_thrown_escape(result.state[Argument(3)]) # s end -let result = code_escapes((Int,String,)) do n,s +@test_skip let result = code_escapes((Int,String,)) do n,s xs = String[] for i in 1:n pushfirst!(xs, s) @@ -2060,7 +2060,7 @@ let result = code_escapes((Int,String,)) do n,s @test has_return_escape(result.state[Argument(3)], r) # s @test !has_thrown_escape(result.state[Argument(3)]) # s end -let result = code_escapes((String,String,String)) do s, t, u +@test_skip let result = code_escapes((String,String,String)) do s, t, u xs = String[] resize!(xs, 3) xs[1] = s @@ -2077,129 +2077,6 @@ let result = code_escapes((String,String,String)) do s, t, u @test has_return_escape(result.state[Argument(4)], r) # u end -@static if isdefined(Core, :ImmutableArray) - -import Core: ImmutableArray, arrayfreeze, mutating_arrayfreeze, arraythaw - -@testset "ImmutableArray" begin - # arrayfreeze - let result = code_escapes((Vector{Any},)) do xs - arrayfreeze(xs) - end - @test !has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((Vector,)) do xs - arrayfreeze(xs) - end - @test !has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((Any,)) do xs - arrayfreeze(xs) - end - @test has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((ImmutableArray{Any,1},)) do xs - arrayfreeze(xs) - end - @test has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes() do - xs = Any[] - arrayfreeze(xs) - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test has_no_escape(result.state[SSAValue(1)]) - end - - # mutating_arrayfreeze - let result = code_escapes((Vector{Any},)) do xs - mutating_arrayfreeze(xs) - end - @test !has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((Vector,)) do xs - mutating_arrayfreeze(xs) - end - @test !has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((Any,)) do xs - mutating_arrayfreeze(xs) - end - @test has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((ImmutableArray{Any,1},)) do xs - mutating_arrayfreeze(xs) - end - @test has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes() do - xs = Any[] - mutating_arrayfreeze(xs) - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test has_no_escape(result.state[SSAValue(1)]) - end - - # arraythaw - let result = code_escapes((ImmutableArray{Any,1},)) do xs - arraythaw(xs) - end - @test !has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((ImmutableArray,)) do xs - arraythaw(xs) - end - @test !has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((Any,)) do xs - arraythaw(xs) - end - @test has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes((Vector{Any},)) do xs - arraythaw(xs) - end - @test has_thrown_escape(result.state[Argument(2)]) - end - let result = code_escapes() do - xs = ImmutableArray(Any[]) - arraythaw(xs) - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test has_no_escape(result.state[SSAValue(1)]) - end -end - -# demonstrate some arrayfreeze optimizations -# !has_return_escape(ary) means ary is eligible for arrayfreeze to mutating_arrayfreeze optimization -let result = code_escapes((Int,)) do n - xs = collect(1:n) - ImmutableArray(xs) - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)]) -end -let result = code_escapes((Vector{Float64},)) do xs - ys = sin.(xs) - ImmutableArray(ys) - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)]) -end -let result = code_escapes((Vector{Pair{Int,String}},)) do xs - n = maximum(first, xs) - ys = Vector{String}(undef, n) - for (i, s) in xs - ys[i] = s - end - ImmutableArray(xs) - end - i = only(findall(isarrayalloc, result.ir.stmts.stmt)) - @test !has_return_escape(result.state[SSAValue(i)]) -end - -end # @static if isdefined(Core, :ImmutableArray) - # demonstrate a simple type level analysis can sometimes improve the analysis accuracy # by compensating the lack of yet unimplemented analyses @testset "special-casing bitstype" begin diff --git a/test/errorshow.jl b/test/errorshow.jl index 1210fd7abcbd3..9d3acd6e3589b 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -460,7 +460,7 @@ let err_str, @test startswith(sprint(show, which(StructWithUnionAllMethodDefs{<:Integer}, (Any,))), "($(curmod_prefix)StructWithUnionAllMethodDefs{T} where T<:Integer)(x)") @test repr("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 1 method)" - @test repr("text/plain", Core.arraysize) == "arraysize (built-in function)" + @test repr("text/plain", Core.getfield) == "getfield (built-in function)" err_str = @except_stackframe String() ErrorException @test err_str == "String() at $sn:$(method_defs_lineno + 0)"