Skip to content

Commit

Permalink
Merge pull request #40029 from JuliaLang/backports-release-1.6
Browse files Browse the repository at this point in the history
Backports release 1.6-RC3
  • Loading branch information
KristofferC authored Mar 16, 2021
2 parents 4b6b9fe + 845a52b commit e4a979d
Show file tree
Hide file tree
Showing 21 changed files with 235 additions and 58 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ Standard library changes
results table ([#38042]).
* `@testset` now supports the option `verbose` to show the test result summary
of the children even if they all pass ([#33755]).
* In `LinearIndices(::Tuple)` and `CartesianIndices(::Tuple)`, integers (as opposed to ranges of integers) in the
argument tuple now consistently describe 1-based ranges, e.g, `CartesianIndices((3, 1:3))` is equivalent to
`CartesianIndices((1:3, 1:3))`. This is how tuples of integers have always been documented to work, but a
bug had caused erroneous behaviors with heterogeneous tuples containing both integers and ranges ([#37829], [#37928]).

#### Package Manager

Expand Down
62 changes: 29 additions & 33 deletions base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -344,26 +344,13 @@ function lift_leaves(compact::IncrementalCompact, @nospecialize(stmt),
else
typ = compact_exprtype(compact, leaf)
if !isa(typ, Const)
# TODO: (disabled since #27126)
# If the leaf is an old ssa value, insert a getfield here
# We will revisit this getfield later when compaction gets
# to the appropriate point.
# N.B.: This can be a bit dangerous because it can lead to
# infinite loops if we accidentally insert a node just ahead
# of where we are
if is_old(compact, leaf) && (isa(field, Int) || isa(field, Symbol))
(isa(typ, DataType) && (!typ.abstract)) || return nothing
@assert !typ.mutable
# If there's the potential for an undefref error on access, we cannot insert a getfield
if field > typ.ninitialized && !isbits(fieldtype(typ, field))
return nothing
lifted_leaves[leaf] = RefValue{Any}(insert_node!(compact, leaf, make_MaybeUndef(result_t), Expr(:call, :unchecked_getfield, SSAValue(leaf.id), field), true))
maybe_undef = true
else
return nothing
lifted_leaves[leaf] = RefValue{Any}(insert_node!(compact, leaf, result_t, Expr(:call, getfield, SSAValue(leaf.id), field), true))
end
continue
end
return nothing
end
leaf = typ.val
Expand Down Expand Up @@ -540,7 +527,6 @@ function getfield_elim_pass!(ir::IRCode)
result_t = compact_exprtype(compact, SSAValue(idx))
is_getfield = is_setfield = false
is_ccall = false
is_unchecked = false
# Step 1: Check whether the statement we're looking at is a getfield/setfield!
if is_known_call(stmt, setfield!, compact)
is_setfield = true
Expand Down Expand Up @@ -580,9 +566,6 @@ function getfield_elim_pass!(ir::IRCode)
(isa(c1, Const) && isa(c2, Const)) && continue
lift_comparison!(compact, idx, c1, c2, stmt, lifting_cache)
continue
elseif isexpr(stmt, :call) && stmt.args[1] === :unchecked_getfield
is_getfield = true
is_unchecked = true
elseif isexpr(stmt, :foreigncall)
nccallargs = length(stmt.args[3]::SimpleVector)
new_preserves = Any[]
Expand Down Expand Up @@ -701,11 +684,11 @@ function getfield_elim_pass!(ir::IRCode)
val = perform_lifting!(compact, visited_phinodes, field, lifting_cache, result_t, lifted_leaves, stmt.args[2])

# Insert the undef check if necessary
if any_undef && !is_unchecked
if any_undef
if val === nothing
insert_node!(compact, SSAValue(idx), Nothing, Expr(:throw_undef_if_not, Symbol("##getfield##"), false))
else
insert_node!(compact, SSAValue(idx), Nothing, Expr(:undefcheck, Symbol("##getfield##"), val.x))
# val must be defined
end
else
@assert val !== nothing
Expand Down Expand Up @@ -939,16 +922,30 @@ function type_lift_pass!(ir::IRCode)
stmt = insts[idx][:inst]
stmt isa Expr || continue
if (stmt.head === :isdefined || stmt.head === :undefcheck)
val = (stmt.head === :isdefined) ? stmt.args[1] : stmt.args[2]
# undef can only show up by being introduced in a phi
# node (or an UpsilonNode() argument to a PhiC node),
# so lift all these nodes that have maybe undef values
# after optimization, undef can only show up by being introduced in
# a phi node (or an UpsilonNode() argument to a PhiC node), so lift
# all these nodes that have maybe undef values
val = stmt.args[(stmt.head === :isdefined) ? 1 : 2]
if stmt.head === :isdefined && (val isa Slot || val isa GlobalRef ||
isexpr(val, :static_parameter) || val isa Argument || val isa Symbol)
# this is a legal node, so assume it was not introduced by
# slot2ssa (at worst, we might leave in a runtime check that
# shouldn't have been there)
continue
end
# otherwise, we definitely have a corrupt node from slot2ssa, and
# must fix or delete that now
processed = IdDict{Int, Union{SSAValue, Bool}}()
while isa(val, SSAValue) && isa(insts[val.id][:inst], PiNode)
val = (insts[val.id][:inst]::PiNode).val
def = val
while true
# peek through PiNodes
isa(val, SSAValue) || break
def = insts[val.id][:inst]
isa(def, PiNode) || break
val = def.val
end
if !isa(val, SSAValue) || (!isa(insts[val.id][:inst], PhiNode) && !isa(insts[val.id][:inst], PhiCNode))
(isa(val, GlobalRef) || isexpr(val, :static_parameter)) && continue
if !isa(val, SSAValue) || (!isa(def, PhiNode) && !isa(def, PhiCNode))
# in most cases, reaching this statement implies we had a value
if stmt.head === :undefcheck
insts[idx][:inst] = nothing
else
Expand All @@ -958,7 +955,6 @@ function type_lift_pass!(ir::IRCode)
end
stmt_id = val.id
worklist = Tuple{Int, Int, SSAValue, Int}[(stmt_id, 0, SSAValue(0), 0)]
def = insts[stmt_id][:inst]
if !haskey(lifted_undef, stmt_id)
first = true
while !isempty(worklist)
Expand Down Expand Up @@ -1034,11 +1030,11 @@ function type_lift_pass!(ir::IRCode)
end
end
end
if stmt.head === :isdefined
insts[idx][:inst] = lifted_undef[stmt_id]
else
insts[idx][:inst] = Expr(:throw_undef_if_not, stmt.args[1], lifted_undef[stmt_id])
inst = lifted_undef[stmt_id]
if stmt.head === :undefcheck
inst = Expr(:throw_undef_if_not, stmt.args[1], inst)
end
insts[idx][:inst] = inst
end
end
ir
Expand Down
4 changes: 3 additions & 1 deletion base/compiler/ssair/slot2ssa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, @nospecializ
return undef_token
end
if !isa(ssa, Argument) && !(ssa === nothing) && ((ci.slotflags[slot] & SLOT_USEDUNDEF) != 0)
# insert a temporary node. type_lift_pass! will remove it
insert_node!(ir, idx, Any, Expr(:undefcheck, ci.slotnames[slot], ssa))
end
if isa(stmt, SlotNumber)
Expand Down Expand Up @@ -147,6 +148,7 @@ function fixemup!(cond, rename, ir::IRCode, ci::CodeInfo, idx::Int, @nospecializ
return true
end
end
# temporarily corrupt the isdefined node. type_lift_pass! will fix it
stmt.args[1] = ssa
end
return stmt
Expand All @@ -162,7 +164,7 @@ function fixemup!(cond, rename, ir::IRCode, ci::CodeInfo, idx::Int, @nospecializ
return nothing
end
op[] = x
elseif isa(val, GlobalRef) && !isdefined(val.mod, val.name)
elseif isa(val, GlobalRef) && !(isdefined(val.mod, val.name) && isconst(val.mod, val.name))
op[] = NewSSAValue(insert_node!(ir, idx, Any, val).id - length(ir.stmts))
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/verify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
end
end
elseif isa(op, GlobalRef)
if !isdefined(op.mod, op.name)
if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name)
@verify_error "Unbound GlobalRef not allowed in value position"
error("")
end
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
end
return allconst ? Const(ty) : Type{ty}
end
istuple = (headtype == Tuple)
istuple = isa(headtype, Type) && (headtype == Tuple)
if !istuple && !isa(headtype, UnionAll)
return Union{}
end
Expand Down
13 changes: 6 additions & 7 deletions base/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -573,17 +573,17 @@ stacktrace_linebreaks()::Bool =
tryparse(Bool, get(ENV, "JULIA_STACKTRACE_LINEBREAKS", "false")) === true

function show_full_backtrace(io::IO, trace::Vector; print_linebreaks::Bool)
n = length(trace)
ndigits_max = ndigits(n)
num_frames = length(trace)
ndigits_max = ndigits(num_frames)

modulecolordict = copy(STACKTRACE_FIXEDCOLORS)
modulecolorcycler = Iterators.Stateful(Iterators.cycle(STACKTRACE_MODULECOLORS))

println(io, "\nStacktrace:")

for (i, frame) in enumerate(trace)
print_stackframe(io, i, frame, 1, ndigits_max, modulecolordict, modulecolorcycler)
if i < n
for (i, (frame, n)) in enumerate(trace)
print_stackframe(io, i, frame, n, ndigits_max, modulecolordict, modulecolorcycler)
if i < num_frames
println(io)
print_linebreaks && println(io)
end
Expand Down Expand Up @@ -782,8 +782,7 @@ function show_backtrace(io::IO, t::Vector)

try invokelatest(update_stackframes_callback[], filtered) catch end
# process_backtrace returns a Vector{Tuple{Frame, Int}}
frames = map(x->first(x)::StackFrame, filtered)
show_full_backtrace(io, frames; print_linebreaks = stacktrace_linebreaks())
show_full_backtrace(io, filtered; print_linebreaks = stacktrace_linebreaks())
return
end

Expand Down
2 changes: 1 addition & 1 deletion base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,7 @@ function symlink(target::AbstractString, link::AbstractString;
@static if Sys.iswindows()
# creating file/directory symlinks requires Administrator privileges
# while junction points apparently do not
if !(flags & UV_FS_SYMLINK_JUNCTION) && err == UV__EPERM
if flags & UV_FS_SYMLINK_JUNCTION == 0 && err == UV__EPERM
msg = "On Windows, creating symlinks requires Administrator privileges.\n$msg"
end
end
Expand Down
23 changes: 11 additions & 12 deletions src/llvm-alloc-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ void Optimizer::optimizeAll()
if (field.hasobjref) {
has_ref = true;
// This can be relaxed a little based on hasload
if (field.hasaggr || field.multiloc) {
// TODO: add support for hasaggr load/store
if (field.hasaggr || field.multiloc || field.size != sizeof(void*)) {
has_refaggr = true;
break;
}
Expand All @@ -361,15 +362,12 @@ void Optimizer::optimizeAll()
splitOnStack(orig);
continue;
}
if (has_ref) {
if (use_info.memops.size() != 1 || has_refaggr ||
use_info.memops.begin()->second.size != sz) {
if (use_info.hastypeof)
optimizeTag(orig);
continue;
}
// The object only has a single field that's a reference with only one kind of access.
if (has_refaggr) {
if (use_info.hastypeof)
optimizeTag(orig);
continue;
}
// The object has no fields with mix reference access
moveToStack(orig, sz, has_ref);
}
}
Expand Down Expand Up @@ -444,6 +442,7 @@ Optimizer::AllocUseInfo::getField(uint32_t offset, uint32_t size, Type *elty)
if (it->first + it->second.size >= offset + size) {
if (it->second.elty != elty)
it->second.elty = nullptr;
assert(it->second.elty == nullptr || (it->first == offset && it->second.size == size));
return *it;
}
if (it->first + it->second.size > offset) {
Expand All @@ -454,7 +453,7 @@ Optimizer::AllocUseInfo::getField(uint32_t offset, uint32_t size, Type *elty)
else {
it = memops.begin();
}
// Now fine the last slot that overlaps with the current memory location.
// Now find the last slot that overlaps with the current memory location.
// Also set `lb` if we didn't find any above.
for (; it != end && it->first < offset + size; ++it) {
if (lb == end)
Expand Down Expand Up @@ -493,8 +492,8 @@ bool Optimizer::AllocUseInfo::addMemOp(Instruction *inst, unsigned opno, uint32_
memop.isaggr = isa<StructType>(elty) || isa<ArrayType>(elty) || isa<VectorType>(elty);
memop.isobjref = hasObjref(elty);
auto &field = getField(offset, size, elty);
if (field.first != offset || field.second.size != size)
field.second.multiloc = true;
if (field.second.hasobjref != memop.isobjref)
field.second.multiloc = true; // can't split this field, since it contains a mix of references and bits
if (!isstore)
field.second.hasload = true;
if (memop.isobjref) {
Expand Down
8 changes: 8 additions & 0 deletions stdlib/LinearAlgebra/src/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,14 @@ end
*(x::Transpose{<:Any,<:AbstractVector}, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y)
dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y)

dot(A::Diagonal, B::Diagonal) = dot(A.diag, B.diag)
function dot(D::Diagonal, B::AbstractMatrix)
size(D) == size(B) || throw(DimensionMismatch("Matrix sizes $(size(D)) and $(size(B)) differ"))
return dot(D.diag, view(B, diagind(B)))
end

dot(A::AbstractMatrix, B::Diagonal) = conj(dot(B, A))

function _mapreduce_prod(f, x, D::Diagonal, y)
if isempty(x) && isempty(D) && isempty(y)
return zero(Base.promote_op(f, eltype(x), eltype(D), eltype(y)))
Expand Down
9 changes: 9 additions & 0 deletions stdlib/LinearAlgebra/test/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -733,4 +733,13 @@ end
@test dot(zeros(Int32, 0), Diagonal(zeros(Int, 0)), zeros(Int16, 0)) === 0
end

@testset "Inner product" begin
A = Diagonal(rand(10) .+ im)
B = Diagonal(rand(10) .+ im)
@test dot(A, B) dot(Matrix(A), B)
@test dot(A, B) dot(A, Matrix(B))
@test dot(A, B) dot(Matrix(A), Matrix(B))
@test dot(A, B) conj(dot(B, A))
end

end # module TestDiagonal
2 changes: 2 additions & 0 deletions stdlib/Serialization/src/Serialization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,8 @@ function deserialize_typename(s::AbstractSerializer, number)
tn.mt.kwsorter = kws
end
end
elseif makenew
tn.mt = Symbol.name.mt
end
return tn::Core.TypeName
end
Expand Down
15 changes: 15 additions & 0 deletions stdlib/Serialization/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,18 @@ let s = join(rand('a':'z', 1024)), io = IOBuffer()
s2 = deserialize(io)
@test Base.summarysize(s2) < 2*sizeof(s)
end

# issue #39895
@eval Main begin
using Test, Serialization
let g = gensym(:g)
closure = eval(:(f -> $g(x) = f(x)))
inc(x) = x + 1
b = IOBuffer()
serialize(b, closure(inc))
seekstart(b)
f = deserialize(b)
# this should not crash
@test_broken f(1) == 2
end
end
36 changes: 36 additions & 0 deletions stdlib/SparseArrays/src/linalg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,42 @@ function dot(x::SparseVector, A::AbstractSparseMatrixCSC, y::SparseVector)
r
end

const WrapperMatrixTypes{T,MT} = Union{
SubArray{T,2,MT},
Adjoint{T,MT},
Transpose{T,MT},
AbstractTriangular{T,MT},
UpperHessenberg{T,MT},
Symmetric{T,MT},
Hermitian{T,MT},
}

function dot(A::MA, B::AbstractSparseMatrixCSC{TB}) where {MA<:Union{DenseMatrixUnion,WrapperMatrixTypes{<:Any,Union{DenseMatrixUnion,AbstractSparseMatrix}}},TB}
T = promote_type(eltype(A), TB)
(m, n) = size(A)
if (m, n) != size(B)
throw(DimensionMismatch())
end
s = zero(T)
if m * n == 0
return s
end
rows = rowvals(B)
vals = nonzeros(B)
@inbounds for j in 1:n
for ridx in nzrange(B, j)
i = rows[ridx]
v = vals[ridx]
s += dot(A[i,j], v)
end
end
return s
end

function dot(A::AbstractSparseMatrixCSC{TA}, B::MB) where {TA,MB<:Union{DenseMatrixUnion,WrapperMatrixTypes{<:Any,Union{DenseMatrixUnion,AbstractSparseMatrix}}}}
return conj(dot(B, A))
end

## triangular sparse handling

possible_adjoint(adj::Bool, a::Real) = a
Expand Down
Loading

0 comments on commit e4a979d

Please sign in to comment.