From f13843d0e7403732846eb91445546b60d1de1dec Mon Sep 17 00:00:00 2001 From: Stephan Hilb Date: Fri, 24 Apr 2020 10:12:17 +0200 Subject: [PATCH 001/232] zero-pad milliseconds for DateTime fixes #35571 --- stdlib/Dates/src/io.jl | 4 ++-- stdlib/Dates/test/conversions.jl | 32 ++++++++++++++++---------------- stdlib/Dates/test/io.jl | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 3ca2a624155e0..ff192092949e8 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -580,9 +580,9 @@ end # show function Base.print(io::IO, dt::DateTime) str = if millisecond(dt) == 0 - format(dt, dateformat"YYYY-mm-dd\THH:MM:SS", 24) + format(dt, dateformat"YYYY-mm-dd\THH:MM:SS", 19) else - format(dt, dateformat"YYYY-mm-dd\THH:MM:SS.s", 26) + format(dt, dateformat"YYYY-mm-dd\THH:MM:SS.sss", 23) end print(io, str) end diff --git a/stdlib/Dates/test/conversions.jl b/stdlib/Dates/test/conversions.jl index bf3196731beab..488af4110e884 100644 --- a/stdlib/Dates/test/conversions.jl +++ b/stdlib/Dates/test/conversions.jl @@ -11,28 +11,28 @@ using Dates @test Dates.value(Dates.DateTime(1970)) == Dates.UNIXEPOCH # Tests from here: https://en.wikipedia.org/wiki/Unix_time - @test string(Dates.unix2datetime(1095379198.75)) == string("2004-09-16T23:59:58.75") + @test string(Dates.unix2datetime(1095379198.75)) == string("2004-09-16T23:59:58.750") @test string(Dates.unix2datetime(1095379199.00)) == string("2004-09-16T23:59:59") - @test string(Dates.unix2datetime(1095379199.25)) == string("2004-09-16T23:59:59.25") - @test string(Dates.unix2datetime(1095379199.50)) == string("2004-09-16T23:59:59.5") - @test string(Dates.unix2datetime(1095379199.75)) == string("2004-09-16T23:59:59.75") + @test string(Dates.unix2datetime(1095379199.25)) == string("2004-09-16T23:59:59.250") + @test string(Dates.unix2datetime(1095379199.50)) == string("2004-09-16T23:59:59.500") + @test string(Dates.unix2datetime(1095379199.75)) == string("2004-09-16T23:59:59.750") @test string(Dates.unix2datetime(1095379200.00)) == string("2004-09-17T00:00:00") - @test string(Dates.unix2datetime(1095379200.25)) == string("2004-09-17T00:00:00.25") - @test string(Dates.unix2datetime(1095379200.50)) == string("2004-09-17T00:00:00.5") - @test string(Dates.unix2datetime(1095379200.75)) == string("2004-09-17T00:00:00.75") + @test string(Dates.unix2datetime(1095379200.25)) == string("2004-09-17T00:00:00.250") + @test string(Dates.unix2datetime(1095379200.50)) == string("2004-09-17T00:00:00.500") + @test string(Dates.unix2datetime(1095379200.75)) == string("2004-09-17T00:00:00.750") @test string(Dates.unix2datetime(1095379201.00)) == string("2004-09-17T00:00:01") - @test string(Dates.unix2datetime(1095379201.25)) == string("2004-09-17T00:00:01.25") - @test string(Dates.unix2datetime(915148798.75)) == string("1998-12-31T23:59:58.75") + @test string(Dates.unix2datetime(1095379201.25)) == string("2004-09-17T00:00:01.250") + @test string(Dates.unix2datetime(915148798.75)) == string("1998-12-31T23:59:58.750") @test string(Dates.unix2datetime(915148799.00)) == string("1998-12-31T23:59:59") - @test string(Dates.unix2datetime(915148799.25)) == string("1998-12-31T23:59:59.25") - @test string(Dates.unix2datetime(915148799.50)) == string("1998-12-31T23:59:59.5") - @test string(Dates.unix2datetime(915148799.75)) == string("1998-12-31T23:59:59.75") + @test string(Dates.unix2datetime(915148799.25)) == string("1998-12-31T23:59:59.250") + @test string(Dates.unix2datetime(915148799.50)) == string("1998-12-31T23:59:59.500") + @test string(Dates.unix2datetime(915148799.75)) == string("1998-12-31T23:59:59.750") @test string(Dates.unix2datetime(915148800.00)) == string("1999-01-01T00:00:00") - @test string(Dates.unix2datetime(915148800.25)) == string("1999-01-01T00:00:00.25") - @test string(Dates.unix2datetime(915148800.50)) == string("1999-01-01T00:00:00.5") - @test string(Dates.unix2datetime(915148800.75)) == string("1999-01-01T00:00:00.75") + @test string(Dates.unix2datetime(915148800.25)) == string("1999-01-01T00:00:00.250") + @test string(Dates.unix2datetime(915148800.50)) == string("1999-01-01T00:00:00.500") + @test string(Dates.unix2datetime(915148800.75)) == string("1999-01-01T00:00:00.750") @test string(Dates.unix2datetime(915148801.00)) == string("1999-01-01T00:00:01") - @test string(Dates.unix2datetime(915148801.25)) == string("1999-01-01T00:00:01.25") + @test string(Dates.unix2datetime(915148801.25)) == string("1999-01-01T00:00:01.250") end @testset "conversion to/from Rata Die" begin diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index 94d7b75727565..450b2b9c92eee 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -18,7 +18,7 @@ using Dates @test string(Dates.DateTime(2000, 1, 1, 0, 0, 0, 1)) == "2000-01-01T00:00:00.001" @test sprint(show, Dates.DateTime(2000, 1, 1, 0, 0, 0, 1)) == "Dates.DateTime(\"2000-01-01T00:00:00.001\")" @test string(Dates.DateTime(2000, 1, 1, 0, 0, 0, 2)) == "2000-01-01T00:00:00.002" - @test string(Dates.DateTime(2000, 1, 1, 0, 0, 0, 500)) == "2000-01-01T00:00:00.5" + @test string(Dates.DateTime(2000, 1, 1, 0, 0, 0, 500)) == "2000-01-01T00:00:00.500" @test string(Dates.DateTime(2000, 1, 1, 0, 0, 0, 998)) == "2000-01-01T00:00:00.998" @test string(Dates.DateTime(2000, 1, 1, 0, 0, 0, 999)) == "2000-01-01T00:00:00.999" end From 4bad702c946dfcb2d0adedd1150a2c1d84da30ce Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 18 May 2020 17:15:05 -0400 Subject: [PATCH 002/232] [build] silence some useless gcc warnings --- src/Makefile | 2 +- src/llvm-version.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 1c51e01d45c92..5b1cecf67cfca 100644 --- a/src/Makefile +++ b/src/Makefile @@ -21,7 +21,7 @@ FLAGS := \ -I$(JULIAHOME)/deps/valgrind ifneq ($(USEMSVC), 1) FLAGS += -Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fvisibility=hidden -fno-common \ - -Wpointer-arith -Wundef + -Wno-comment -Wpointer-arith -Wundef ifeq ($(USEGCC),1) # GCC bug #25509 (void)__attribute__((warn_unused_result)) FLAGS += -Wno-unused-result endif diff --git a/src/llvm-version.h b/src/llvm-version.h index d9be9101e7c24..a00ccd12c1f5e 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -17,3 +17,8 @@ #endif #define LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING 0 + +#if defined(__GNUC__) && (__GNUC__ >= 9) +// Added in GCC 9, this warning is annoying +#pragma GCC diagnostic ignored "-Winit-list-lifetime" +#endif From 1b44fc390cde5fd7b9bcf4578d5e753375bb7b5a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 18 May 2020 17:16:24 -0400 Subject: [PATCH 003/232] [llvm] add some more debugger stubs for dumping LLVM machine objects --- src/codegen.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 487f1a365a171..e35c7fc4da999 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7583,6 +7583,22 @@ extern "C" void jl_dump_llvm_debugloc(void *v) llvm_dump((DebugLoc*)v); } +namespace llvm { + class MachineBasicBlock; + class MachineFunction; + raw_ostream& operator<<(raw_ostream &OS, const MachineBasicBlock &MBB); + void printMIR(raw_ostream &OS, const MachineFunction &MF); +} +extern "C" void jl_dump_llvm_mbb(void *v) +{ + errs() << *(llvm::MachineBasicBlock*)v; +} +extern "C" void jl_dump_llvm_mfunction(void *v) +{ + llvm::printMIR(errs(), *(llvm::MachineFunction*)v); +} + + extern void jl_write_bitcode_func(void *F, char *fname) { std::error_code EC; raw_fd_ostream OS(fname, EC, sys::fs::F_None); From f0dd7818f04ea3d68d534f460b69a6c3b4afbfd2 Mon Sep 17 00:00:00 2001 From: Julian Samaroo Date: Mon, 18 May 2020 20:58:07 -0500 Subject: [PATCH 004/232] Make processor_arm.cpp compile on musl (#34777) --- src/processor_arm.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index 7b5fe0003bb56..96ca1789f5937 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -10,10 +10,16 @@ #include #include -#if defined(_CPU_AARCH64_) || __GLIBC_PREREQ(2, 16) +// This nesting is required to allow compilation on musl +#define USE_DYN_GETAUXVAL +#if defined(_CPU_AARCH64_) +# undef USE_DYN_GETAUXVAL # include -#else -# define DYN_GETAUXVAL +#elif defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2, 16) +# undef USE_DYN_GETAUXVAL +# include +# endif #endif namespace ARM { @@ -498,7 +504,7 @@ static constexpr size_t ncpu_names = sizeof(cpus) / sizeof(cpus[0]); # define AT_HWCAP2 26 #endif -#if defined(DYN_GETAUXVAL) +#if defined(USE_DYN_GETAUXVAL) static unsigned long getauxval_procfs(unsigned long type) { int fd = open("/proc/self/auxv", O_RDONLY); From cebd4fa959b6f9835a3abe70e705136b5e914e4a Mon Sep 17 00:00:00 2001 From: Rogerluo Date: Tue, 19 May 2020 10:42:57 -0400 Subject: [PATCH 005/232] add inplace kron (#31069) --- NEWS.md | 2 +- base/exports.jl | 1 + base/operators.jl | 2 + stdlib/LinearAlgebra/docs/src/index.md | 1 + stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 +- stdlib/LinearAlgebra/src/bitarray.jl | 26 ++++++-- stdlib/LinearAlgebra/src/dense.jl | 46 +++++++++++--- stdlib/LinearAlgebra/src/diagonal.jl | 60 +++++++++++++----- stdlib/SparseArrays/src/SparseArrays.jl | 2 +- stdlib/SparseArrays/src/linalg.jl | 75 ++++++++++++++++++----- 10 files changed, 169 insertions(+), 48 deletions(-) diff --git a/NEWS.md b/NEWS.md index c3173351ec9c5..ea03e90990f59 100644 --- a/NEWS.md +++ b/NEWS.md @@ -33,7 +33,7 @@ Build system changes New library functions --------------------- - +* New function `Base.kron!` and corresponding overloads for various matrix types for performing Kronecker product in-place. ([#31069]). New library features -------------------- diff --git a/base/exports.jl b/base/exports.jl index 316025db9ce6c..3d11cc0481931 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -463,6 +463,7 @@ export adjoint, transpose, kron, + kron!, # bitarrays falses, diff --git a/base/operators.jl b/base/operators.jl index c304530dfef80..c7998b51756c9 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -542,6 +542,8 @@ for op in (:+, :*, :&, :|, :xor, :min, :max, :kron) end end +function kron! end + const var"'" = adjoint """ diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index b78ed785080e0..c5f0448bfa629 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -409,6 +409,7 @@ Base.inv(::AbstractMatrix) LinearAlgebra.pinv LinearAlgebra.nullspace Base.kron +Base.kron! LinearAlgebra.exp(::StridedMatrix{<:LinearAlgebra.BlasFloat}) Base.:^(::AbstractMatrix, ::Number) Base.:^(::Number, ::AbstractMatrix) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index bb1dcb3c17ea7..e9476645d5e89 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -11,7 +11,7 @@ import Base: \, /, *, ^, +, -, == import Base: USE_BLAS64, abs, acos, acosh, acot, acoth, acsc, acsch, adjoint, asec, asech, asin, asinh, atan, atanh, axes, big, broadcast, ceil, conj, convert, copy, copyto!, cos, cosh, cot, coth, csc, csch, eltype, exp, fill!, floor, getindex, hcat, - getproperty, imag, inv, isapprox, isone, iszero, IndexStyle, kron, length, log, map, ndims, + getproperty, imag, inv, isapprox, isone, iszero, IndexStyle, kron, kron!, length, log, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, sec, sech, setindex!, show, similar, sin, sincos, sinh, size, sqrt, strides, stride, tan, tanh, transpose, trunc, typed_hcat, vec diff --git a/stdlib/LinearAlgebra/src/bitarray.jl b/stdlib/LinearAlgebra/src/bitarray.jl index 3e38b073a992b..d1857c3c38659 100644 --- a/stdlib/LinearAlgebra/src/bitarray.jl +++ b/stdlib/LinearAlgebra/src/bitarray.jl @@ -92,22 +92,29 @@ qr(A::BitMatrix) = qr(float(A)) ## kron -function kron(a::BitVector, b::BitVector) +@inline function kron!(R::BitVector, a::BitVector, b::BitVector) m = length(a) n = length(b) - R = falses(n * m) + @boundscheck length(R) == n*m || throw(DimensionMismatch()) Rc = R.chunks bc = b.chunks for j = 1:m a[j] && Base.copy_chunks!(Rc, (j-1)*n+1, bc, 1, n) end - R + return R end -function kron(a::BitMatrix, b::BitMatrix) +function kron(a::BitVector, b::BitVector) + m = length(a) + n = length(b) + R = falses(n * m) + return @inbounds kron!(R, a, b) +end + +function kron!(R::BitMatrix, a::BitMatrix, b::BitMatrix) mA,nA = size(a) mB,nB = size(b) - R = falses(mA*mB, nA*nB) + @boundscheck size(R) == (mA*mB, nA*nB) || throw(DimensionMismatch()) for i = 1:mA ri = (1:mB) .+ ((i-1)*mB) @@ -118,7 +125,14 @@ function kron(a::BitMatrix, b::BitMatrix) end end end - R + return R +end + +function kron(a::BitMatrix, b::BitMatrix) + mA,nA = size(a) + mB,nB = size(b) + R = falses(mA*mB, nA*nB) + return @inbounds kron!(R, a, b) end ## Structure query functions diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index d33cd809f5339..8a57a1eaf52cf 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -336,6 +336,29 @@ function tr(A::Matrix{T}) where T t end +""" + kron!(C, A, B) + +`kron!` is the in-place version of [`kron`](@ref). Computes `kron(A, B)` and stores the result in `C` +overwriting the existing value of `C`. + +!!! tip + Bounds checking can be disabled by [`@inbounds`](@ref), but you need to take care of the shape + of `C`, `A`, `B` yourself. +""" +@inline function kron!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractMatrix) + require_one_based_indexing(A, B) + @boundscheck (size(C) == (size(A,1)*size(B,1), size(A,2)*size(B,2))) || throw(DimensionMismatch()) + m = 0 + @inbounds for j = 1:size(A,2), l = 1:size(B,2), i = 1:size(A,1) + Aij = A[i,j] + for k = 1:size(B,1) + C[m += 1] = Aij*B[k,l] + end + end + return C +end + """ kron(A, B) @@ -383,18 +406,23 @@ julia> reshape(kron(v,w), (length(w), length(v))) ``` """ function kron(a::AbstractMatrix{T}, b::AbstractMatrix{S}) where {T,S} - require_one_based_indexing(a, b) R = Matrix{promote_op(*,T,S)}(undef, size(a,1)*size(b,1), size(a,2)*size(b,2)) - m = 0 - @inbounds for j = 1:size(a,2), l = 1:size(b,2), i = 1:size(a,1) - aij = a[i,j] - for k = 1:size(b,1) - R[m += 1] = aij*b[k,l] - end - end - R + return @inbounds kron!(R, a, b) end +kron!(c::AbstractVecOrMat, a::AbstractVecOrMat, b::Number) = mul!(c, a, b) + +Base.@propagate_inbounds function kron!(c::AbstractVector, a::AbstractVector, b::AbstractVector) + C = reshape(c, length(a)*length(b), 1) + A = reshape(a ,length(a), 1) + B = reshape(b, length(b), 1) + kron!(C, A, B) + return c +end + +Base.@propagate_inbounds kron!(C::AbstractMatrix, a::AbstractMatrix, b::AbstractVector) = kron!(C, a, reshape(b, length(b), 1)) +Base.@propagate_inbounds kron!(C::AbstractMatrix, a::AbstractVector, b::AbstractMatrix) = kron!(C, reshape(a, length(a), 1), b) + kron(a::Number, b::Union{Number, AbstractVecOrMat}) = a * b kron(a::AbstractVecOrMat, b::Number) = a * b kron(a::AbstractVector, b::AbstractVector) = vec(kron(reshape(a ,length(a), 1), reshape(b, length(b), 1))) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index f3b4ac17eec78..27e8ba4c80f3d 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -493,52 +493,80 @@ rdiv!(A::AbstractMatrix{T}, transD::Transpose{<:Any,<:Diagonal{T}}) where {T} = (\)(A::Union{QR,QRCompactWY,QRPivoted}, B::Diagonal) = invoke(\, Tuple{Union{QR,QRCompactWY,QRPivoted}, AbstractVecOrMat}, A, B) -function kron(A::Diagonal{T1}, B::Diagonal{T2}) where {T1<:Number, T2<:Number} + +@inline function kron!(C::AbstractMatrix{T}, A::Diagonal, B::Diagonal) where T + fill!(C, zero(T)) valA = A.diag; nA = length(valA) valB = B.diag; nB = length(valB) - valC = Vector{typeof(zero(T1)*zero(T2))}(undef,nA*nB) + nC = checksquare(C) + @boundscheck nC == nA*nB || + throw(DimensionMismatch("expect C to be a $(nA*nB)x$(nA*nB) matrix, got size $(nC)x$(nC)")) + @inbounds for i = 1:nA, j = 1:nB - valC[(i-1)*nB+j] = valA[i] * valB[j] + idx = (i-1)*nB+j + C[idx, idx] = valA[i] * valB[j] end - return Diagonal(valC) + return C end -function kron(A::Diagonal{T}, B::AbstractMatrix{S}) where {T<:Number, S<:Number} +function kron(A::Diagonal{T1}, B::Diagonal{T2}) where {T1<:Number, T2<:Number} + valA = A.diag; nA = length(valA) + valB = B.diag; nB = length(valB) + valC = Vector{typeof(zero(T1)*zero(T2))}(undef,nA*nB) + C = Diagonal(valC) + return @inbounds kron!(C, A, B) +end + +@inline function kron!(C::AbstractMatrix, A::Diagonal, B::AbstractMatrix) Base.require_one_based_indexing(B) - (mA, nA) = size(A); (mB, nB) = size(B) - R = zeros(Base.promote_op(*, T, S), mA * mB, nA * nB) + (mA, nA) = size(A); (mB, nB) = size(B); (mC, nC) = size(C); + @boundscheck (mC, nC) == (mA * mB, nA * nB) || + throw(DimensionMismatch("expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) m = 1 - for j = 1:nA + @inbounds for j = 1:nA A_jj = A[j,j] for k = 1:nB for l = 1:mB - R[m] = A_jj * B[l,k] + C[m] = A_jj * B[l,k] m += 1 end m += (nA - 1) * mB end m += mB end - return R + return C end -function kron(A::AbstractMatrix{T}, B::Diagonal{S}) where {T<:Number, S<:Number} +@inline function kron!(C::AbstractMatrix, A::AbstractMatrix, B::Diagonal) require_one_based_indexing(A) - (mA, nA) = size(A); (mB, nB) = size(B) - R = zeros(promote_op(*, T, S), mA * mB, nA * nB) + (mA, nA) = size(A); (mB, nB) = size(B); (mC, nC) = size(C); + @boundscheck (mC, nC) == (mA * mB, nA * nB) || + throw(DimensionMismatch("expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) m = 1 - for j = 1:nA + @inbounds for j = 1:nA for l = 1:mB Bll = B[l,l] for k = 1:mA - R[m] = A[k,j] * Bll + C[m] = A[k,j] * Bll m += nB end m += 1 end m -= nB end - return R + return C +end + +function kron(A::Diagonal{T}, B::AbstractMatrix{S}) where {T<:Number, S<:Number} + (mA, nA) = size(A); (mB, nB) = size(B) + R = zeros(Base.promote_op(*, T, S), mA * mB, nA * nB) + return @inbounds kron!(R, A, B) +end + +function kron(A::AbstractMatrix{T}, B::Diagonal{S}) where {T<:Number, S<:Number} + (mA, nA) = size(A); (mB, nB) = size(B) + R = zeros(promote_op(*, T, S), mA * mB, nA * nB) + return @inbounds kron!(R, A, B) end conj(D::Diagonal) = Diagonal(conj(D.diag)) diff --git a/stdlib/SparseArrays/src/SparseArrays.jl b/stdlib/SparseArrays/src/SparseArrays.jl index f4867be25a042..0616763205696 100644 --- a/stdlib/SparseArrays/src/SparseArrays.jl +++ b/stdlib/SparseArrays/src/SparseArrays.jl @@ -24,7 +24,7 @@ import Base: acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, tand, tanh, trunc, abs, abs2, broadcast, ceil, complex, conj, convert, copy, copyto!, adjoint, exp, expm1, findall, findmax, findmin, float, getindex, - vcat, hcat, hvcat, cat, imag, argmax, kron, length, log, log1p, max, min, + vcat, hcat, hvcat, cat, imag, argmax, kron, kron!, length, log, log1p, max, min, maximum, minimum, one, promote_eltype, real, reshape, rot180, rotl90, rotr90, round, setindex!, similar, size, transpose, vec, permute!, map, map!, Array, diff, circshift!, circshift diff --git a/stdlib/SparseArrays/src/linalg.jl b/stdlib/SparseArrays/src/linalg.jl index c9d25c58750bd..c54a6c760c0fd 100644 --- a/stdlib/SparseArrays/src/linalg.jl +++ b/stdlib/SparseArrays/src/linalg.jl @@ -1295,16 +1295,21 @@ function opnormestinv(A::AbstractSparseMatrixCSC{T}, t::Integer = min(2,maximum( end ## kron - -# sparse matrix ⊗ sparse matrix -function kron(A::AbstractSparseMatrixCSC{T1,S1}, B::AbstractSparseMatrixCSC{T2,S2}) where {T1,S1,T2,S2} +@inline function kron!(C::SparseMatrixCSC, A::AbstractSparseMatrixCSC, B::AbstractSparseMatrixCSC) nnzC = nnz(A)*nnz(B) mA, nA = size(A); mB, nB = size(B) mC, nC = mA*mB, nA*nB - colptrC = Vector{promote_type(S1,S2)}(undef, nC+1) - rowvalC = Vector{promote_type(S1,S2)}(undef, nnzC) - nzvalC = Vector{typeof(one(T1)*one(T2))}(undef, nnzC) - colptrC[1] = 1 + + rowvalC = rowvals(C) + nzvalC = nonzeros(C) + colptrC = getcolptr(C) + + @boundscheck begin + length(colptrC) == nC+1 || throw(DimensionMismatch("expect C to be preallocated with $(nC+1) colptrs ")) + length(rowvalC) == nnzC || throw(DimensionMismatch("expect C to be preallocated with $(nnzC) rowvals")) + length(nzvalC) == nnzC || throw(DimensionMismatch("expect C to be preallocated with $(nnzC) nzvals")) + end + col = 1 @inbounds for j = 1:nA startA = getcolptr(A)[j] @@ -1328,7 +1333,43 @@ function kron(A::AbstractSparseMatrixCSC{T1,S1}, B::AbstractSparseMatrixCSC{T2,S end end end - return SparseMatrixCSC(mC, nC, colptrC, rowvalC, nzvalC) + return C +end + +@inline function kron!(z::SparseVector, x::SparseVector, y::SparseVector) + nnzx = nnz(x); nnzy = nnz(y); nnzz = nnz(z); + nzind = nonzeroinds(z) + nzval = nonzeros(z) + + @boundscheck begin + nnzval = length(nzval); nnzind = length(nzind) + nnzz = nnzx*nnzy + nnzval == nnzz || throw(DimensionMismatch("expect z to be preallocated with $nnzz nonzeros")) + nnzind == nnzz || throw(DimensionMismatch("expect z to be preallocated with $nnzz nonzeros")) + end + + @inbounds for i = 1:nnzx, j = 1:nnzy + this_ind = (i-1)*nnzy+j + nzind[this_ind] = (nonzeroinds(x)[i]-1)*length(y) + nonzeroinds(y)[j] + nzval[this_ind] = nonzeros(x)[i] * nonzeros(y)[j] + end + return z +end + +# sparse matrix ⊗ sparse matrix +function kron(A::AbstractSparseMatrixCSC{T1,S1}, B::AbstractSparseMatrixCSC{T2,S2}) where {T1,S1,T2,S2} + nnzC = nnz(A)*nnz(B) + mA, nA = size(A); mB, nB = size(B) + mC, nC = mA*mB, nA*nB + Tv = typeof(one(T1)*one(T2)) + Ti = promote_type(S1,S2) + colptrC = Vector{Ti}(undef, nC+1) + rowvalC = Vector{Ti}(undef, nnzC) + nzvalC = Vector{Tv}(undef, nnzC) + colptrC[1] = 1 + # skip sparse_check + C = SparseMatrixCSC{Tv, Ti}(mC, nC, colptrC, rowvalC, nzvalC) + return @inbounds kron!(C, A, B) end # sparse vector ⊗ sparse vector @@ -1337,27 +1378,33 @@ function kron(x::SparseVector{T1,S1}, y::SparseVector{T2,S2}) where {T1,S1,T2,S2 nnzz = nnzx*nnzy # number of nonzeros in new vector nzind = Vector{promote_type(S1,S2)}(undef, nnzz) # the indices of nonzeros nzval = Vector{typeof(one(T1)*one(T2))}(undef, nnzz) # the values of nonzeros - @inbounds for i = 1:nnzx, j = 1:nnzy - this_ind = (i-1)*nnzy+j - nzind[this_ind] = (nonzeroinds(x)[i]-1)*length(y::SparseVector) + nonzeroinds(y)[j] - nzval[this_ind] = nonzeros(x)[i] * nonzeros(y)[j] - end - return SparseVector(length(x::SparseVector)*length(y::SparseVector), nzind, nzval) + z = SparseVector(length(x)*length(y), nzind, nzval) + return @inbounds kron!(z, x, y) end # sparse matrix ⊗ sparse vector & vice versa +Base.@propagate_inbounds kron!(C::SparseMatrixCSC, A::AbstractSparseMatrixCSC, x::SparseVector) = kron!(C, A, SparseMatrixCSC(x)) +Base.@propagate_inbounds kron!(C::SparseMatrixCSC, x::SparseVector, A::AbstractSparseMatrixCSC) = kron!(C, SparseMatrixCSC(x), A) + kron(A::AbstractSparseMatrixCSC, x::SparseVector) = kron(A, SparseMatrixCSC(x)) kron(x::SparseVector, A::AbstractSparseMatrixCSC) = kron(SparseMatrixCSC(x), A) # sparse vec/mat ⊗ vec/mat and vice versa +Base.@propagate_inbounds kron!(C::SparseMatrixCSC, A::Union{SparseVector,AbstractSparseMatrixCSC}, B::VecOrMat) = kron!(C, A, sparse(B)) +Base.@propagate_inbounds kron!(C::SparseMatrixCSC, A::VecOrMat, B::Union{SparseVector,AbstractSparseMatrixCSC}) = kron!(C, sparse(A), B) + kron(A::Union{SparseVector,AbstractSparseMatrixCSC}, B::VecOrMat) = kron(A, sparse(B)) kron(A::VecOrMat, B::Union{SparseVector,AbstractSparseMatrixCSC}) = kron(sparse(A), B) # sparse vec/mat ⊗ Diagonal and vice versa +Base.@propagate_inbounds kron!(C::SparseMatrixCSC, A::Diagonal{T}, B::Union{SparseVector{S}, AbstractSparseMatrixCSC{S}}) where {T<:Number, S<:Number} = kron!(C, sparse(A), B) +Base.@propagate_inbounds kron!(C::SparseMatrixCSC, A::Union{SparseVector{T}, AbstractSparseMatrixCSC{T}}, B::Diagonal{S}) where {T<:Number, S<:Number} = kron!(C, A, sparse(B)) + kron(A::Diagonal{T}, B::Union{SparseVector{S}, AbstractSparseMatrixCSC{S}}) where {T<:Number, S<:Number} = kron(sparse(A), B) kron(A::Union{SparseVector{T}, AbstractSparseMatrixCSC{T}}, B::Diagonal{S}) where {T<:Number, S<:Number} = kron(A, sparse(B)) # sparse outer product +kron!(C::SparseMatrixCSC, A::SparseVectorUnion, B::AdjOrTransSparseVectorUnion) = broadcast!(*, C, A, B) kron(A::SparseVectorUnion, B::AdjOrTransSparseVectorUnion) = A .* B ## det, inv, cond From 9d70d450e771236b7e1e7cb0c7ac965a8843d662 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 19 May 2020 16:27:45 -0400 Subject: [PATCH 006/232] optimize more tuple splatting independent of length when possible (#35930) --- base/compiler/ssair/inlining.jl | 26 ++++++++++++++++++++------ test/compiler/inline.jl | 5 +++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 7c42385f28a98..2acead5985078 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -895,6 +895,26 @@ function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::Optimizatio if arg_start > length(atypes) return nothing end + ft = atypes[arg_start] + if ft isa Const && ft.val === Core.tuple + # if one argument is a tuple already, and the rest are empty, we can just return it + # e.g. rewrite `((t::Tuple)...,)` to `t` + nonempty_idx = 0 + for i = (arg_start + 1):length(atypes) + ti = atypes[i] + ti ⊑ Tuple{} && continue + if ti ⊑ Tuple && nonempty_idx == 0 + nonempty_idx = i + continue + end + nonempty_idx = 0 + break + end + if nonempty_idx != 0 + ir.stmts[idx] = stmt.args[nonempty_idx] + return nothing + end + end # Try to figure out the signature of the function being called # and if rewrite_apply_exprargs can deal with this form for i = (arg_start + 1):length(atypes) @@ -905,12 +925,6 @@ function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::Optimizatio end # Independent of whether we can inline, the above analysis allows us to rewrite # this apply call to a regular call - ft = atypes[arg_start] - if length(atypes) == arg_start+1 && ft isa Const && ft.val === Core.tuple && atypes[arg_start+1] ⊑ Tuple - # rewrite `((t::Tuple)...,)` to `t` - ir.stmts[idx] = stmt.args[arg_start+1] - return nothing - end stmt.args, atypes = rewrite_apply_exprargs!(ir, idx, stmt.args, atypes, arg_start) has_free_typevars(ft) && return nothing f = singleton_type(ft) diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 1ff068ae76e7e..0fa46b4554a61 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -222,6 +222,11 @@ end f_identity_splat(t) = (t...,) @test length(code_typed(f_identity_splat, (Tuple{Int,Int},))[1][1].code) == 1 +# splatting one tuple into (,) plus zero or more empties should reduce +# this pattern appears for example in `fill_to_length` +f_splat_with_empties(t) = (()..., t..., ()..., ()...) +@test length(code_typed(f_splat_with_empties, (NTuple{200,UInt8},))[1][1].code) == 1 + # check that <: can be fully eliminated struct SomeArbitraryStruct; end function f_subtype() From 9655c212522176a0df6f2d87d8ae44620ed32ce3 Mon Sep 17 00:00:00 2001 From: Lyndon White Date: Wed, 20 May 2020 02:37:16 +0100 Subject: [PATCH 007/232] Make view of a string return a substring (#35879) * make taking views of string indexing give substrings * add news * Remove excess whitespace * handle single character string views correctly * test values of views * make substring etc work on AbstractUnitRanges * Fix missing space --- NEWS.md | 2 ++ base/strings/substring.jl | 6 +++++- test/strings/basic.jl | 31 +++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ea03e90990f59..e69006f869186 100644 --- a/NEWS.md +++ b/NEWS.md @@ -43,6 +43,8 @@ Standard library changes ------------------------ * The `nextprod` function now accepts tuples and other array types for its first argument ([#35791]). * The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]). +* `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). +* All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). #### LinearAlgebra diff --git a/base/strings/substring.jl b/base/strings/substring.jl index d98dab5ae790b..312ca1941fbce 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -37,7 +37,7 @@ end @propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j) @propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j)) -@propagate_inbounds SubString(s::AbstractString, r::UnitRange{<:Integer}) = SubString(s, first(r), last(r)) +@propagate_inbounds SubString(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, first(r), last(r)) @propagate_inbounds function SubString(s::SubString, i::Int, j::Int) @boundscheck i ≤ j && checkbounds(s, i:j) @@ -47,6 +47,10 @@ end SubString(s::AbstractString) = SubString(s, 1, lastindex(s)) SubString{T}(s::T) where {T<:AbstractString} = SubString{T}(s, 1, lastindex(s)) +@propagate_inbounds view(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r) +@propagate_inbounds maybeview(s::AbstractString, r::AbstractUnitRange{<:Integer}) = view(s, r) +@propagate_inbounds maybeview(s::AbstractString, args...) = getindex(s, args...) + convert(::Type{SubString{S}}, s::AbstractString) where {S<:AbstractString} = SubString(convert(S, s)) convert(::Type{T}, s::T) where {T<:SubString} = s diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 8dc7647a7219a..aa6974e53a157 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -166,6 +166,37 @@ end @test endswith(z)(z) end +@testset "SubStrings and Views" begin + x = "abcdefg" + @testset "basic unit range" begin + @test SubString(x, 2:4) == "bcd" + @test view(x, 2:4) == "bcd" + @test view(x, 2:4) isa SubString + @test (@view x[4:end]) == "defg" + @test (@view x[4:end]) isa SubString + end + + @testset "other AbstractUnitRanges" begin + @test SubString(x, Base.OneTo(3)) == "abc" + @test view(x, Base.OneTo(4)) == "abcd" + @test view(x, Base.OneTo(4)) isa SubString + end + + @testset "views but not view" begin + # We don't (at present) make non-contiguous SubStrings with views + @test_throws MethodError (@view x[[1,3,5]]) + @test (@views (x[[1,3,5]])) isa String + + # We don't (at present) make single character SubStrings with views + @test_throws MethodError (@view x[3]) + @test (@views (x[3])) isa Char + + @test (@views (x[3], x[1:2], x[[1,4]])) isa Tuple{Char, SubString, String} + @test (@views (x[3], x[1:2], x[[1,4]])) == ('c', "ab", "ad") + end +end + + @testset "filter specialization on String issue #32460" begin @test filter(x -> x ∉ ['작', 'Ï', 'z', 'ξ'], GenericString("J'étais n작작é pour plaiÏre à toute âξme un peu fière")) == From 8a0ae64f0682451b214f11147d83d13aeeb91898 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 7 May 2020 08:49:02 +0200 Subject: [PATCH 008/232] Add .clang-format file for Julia's C syntax. [ci skip] --- .clang-format | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000..0322d0f6749a9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,118 @@ +Language: Cpp +Standard: Cpp11 +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Stroustrup +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: false +ColumnLimit: 92 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^(<|"(llvm|llvm-c|clang|clang-c)/)' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 8 +UseCRLF: true +UseTab: Never +ForEachMacros: + - JL_TRY + - JL_CATCH +StatementMacros: + - bi_fintrinsic + - bi_iintrinsic_fast + - bi_intrinsic_ctype + - bool_fintrinsic + - bool_iintrinsic_fast + - bool_intrinsic_ctype + - checked_intrinsic_ctype + - cvt_iintrinsic + - fpiseq_n + - fpislt_n + - ter_fintrinsic + - ter_intrinsic_ctype + - un_fintrinsic + - un_fintrinsic_withtype + - un_iintrinsic_ctype + - uu_iintrinsic_ctype From 40a8b42d45a274a0f91565693830360faa12081c Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 20 May 2020 21:03:35 -0400 Subject: [PATCH 009/232] type docs: add generalization of NoFields example (#35938) --- doc/src/manual/types.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index c29c85500e130..c1c77ffa8acb9 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -405,6 +405,18 @@ true The [`===`](@ref) function confirms that the "two" constructed instances of `NoFields` are actually one and the same. Singleton types are described in further detail [below](@ref man-singleton-types). +More generally, if all the fields of an immutable structure are indistinguishable (`===`) then two +immutable values containing those fields are also indistinguishable: + +```jldoctest +julia> struct X + a::Int + b::Float64 + end + +julia> X(1, 2) === X(1, 2) +true +``` There is much more to say about how instances of composite types are created, but that discussion depends on both [Parametric Types](@ref) and on [Methods](@ref), and is sufficiently important From e5664aa8289cb7ae7d3186cd69589ad051d8b7d7 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 21 May 2020 16:21:23 +0200 Subject: [PATCH 010/232] Remove contrib/mac/{juliarc.jl,mac-gtk.sh} (#35881) Also remove a reference to contrib/mac/startup.jl from the main Makefile --- Makefile | 6 ------ contrib/mac/juliarc.jl | 9 --------- contrib/mac/mac-gtk.sh | 35 ----------------------------------- 3 files changed, 50 deletions(-) delete mode 100644 contrib/mac/juliarc.jl delete mode 100644 contrib/mac/mac-gtk.sh diff --git a/Makefile b/Makefile index 9e1a697b739ee..cad588c148d42 100644 --- a/Makefile +++ b/Makefile @@ -458,12 +458,6 @@ ifeq ($(OS), Linux) # Copy over any bundled ca certs we picked up from the system during buildi -cp $(build_datarootdir)/julia/cert.pem $(DESTDIR)$(datarootdir)/julia/ -endif - # Copy in startup.jl files per-platform for binary distributions as well - # Note that we don't install to sysconfdir: we always install to $(DESTDIR)$(prefix)/etc. - # If you want to make a distribution with a hardcoded path, you take care of installation -ifeq ($(OS), Darwin) - -cat $(JULIAHOME)/contrib/mac/startup.jl >> $(DESTDIR)$(prefix)/etc/julia/startup.jl endif ifeq ($(OS), WINNT) cd $(BUILDROOT)/julia-$(JULIA_COMMIT)/bin && rm -f llvm* llc.exe lli.exe opt.exe LTO.dll bugpoint.exe macho-dump.exe diff --git a/contrib/mac/juliarc.jl b/contrib/mac/juliarc.jl deleted file mode 100644 index 6822c1b1519e4..0000000000000 --- a/contrib/mac/juliarc.jl +++ /dev/null @@ -1,9 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Set up environment for Julia OSX binary distribution -let - ROOT = abspath(Sys.BINDIR,"..") - ENV["PATH"]="$(Sys.BINDIR):$(ENV["PATH"])" - ENV["FONTCONFIG_PATH"] = joinpath(ROOT, "etc", "fonts") - ENV["TK_LIBRARY"] = "/System/Library/Frameworks/Tk.framework/Versions/8.5/Resources/Scripts" -end diff --git a/contrib/mac/mac-gtk.sh b/contrib/mac/mac-gtk.sh deleted file mode 100644 index 86be8aa8e1670..0000000000000 --- a/contrib/mac/mac-gtk.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# This script will attempt to download and build GTK+-3, -# including dependencies, in ~/gtk (also puts stuff in -# ~/.local, ~/Source, ~/.jhbuildrc*) -# While this should work, it may be preferable to execute -# each line separately in the terminal - -curl -O https://raw.github.com/jralls/gtk-osx-build/master/gtk-osx-build-setup.sh -sh gtk-osx-build-setup.sh -export PATH=$PATH:~/.local/bin/ -sed -i -e 's/^setup_sdk/#setup_sdk/g' ~/.jhbuildrc-custom -cat << EOF >> .jhbuildrc-custom -setup_sdk(target=_target, sdk_version=_target, architectures=[_default_arch]) -os.environ["DYLD_LIBRARY_PATH"] = "" -build_policy = "updated-deps" -modules = [ "meta-gtk-osx-bootstrap", - "freetype", "fontconfig", - "meta-gtk-osx-core", - "meta-gtk-osx-themes", - "gtk-quartz-engine" ] -EOF - -jhbuild bootstrap --skip=libiconv --ignore-system -jhbuild build - -cd ~/gtk/source -curl -O http://ftp.gnome.org/pub/gnome/sources/gtk-mac-bundler/0.6/gtk-mac-bundler-0.6.1.tar.bz2 -tar jxvf gtk-mac-bundler-0.6.1.tar.bz2 -cd gtk-mac-bundler-0.6.1 -make install -cd ~/gtk - - From 2e74ac16ec329589e50f03d339868c80ba77fcc8 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Thu, 21 May 2020 10:34:33 -0400 Subject: [PATCH 011/232] Update contrib/README to only refer to files still in here. --- contrib/README.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/contrib/README.md b/contrib/README.md index 574cf24415ee7..d1b2485dabe55 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -1,8 +1,10 @@ Installation -============================= +============ | Name | Description | | ---------------------------- | --------------------------------------------------------- | +|[ mac/ ](https://github.com/JuliaLang/julia/blob/master/contrib/mac/) | Mac install files | +|[ windows/ ](https://github.com/JuliaLang/julia/blob/master/contrib/windows/) | Windows install files | |[ add_license_to_files.jl ](https://github.com/JuliaLang/julia/blob/master/contrib/add_license_to_files.jl ) | Add the Julia license to files in the Julia Project | |[ check-whitespace.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/check-whitespace.sh) | Check for trailing white space | |[ commit-name.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/commit-name.sh) | Computes a version name for a commit | @@ -13,20 +15,12 @@ Installation |[ julia.appdata.xml ](https://github.com/JuliaLang/julia/blob/master/contrib/julia.appdata.xml) | Appdata config file | |[ julia-config.jl ](https://github.com/JuliaLang/julia/blob/master/contrib/julia-config.jl) | Determines build parameters required by an embedded Julia | |[ julia.desktop ](https://github.com/JuliaLang/julia/blob/master/contrib/julia.desktop) | GNOME desktop config file | -|[ mac/ ](https://github.com/JuliaLang/julia/blob/master/contrib/mac/) | Mac install files | |[ relative_path.py ](https://github.com/JuliaLang/julia/blob/master/contrib/relative_path.py) | Convert absolute paths into relative paths | -|[ repackage_system_suitesparse4.make ](https://github.com/JuliaLang/julia/blob/master/contrib/repackage_system_suitesparse4.make) | Links shared libraries from static-library for suitesparse4 | |[ stringreplace.c ](https://github.com/JuliaLang/julia/blob/master/contrib/stringreplace.c) | Replace strings to hardcoded paths in binaries during `make install` | |[ travis_fastfail.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/travis_fastfail.sh ) | Checks for queued build tests in Travis | -|[ windows/ ](https://github.com/JuliaLang/julia/blob/master/contrib/windows/) | Windows install files | - -Editors + Debuggers -============================= - -[JuliaEditorSupport](https://github.com/JuliaEditorSupport) GitHub organization hosts julia extensions for various text editors and IDEs. - -If your favorite IDE, is not listed there, [open an issue in the JuliaEditorSupport/roadmap](https://github.com/JuliaEditorSupport/roadmap/issues) repository. +Debugging +========= | Name | Description | | ------------------------------ | ----------------------------------------------------------- | From 6beec65e0ae765a1ce1afeb4bf5b47e34af3e659 Mon Sep 17 00:00:00 2001 From: Mustafa M Date: Thu, 21 May 2020 10:38:09 -0400 Subject: [PATCH 012/232] Add a recommended terminal for Windows to README (#35960) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b106c682a1bda..608863b8904df 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ The Julia REPL is quite powerful. See the section in the manual on [the Julia REPL](https://docs.julialang.org/en/v1/stdlib/REPL/) for more details. +On Windows we highly recommend running Julia using a modern terminal, +such as installing the [Windows Terminal from the Microsoft Store](https://aka.ms/terminal). + Support for editing Julia is available for many [widely used editors](https://github.com/JuliaEditorSupport): [Emacs](https://github.com/JuliaEditorSupport/julia-emacs), From bde346ff22eb412f2e1ec644bceb6ed762d8a0d0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 21 May 2020 16:49:39 -0400 Subject: [PATCH 013/232] gc: fix MEMDEBUG build (#35622) Sink the assume to the right point, to ensure the compiler doesn't use this information to mis-optimize the code in MEMDEBUG mode. --- src/gc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gc.c b/src/gc.c index 243b05aab6502..a3b0322913834 100644 --- a/src/gc.c +++ b/src/gc.c @@ -725,6 +725,7 @@ STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o, #ifdef MEMDEBUG gc_setmark_big(ptls, o, mark_mode); #else + jl_assume(page); if (mark_mode == GC_OLD_MARKED) { ptls->gc_cache.perm_scanned_bytes += page->osize; jl_atomic_fetch_add_relaxed(&page->nold, 1); @@ -748,7 +749,7 @@ STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o, STATIC_INLINE void gc_setmark_pool(jl_ptls_t ptls, jl_taggedvalue_t *o, uint8_t mark_mode) JL_NOTSAFEPOINT { - gc_setmark_pool_(ptls, o, mark_mode, jl_assume(page_metadata(o))); + gc_setmark_pool_(ptls, o, mark_mode, page_metadata(o)); } STATIC_INLINE void gc_setmark(jl_ptls_t ptls, jl_taggedvalue_t *o, From 853fe04e303621a7d11db980905ab26979b8b1fc Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 21 May 2020 17:00:12 -0400 Subject: [PATCH 014/232] fix some 1.5 compatibility issues (#35965) - restore some `copyto!` methods that Knet assumed existed - allow `findfirst` to work on InfiniteArrays again - allow constructing a LineNumberNode from a String --- base/array.jl | 14 +++++++++++++- base/boot.jl | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/base/array.jl b/base/array.jl index 7ec05e443ee88..d52997bf7af53 100644 --- a/base/array.jl +++ b/base/array.jl @@ -320,6 +320,15 @@ Copy `N` elements from collection `src` starting at offset `so`, to array `dest` offset `do`. Return `dest`. """ function copyto!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) + return _copyto_impl!(dest, doffs, src, soffs, n) +end + +# this is only needed to avoid possible ambiguities with methods added in some packages +function copyto!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T + return _copyto_impl!(dest, doffs, src, soffs, n) +end + +function _copyto_impl!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) n == 0 && return dest n > 0 || _throw_argerror() if soffs < 1 || doffs < 1 || soffs+n-1 > length(src) || doffs+n-1 > length(dest) @@ -339,6 +348,9 @@ end copyto!(dest::Array, src::Array) = copyto!(dest, 1, src, 1, length(src)) +# also to avoid ambiguities in packages +copyto!(dest::Array{T}, src::Array{T}) where {T} = copyto!(dest, 1, src, 1, length(src)) + # N.B: The generic definition in multidimensional.jl covers, this, this is just here # for bootstrapping purposes. function fill!(dest::Array{T}, x) where T @@ -1762,8 +1774,8 @@ CartesianIndex(1, 1) ``` """ function findnext(testf::Function, A, start) + i = oftype(first(keys(A)), start) l = last(keys(A)) - i = oftype(l, start) i > l && return nothing while true testf(A[i]) && return i diff --git a/base/boot.jl b/base/boot.jl index 17a777a7a26e6..034ca642c459a 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -370,6 +370,7 @@ _new(:QuoteNode, :Any) _new(:SSAValue, :Int) eval(Core, :(LineNumberNode(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) eval(Core, :(LineNumberNode(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) +LineNumberNode(l::Int, f::String) = LineNumberNode(l, Symbol(f)) eval(Core, :(GlobalRef(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) eval(Core, :(SlotNumber(n::Int) = $(Expr(:new, :SlotNumber, :n)))) eval(Core, :(TypedSlot(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) From fab520758e2465d939c93497e9489e1f104cc66d Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 21 May 2020 17:35:32 -0400 Subject: [PATCH 015/232] faster filesize and read(::IOStream) (#35925) --- base/filesystem.jl | 2 +- base/iostream.jl | 51 +++++++++++++++++++++++++++++++--------------- src/support/ios.c | 28 +++++++++++++++++++++++++ src/support/ios.h | 3 +++ 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/base/filesystem.jl b/base/filesystem.jl index 29b77a49c6403..e4f537ac88f53 100644 --- a/base/filesystem.jl +++ b/base/filesystem.jl @@ -42,7 +42,7 @@ import .Base: IOError, _UVError, _sizeof_uv_fs, check_open, close, eof, eventloop, fd, isopen, bytesavailable, position, read, read!, readavailable, seek, seekend, show, skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error, - rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror + rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize import .Base.RefValue diff --git a/base/iostream.jl b/base/iostream.jl index 448721eb3ff8f..48238ba191503 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -219,6 +219,12 @@ function position(s::IOStream) return pos end +function filesize(s::IOStream) + sz = @_lock_ios s ccall(:ios_filesize, Int64, (Ptr{Cvoid},), s.ios) + systemerror("filesize", sz == -1) + return sz +end + _eof_nolock(s::IOStream) = ccall(:ios_eof_blocking, Cint, (Ptr{Cvoid},), s.ios) != 0 eof(s::IOStream) = @_lock_ios s _eof_nolock(s) @@ -441,9 +447,10 @@ function readbytes_all!(s::IOStream, b::Array{UInt8}, nb) lb = max(65536, (nr+1) * 2) resize!(b, lb) end - nr += Int(ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), - s.ios, pointer(b, nr+1), min(lb-nr, nb-nr))) - _eof_nolock(s) && break + thisr = Int(ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + s.ios, pointer(b, nr+1), min(lb-nr, nb-nr))) + nr += thisr + (nr == nb || thisr == 0 || _eof_nolock(s)) && break end end if lb > olb && lb > nr @@ -486,21 +493,33 @@ function readbytes!(s::IOStream, b::Array{UInt8}, nb=length(b); all::Bool=true) end function read(s::IOStream) - sz = try # filesize is just a hint, so ignore if `fstat` fails - filesize(s) - catch ex - ex isa IOError || rethrow() - Int64(0) - end - if sz > 0 - pos = position(s) - if pos > 0 - sz -= pos + # First we try to fill the buffer. If that gives us the whole file, + # copy it out and return. Otherwise look at the file size and use it + # to prealloate space. Determining the size requires extra syscalls, + # which we want to avoid for small files. + @_lock_ios s begin + nb = ccall(:ios_fillbuf, Cssize_t, (Ptr{Cvoid},), s.ios) + if nb != -1 + b = StringVector(nb) + readbytes_all!(s, b, nb) + else + sz = try # filesize is just a hint, so ignore if it fails + filesize(s) + catch ex + ex isa IOError || rethrow() + Int64(-1) + end + if sz > 0 + pos = position(s) + if pos > 0 + sz -= pos + end + end + b = StringVector(sz < 0 ? 1024 : sz) + nr = readbytes_all!(s, b, sz < 0 ? typemax(Int) : sz) + resize!(b, nr) end end - b = StringVector(sz <= 0 ? 1024 : sz) - nr = readbytes_all!(s, b, typemax(Int)) - resize!(b, nr) return b end diff --git a/src/support/ios.c b/src/support/ios.c index e201e641c6953..cdeb44e916c81 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -367,6 +367,17 @@ size_t ios_readprep(ios_t *s, size_t n) return (size_t)(s->size - s->bpos); } +// attempt to fill the buffer. returns the number of bytes available if we +// have read the whole file, or -1 if there might be more data. +ssize_t ios_fillbuf(ios_t *s) +{ + size_t nb = s->maxsize - s->bpos; + size_t got = ios_readprep(s, nb); + if (got < nb) + return (ssize_t)got; + return -1; +} + static void _write_update_pos(ios_t *s) { if (s->bpos > s->ndirty) s->ndirty = s->bpos; @@ -535,6 +546,22 @@ int64_t ios_pos(ios_t *s) return fdpos; } +int64_t ios_filesize(ios_t *s) +{ + if (s->fd == -1) + return -1; + int64_t fdpos = s->fpos; + if (fdpos == (int64_t)-1) { + fdpos = lseek(s->fd, 0, SEEK_CUR); + if (fdpos == (int64_t)-1) + return fdpos; + s->fpos = fdpos; + } + off_t sz = lseek(s->fd, 0, SEEK_END); + lseek(s->fd, (off_t)fdpos, SEEK_SET); + return sz; +} + int ios_trunc(ios_t *s, size_t size) { if (s->bm == bm_mem) { @@ -936,6 +963,7 @@ ios_t *ios_file(ios_t *s, const char *fname, int rd, int wr, int create, int tru goto open_file_err; s = ios_fd(s, fd, 1, 1); + s->fpos = 0; if (!rd) s->readable = 0; if (!wr) diff --git a/src/support/ios.h b/src/support/ios.h index fd39514695be2..3ba5ab4884284 100644 --- a/src/support/ios.h +++ b/src/support/ios.h @@ -89,6 +89,7 @@ JL_DLLEXPORT int64_t ios_seek(ios_t *s, int64_t pos) JL_NOTSAFEPOINT; // absolut JL_DLLEXPORT int64_t ios_seek_end(ios_t *s) JL_NOTSAFEPOINT; JL_DLLEXPORT int64_t ios_skip(ios_t *s, int64_t offs); // relative seek JL_DLLEXPORT int64_t ios_pos(ios_t *s) JL_NOTSAFEPOINT; // get current position +JL_DLLEXPORT int64_t ios_filesize(ios_t *s); JL_DLLEXPORT int ios_trunc(ios_t *s, size_t size) JL_NOTSAFEPOINT; JL_DLLEXPORT int ios_eof(ios_t *s); JL_DLLEXPORT int ios_eof_blocking(ios_t *s); @@ -108,6 +109,8 @@ JL_DLLEXPORT size_t ios_copyuntil(ios_t *to, ios_t *from, char delim) JL_NOTSAFE JL_DLLEXPORT size_t ios_nchomp(ios_t *from, size_t ntowrite); // ensure at least n bytes are buffered if possible. returns # available. JL_DLLEXPORT size_t ios_readprep(ios_t *from, size_t n); +// fill the buffer and determine whether it contains the whole rest of the file +JL_DLLEXPORT ssize_t ios_fillbuf(ios_t *s); /* stream creation */ JL_DLLEXPORT From dd1e54d54dc4b6c342602c9169de268c5e37ac2a Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 21 May 2020 17:36:12 -0400 Subject: [PATCH 016/232] fix #35937, serializing TaskFailedException in Distributed (#35943) --- stdlib/Distributed/src/clusterserialize.jl | 39 ++++++++++++++++++++- stdlib/Distributed/test/distributed_exec.jl | 13 +++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/stdlib/Distributed/src/clusterserialize.jl b/stdlib/Distributed/src/clusterserialize.jl index 6bca816687af3..c50393caeb0f5 100644 --- a/stdlib/Distributed/src/clusterserialize.jl +++ b/stdlib/Distributed/src/clusterserialize.jl @@ -2,7 +2,7 @@ using Serialization: serialize_cycle, deserialize_cycle, writetag, serialize_typename, deserialize_typename, - TYPENAME_TAG, reset_state, serialize_type + TYPENAME_TAG, TASK_TAG, reset_state, serialize_type using Serialization.__deserialized_types__ import Serialization: object_number, lookup_object_number, remember_object @@ -102,6 +102,26 @@ function serialize(s::ClusterSerializer, t::Core.TypeName) nothing end +function serialize(s::ClusterSerializer, t::Task) + serialize_cycle(s, t) && return + if istaskstarted(t) && !istaskdone(t) + error("cannot serialize a running Task") + end + writetag(s.io, TASK_TAG) + serialize(s, t.code) + serialize(s, t.storage) + bt = t.backtrace + if bt !== nothing + if !isa(bt, Vector{Any}) + bt = Base.process_backtrace(bt, 100) + end + serialize(s, bt) + end + serialize(s, t.state) + serialize(s, t.result) + serialize(s, t.exception) +end + function serialize(s::ClusterSerializer, g::GlobalRef) # Record if required and then invoke the default GlobalRef serializer. sym = g.name @@ -231,6 +251,23 @@ function deserialize(s::ClusterSerializer, t::Type{<:CapturedException}) return CapturedException(capex, bt) end +function deserialize(s::ClusterSerializer, ::Type{Task}) + t = Task(nothing) + deserialize_cycle(s, t) + t.code = deserialize(s) + t.storage = deserialize(s) + state_or_bt = deserialize(s) + if state_or_bt isa Symbol + t.state = state_or_bt + else + t.backtrace = state_or_bt + t.state = deserialize(s) + end + t.result = deserialize(s) + t.exception = deserialize(s) + t +end + """ clear!(syms, pids=workers(); mod=Main) diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 379a2b5b46fb5..6bc3cef045fd6 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -1682,6 +1682,19 @@ let (h, t) = Distributed.head_and_tail(Int[], 0) @test collect(t) == [] end +# issue #35937 +let e + try + pmap(1) do _ + wait(@async error(42)) + end + catch ex + e = ex + end + # check that the inner TaskFailedException is correctly formed & can be printed + @test sprint(showerror, e) isa String +end + include("splitrange.jl") # Run topology tests last after removing all workers, since a given From 97a62584750c4f8d23ad57305a65721074f7038e Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 22 May 2020 01:27:31 -0400 Subject: [PATCH 017/232] fix error thrown by `filesize(::IOStream)` (#35984) --- base/iostream.jl | 5 ++++- src/support/ios.c | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base/iostream.jl b/base/iostream.jl index 48238ba191503..f7cec7a329a57 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -221,7 +221,10 @@ end function filesize(s::IOStream) sz = @_lock_ios s ccall(:ios_filesize, Int64, (Ptr{Cvoid},), s.ios) - systemerror("filesize", sz == -1) + if sz == -1 + err = Libc.errno() + throw(IOError(string("filesize: ", Libc.strerror(err), " for ", s.name), err)) + end return sz end diff --git a/src/support/ios.c b/src/support/ios.c index cdeb44e916c81..4ab093ff40e78 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -548,8 +548,6 @@ int64_t ios_pos(ios_t *s) int64_t ios_filesize(ios_t *s) { - if (s->fd == -1) - return -1; int64_t fdpos = s->fpos; if (fdpos == (int64_t)-1) { fdpos = lseek(s->fd, 0, SEEK_CUR); From a5e032e29686fabc8b88a10726c79feb18900055 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Fri, 22 May 2020 18:45:51 +0200 Subject: [PATCH 018/232] Display sparse matrices by showing their structure with braille patterns (#33821) * Display sparse matrices by showing their structure with braille patterns * Adopt test which involves showing of sparse matrix * Adopt docstrings * Call `rowvals(S)` only once * Extend code documentation * Simplify calculation of `scaleHeight` and `scaleWidth` * Make `brailleBlocks` a `const` * Initialize `brailleGrid` with `UInt16(10240)` * Let the compiler do the conversion of `\n` * Improve printing to `io` * Unwrap the helper functions directly into the loop * Show small sparse matrix in traditional manner * Add tests for `isstored` * Adapt tests for `show` * Adapt doctests * Use `m` and `n` instead of `size` calls * Initialize `scaleHeight` and `scaleWidth` in `else` clause * Reenable commented test * Declare type of `maxHeight` and `maxWidth` * Do not print leading newline in `_show_with_braille_patterns` * Reenable test for issue 30589 * Shorten type declarations of `maxHeight` and `maxWidth` * Add `isstored` to Base * Add `isstored` for sparse vectors * Make use of Base function `isstored` * Add `boundscheck` macro * Clarify news * Call `issorted` by `Base.issored` * Use `Base.isstored` in tests * Set cutoff value to print sparse matrices with braille to 16 Co-authored-by: Steven G. Johnson --- NEWS.md | 1 + base/abstractarray.jl | 5 + stdlib/LinearAlgebra/src/svd.jl | 8 +- stdlib/SparseArrays/docs/src/index.md | 28 ++- stdlib/SparseArrays/src/abstractsparse.jl | 7 +- stdlib/SparseArrays/src/sparsematrix.jl | 262 ++++++++++++---------- stdlib/SparseArrays/src/sparsevector.jl | 5 + stdlib/SparseArrays/test/sparse.jl | 126 +++++++---- stdlib/SparseArrays/test/sparsevector.jl | 12 + stdlib/SuiteSparse/src/cholmod.jl | 6 +- stdlib/SuiteSparse/src/spqr.jl | 28 +-- test/show.jl | 8 +- 12 files changed, 295 insertions(+), 201 deletions(-) diff --git a/NEWS.md b/NEWS.md index e69006f869186..f8a7faae3f248 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,6 +60,7 @@ Standard library changes #### SparseArrays +* Display large sparse matrices with a Unicode "spy" plot of their nonzero patterns, and display small sparse matrices by an `Matrix`-like 2d layout of their contents. #### Dates diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 4e1c3e2d86dec..918c6c0a88ff8 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -412,6 +412,11 @@ function isassigned(a::AbstractArray, i::Integer...) end end +function isstored(A::AbstractArray{<:Any,N}, I::Vararg{Integer,N}) where {N} + @boundscheck checkbounds(A, I...) + return true +end + # used to compute "end" for last index function trailingsize(A, n) s = 1 diff --git a/stdlib/LinearAlgebra/src/svd.jl b/stdlib/LinearAlgebra/src/svd.jl index 61e12a1a5945f..9eb153d916e66 100644 --- a/stdlib/LinearAlgebra/src/svd.jl +++ b/stdlib/LinearAlgebra/src/svd.jl @@ -303,12 +303,12 @@ Q factor: 0.0 1.0 D1 factor: 2×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 2 stored entries: - [1, 1] = 0.707107 - [2, 2] = 0.707107 + 0.707107 ⋅ + ⋅ 0.707107 D2 factor: 2×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 2 stored entries: - [1, 1] = 0.707107 - [2, 2] = 0.707107 + 0.707107 ⋅ + ⋅ 0.707107 R0 factor: 2×2 Array{Float64,2}: 1.41421 0.0 diff --git a/stdlib/SparseArrays/docs/src/index.md b/stdlib/SparseArrays/docs/src/index.md index 17fb2b2dc368b..5984720a6bca2 100644 --- a/stdlib/SparseArrays/docs/src/index.md +++ b/stdlib/SparseArrays/docs/src/index.md @@ -52,15 +52,15 @@ remove stored zeros from the sparse matrix. ```jldoctest julia> A = sparse([1, 1, 2, 3], [1, 3, 2, 3], [0, 1, 2, 0]) 3×3 SparseMatrixCSC{Int64,Int64} with 4 stored entries: - [1, 1] = 0 - [2, 2] = 2 - [1, 3] = 1 - [3, 3] = 0 + 0 ⋅ 1 + ⋅ 2 ⋅ + ⋅ ⋅ 0 julia> dropzeros(A) 3×3 SparseMatrixCSC{Int64,Int64} with 2 stored entries: - [2, 2] = 2 - [1, 3] = 1 + ⋅ ⋅ 1 + ⋅ 2 ⋅ + ⋅ ⋅ ⋅ ``` ## Sparse Vector Storage @@ -106,10 +106,8 @@ julia> I = [1, 4, 3, 5]; J = [4, 7, 18, 9]; V = [1, 2, -5, 3]; julia> S = sparse(I,J,V) 5×18 SparseMatrixCSC{Int64,Int64} with 4 stored entries: - [1, 4] = 1 - [4, 7] = 2 - [5, 9] = 3 - [3, 18] = -5 +⠀⠈⠀⡀⠀⠀⠀⠀⠠ +⠀⠀⠀⠀⠁⠀⠀⠀⠀ julia> R = sparsevec(I,V) 5-element SparseVector{Int64,Int64} with 4 stored entries: @@ -152,11 +150,11 @@ the [`sparse`](@ref) function: ```jldoctest julia> sparse(Matrix(1.0I, 5, 5)) 5×5 SparseMatrixCSC{Float64,Int64} with 5 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 - [4, 4] = 1.0 - [5, 5] = 1.0 + 1.0 ⋅ ⋅ ⋅ ⋅ + ⋅ 1.0 ⋅ ⋅ ⋅ + ⋅ ⋅ 1.0 ⋅ ⋅ + ⋅ ⋅ ⋅ 1.0 ⋅ + ⋅ ⋅ ⋅ ⋅ 1.0 julia> sparse([1.0, 0.0, 1.0]) 3-element SparseVector{Float64,Int64} with 2 stored entries: diff --git a/stdlib/SparseArrays/src/abstractsparse.jl b/stdlib/SparseArrays/src/abstractsparse.jl index 92724fccfe58f..9cc33cdbc6e29 100644 --- a/stdlib/SparseArrays/src/abstractsparse.jl +++ b/stdlib/SparseArrays/src/abstractsparse.jl @@ -112,10 +112,9 @@ Return a tuple `(I, J, V)` where `I` and `J` are the row and column indices of t ```jldoctest julia> A = sparse([1 2 0; 0 0 3; 0 4 0]) 3×3 SparseMatrixCSC{Int64,Int64} with 4 stored entries: - [1, 1] = 1 - [1, 2] = 2 - [3, 2] = 4 - [2, 3] = 3 + 1 2 ⋅ + ⋅ ⋅ 3 + ⋅ 4 ⋅ julia> findnz(A) ([1, 1, 3, 2], [1, 2, 2, 3], [1, 2, 4, 3]) diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 411f3fddc0a2a..ba8d2fba823a5 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -100,9 +100,9 @@ Returns the number of stored (filled) elements in a sparse array. ```jldoctest julia> A = sparse(2I, 3, 3) 3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: - [1, 1] = 2 - [2, 2] = 2 - [3, 3] = 2 + 2 ⋅ ⋅ + ⋅ 2 ⋅ + ⋅ ⋅ 2 julia> nnz(A) 3 @@ -125,9 +125,9 @@ modifications to the returned vector will mutate `A` as well. See ```jldoctest julia> A = sparse(2I, 3, 3) 3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: - [1, 1] = 2 - [2, 2] = 2 - [3, 3] = 2 + 2 ⋅ ⋅ + ⋅ 2 ⋅ + ⋅ ⋅ 2 julia> nonzeros(A) 3-element Array{Int64,1}: @@ -151,9 +151,9 @@ nonzero values. See also [`nonzeros`](@ref) and [`nzrange`](@ref). ```jldoctest julia> A = sparse(2I, 3, 3) 3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: - [1, 1] = 2 - [2, 2] = 2 - [3, 3] = 2 + 2 ⋅ ⋅ + ⋅ 2 ⋅ + ⋅ ⋅ 2 julia> rowvals(A) 3-element Array{Int64,1}: @@ -187,63 +187,102 @@ column. In conjunction with [`nonzeros`](@ref) and nzrange(S::AbstractSparseMatrixCSC, col::Integer) = getcolptr(S)[col]:(getcolptr(S)[col+1]-1) nzrange(S::SparseMatrixCSCView, col::Integer) = nzrange(S.parent, S.indices[2][col]) +function Base.isstored(A::AbstractSparseMatrixCSC, i::Integer, j::Integer) + @boundscheck checkbounds(A, i, j) + rows = rowvals(A) + for istored in nzrange(A, j) # could do binary search if the row indices are sorted? + i == rows[istored] && return true + end + return false +end + +Base.replace_in_print_matrix(A::AbstractSparseMatrix, i::Integer, j::Integer, s::AbstractString) = + Base.isstored(A, i, j) ? s : Base.replace_with_centered_mark(s) + function Base.show(io::IO, ::MIME"text/plain", S::AbstractSparseMatrixCSC) xnnz = nnz(S) m, n = size(S) print(io, m, "×", n, " ", typeof(S), " with ", xnnz, " stored ", xnnz == 1 ? "entry" : "entries") - if xnnz != 0 + if !(m == 0 || n == 0) print(io, ":") show(IOContext(io, :typeinfo => eltype(S)), S) end end Base.show(io::IO, S::AbstractSparseMatrixCSC) = Base.show(convert(IOContext, io), S::AbstractSparseMatrixCSC) -function Base.show(io::IOContext, S::AbstractSparseMatrixCSC) - nnz(S) == 0 && return show(io, MIME("text/plain"), S) - ioc = IOContext(io, :compact => true) - function _format_line(r, col, padr, padc) - print(ioc, "\n [", rpad(rowvals(S)[r], padr), ", ", lpad(col, padc), "] = ") - if isassigned(nonzeros(S), Int(r)) - show(ioc, nonzeros(S)[r]) - else - print(ioc, Base.undef_ref_str) - end +const brailleBlocks = UInt16['⠁', '⠂', '⠄', '⡀', '⠈', '⠐', '⠠', '⢀'] +function _show_with_braille_patterns(io::IOContext, S::AbstractSparseMatrixCSC) + m, n = size(S) + (m == 0 || n == 0) && return show(io, MIME("text/plain"), S) + + # The maximal number of characters we allow to display the matrix + local maxHeight::Int, maxWidth::Int + maxHeight = displaysize(io)[1] - 4 # -4 from [Prompt, header, newline after elements, new prompt] + maxWidth = displaysize(io)[2] ÷ 2 + + # In the process of generating the braille pattern to display the nonzero + # structure of `S`, we need to be able to scale the matrix `S` to a + # smaller matrix with the same aspect ratio as `S`, but fits on the + # available screen space. The size of that smaller matrix is stored + # in the variables `scaleHeight` and `scaleWidth`. If no scaling is needed, + # we can use the size `m × n` of `S` directly. + # We determine if scaling is needed and set the scaling factors + # `scaleHeight` and `scaleWidth` accordingly. Note that each available + # character can contain up to 4 braille dots in its height (⡇) and up to + # 2 braille dots in its width (⠉). + if get(io, :limit, true) && (m > 4maxHeight || n > 2maxWidth) + s = min(2maxWidth / n, 4maxHeight / m) + scaleHeight = floor(Int, s * m) + scaleWidth = floor(Int, s * n) + else + scaleHeight = m + scaleWidth = n end - function _get_cols(from, to) - idx = eltype(getcolptr(S))[] - c = searchsortedlast(getcolptr(S), from) - for i = from:to - while i == getcolptr(S)[c+1] - c +=1 - end - push!(idx, c) + # `brailleGrid` is used to store the needed braille characters for + # the matrix `S`. Each row of the braille pattern to print is stored + # in a column of `brailleGrid`. + brailleGrid = fill(UInt16(10240), (scaleWidth - 1) ÷ 2 + 2, (scaleHeight - 1) ÷ 4 + 1) + brailleGrid[end, :] .= '\n' + + rvals = rowvals(S) + rowscale = max(1, scaleHeight - 1) / max(1, m - 1) + colscale = max(1, scaleWidth - 1) / max(1, n - 1) + @inbounds for j = 1:n + # Scale the column index `j` to the best matching column index + # of a matrix of size `scaleHeight × scaleWidth` + sj = round(Int, (j - 1) * colscale + 1) + for x in nzrange(S, j) + # Scale the row index `i` to the best matching row index + # of a matrix of size `scaleHeight × scaleWidth` + si = round(Int, (rvals[x] - 1) * rowscale + 1) + + # Given the index pair `(si, sj)` of the scaled matrix, + # calculate the corresponding triple `(k, l, p)` such that the + # element at `(si, sj)` can be found at position `(k, l)` in the + # braille grid `brailleGrid` and corresponds to the 1-dot braille + # character `brailleBlocks[p]` + k = (sj - 1) ÷ 2 + 1 + l = (si - 1) ÷ 4 + 1 + p = ((sj - 1) % 2) * 4 + ((si - 1) % 4 + 1) + + brailleGrid[k, l] |= brailleBlocks[p] end - idx end + foreach(c -> print(io, Char(c)), @view brailleGrid[1:end-1]) +end - rows = displaysize(io)[1] - 4 # -4 from [Prompt, header, newline after elements, new prompt] - if !get(io, :limit, false) || rows >= nnz(S) # Will the whole matrix fit when printed? - cols = _get_cols(1, nnz(S)) - padr, padc = ndigits.((maximum(rowvals(S)[1:nnz(S)]), cols[end])) - _format_line.(1:nnz(S), cols, padr, padc) - else - if rows <= 2 - print(io, "\n \u22ee") - return - end - s1, e1 = 1, div(rows - 1, 2) # -1 accounts for \vdots - s2, e2 = nnz(S) - (rows - 1 - e1) + 1, nnz(S) - cols1, cols2 = _get_cols(s1, e1), _get_cols(s2, e2) - padr = ndigits(max(maximum(rowvals(S)[s1:e1]), maximum(rowvals(S)[s2:e2]))) - padc = ndigits(cols2[end]) - _format_line.(s1:e1, cols1, padr, padc) - print(io, "\n \u22ee") - _format_line.(s2:e2, cols2, padr, padc) +function Base.show(io::IOContext, S::AbstractSparseMatrixCSC) + if max(size(S)...) < 16 && !get(io, :compact, false) + ioc = IOContext(io, :compact => true) + println(ioc) + Base.print_matrix(ioc, S) + return end - return + println(io) + _show_with_braille_patterns(io, S) end ## Reshape @@ -620,9 +659,9 @@ julia> A = Matrix(1.0I, 3, 3) julia> sparse(A) 3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 + 1.0 ⋅ ⋅ + ⋅ 1.0 ⋅ + ⋅ ⋅ 1.0 ``` """ sparse(A::AbstractMatrix{Tv}) where {Tv} = convert(SparseMatrixCSC{Tv,Int}, A) @@ -660,9 +699,9 @@ julia> Vs = [1; 2; 3]; julia> sparse(Is, Js, Vs) 3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: - [1, 1] = 1 - [2, 2] = 2 - [3, 3] = 3 + 1 ⋅ ⋅ + ⋅ 2 ⋅ + ⋅ ⋅ 3 ``` """ function sparse(I::AbstractVector{Ti}, J::AbstractVector{Ti}, V::AbstractVector{Tv}, m::Integer, n::Integer, combine) where {Tv,Ti<:Integer} @@ -1268,33 +1307,24 @@ For expert drivers and additional information, see [`permute!`](@ref). ```jldoctest julia> A = spdiagm(0 => [1, 2, 3, 4], 1 => [5, 6, 7]) 4×4 SparseMatrixCSC{Int64,Int64} with 7 stored entries: - [1, 1] = 1 - [1, 2] = 5 - [2, 2] = 2 - [2, 3] = 6 - [3, 3] = 3 - [3, 4] = 7 - [4, 4] = 4 + 1 5 ⋅ ⋅ + ⋅ 2 6 ⋅ + ⋅ ⋅ 3 7 + ⋅ ⋅ ⋅ 4 julia> permute(A, [4, 3, 2, 1], [1, 2, 3, 4]) 4×4 SparseMatrixCSC{Int64,Int64} with 7 stored entries: - [4, 1] = 1 - [3, 2] = 2 - [4, 2] = 5 - [2, 3] = 3 - [3, 3] = 6 - [1, 4] = 4 - [2, 4] = 7 + ⋅ ⋅ ⋅ 4 + ⋅ ⋅ 3 7 + ⋅ 2 6 ⋅ + 1 5 ⋅ ⋅ julia> permute(A, [1, 2, 3, 4], [4, 3, 2, 1]) 4×4 SparseMatrixCSC{Int64,Int64} with 7 stored entries: - [3, 1] = 7 - [4, 1] = 4 - [2, 2] = 6 - [3, 2] = 3 - [1, 3] = 5 - [2, 3] = 2 - [1, 4] = 1 + ⋅ ⋅ 5 1 + ⋅ 6 2 ⋅ + 7 3 ⋅ ⋅ + 4 ⋅ ⋅ ⋅ ``` """ function permute(A::AbstractSparseMatrixCSC{Tv,Ti}, p::AbstractVector{<:Integer}, @@ -1330,15 +1360,17 @@ and no space beyond that passed in. ```jldoctest julia> A = sparse(Diagonal([1, 2, 3, 4])) 4×4 SparseMatrixCSC{Int64,Int64} with 4 stored entries: - [1, 1] = 1 - [2, 2] = 2 - [3, 3] = 3 - [4, 4] = 4 + 1 ⋅ ⋅ ⋅ + ⋅ 2 ⋅ ⋅ + ⋅ ⋅ 3 ⋅ + ⋅ ⋅ ⋅ 4 julia> SparseArrays.fkeep!(A, (i, j, v) -> isodd(v)) 4×4 SparseMatrixCSC{Int64,Int64} with 2 stored entries: - [1, 1] = 1 - [3, 3] = 3 + 1 ⋅ ⋅ ⋅ + ⋅ ⋅ ⋅ ⋅ + ⋅ ⋅ 3 ⋅ + ⋅ ⋅ ⋅ ⋅ ``` """ function fkeep!(A::AbstractSparseMatrixCSC, f, trim::Bool = true) @@ -1409,14 +1441,15 @@ For an in-place version and algorithmic information, see [`dropzeros!`](@ref). ```jldoctest julia> A = sparse([1, 2, 3], [1, 2, 3], [1.0, 0.0, 1.0]) 3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 2] = 0.0 - [3, 3] = 1.0 + 1.0 ⋅ ⋅ + ⋅ 0.0 ⋅ + ⋅ ⋅ 1.0 julia> dropzeros(A) 3×3 SparseMatrixCSC{Float64,Int64} with 2 stored entries: - [1, 1] = 1.0 - [3, 3] = 1.0 + 1.0 ⋅ ⋅ + ⋅ ⋅ ⋅ + ⋅ ⋅ 1.0 ``` """ dropzeros(A::AbstractSparseMatrixCSC) = dropzeros!(copy(A)) @@ -1528,7 +1561,8 @@ argument specifies a random number generator, see [Random Numbers](@ref). ```jldoctest; setup = :(using Random; Random.seed!(1234)) julia> sprand(Bool, 2, 2, 0.5) 2×2 SparseMatrixCSC{Bool,Int64} with 1 stored entry: - [2, 2] = 1 + ⋅ ⋅ + ⋅ 1 julia> sprand(Float64, 3, 0.75) 3-element SparseVector{Float64,Int64} with 1 stored entry: @@ -1574,8 +1608,8 @@ argument specifies a random number generator, see [Random Numbers](@ref). ```jldoctest; setup = :(using Random; Random.seed!(0)) julia> sprandn(2, 2, 0.75) 2×2 SparseMatrixCSC{Float64,Int64} with 2 stored entries: - [1, 2] = 0.586617 - [2, 2] = 0.297336 + ⋅ 0.586617 + ⋅ 0.297336 ``` """ sprandn(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) = @@ -1600,7 +1634,10 @@ specified. # Examples ```jldoctest julia> spzeros(3, 3) -3×3 SparseMatrixCSC{Float64,Int64} with 0 stored entries +3×3 SparseMatrixCSC{Float64,Int64} with 0 stored entries: + ⋅ ⋅ ⋅ + ⋅ ⋅ ⋅ + ⋅ ⋅ ⋅ julia> spzeros(Float32, 4) 4-element SparseVector{Float32,Int64} with 0 stored entries @@ -3047,12 +3084,13 @@ Drop entry `A[i,j]` from `A` if `A[i,j]` is stored, and otherwise do nothing. ```jldoctest julia> A = sparse([1 2; 0 0]) 2×2 SparseMatrixCSC{Int64,Int64} with 2 stored entries: - [1, 1] = 1 - [1, 2] = 2 + 1 2 + ⋅ ⋅ julia> SparseArrays.dropstored!(A, 1, 2); A 2×2 SparseMatrixCSC{Int64,Int64} with 1 stored entry: - [1, 1] = 1 + 1 ⋅ + ⋅ ⋅ ``` """ function dropstored!(A::AbstractSparseMatrixCSC, i::Integer, j::Integer) @@ -3085,16 +3123,17 @@ stored and otherwise do nothing. Derivative forms: ```jldoctest julia> A = sparse(Diagonal([1, 2, 3, 4])) 4×4 SparseMatrixCSC{Int64,Int64} with 4 stored entries: - [1, 1] = 1 - [2, 2] = 2 - [3, 3] = 3 - [4, 4] = 4 + 1 ⋅ ⋅ ⋅ + ⋅ 2 ⋅ ⋅ + ⋅ ⋅ 3 ⋅ + ⋅ ⋅ ⋅ 4 julia> SparseArrays.dropstored!(A, [1, 2], [1, 1]) 4×4 SparseMatrixCSC{Int64,Int64} with 3 stored entries: - [2, 2] = 2 - [3, 3] = 3 - [4, 4] = 4 + ⋅ ⋅ ⋅ ⋅ + ⋅ 2 ⋅ ⋅ + ⋅ ⋅ 3 ⋅ + ⋅ ⋅ ⋅ 4 ``` """ function dropstored!(A::AbstractSparseMatrixCSC, @@ -3272,11 +3311,11 @@ Concatenate matrices block-diagonally. Currently only implemented for sparse mat ```jldoctest julia> blockdiag(sparse(2I, 3, 3), sparse(4I, 2, 2)) 5×5 SparseMatrixCSC{Int64,Int64} with 5 stored entries: - [1, 1] = 2 - [2, 2] = 2 - [3, 3] = 2 - [4, 4] = 4 - [5, 5] = 4 + 2 ⋅ ⋅ ⋅ ⋅ + ⋅ 2 ⋅ ⋅ ⋅ + ⋅ ⋅ 2 ⋅ ⋅ + ⋅ ⋅ ⋅ 4 ⋅ + ⋅ ⋅ ⋅ ⋅ 4 ``` """ function blockdiag(X::AbstractSparseMatrixCSC...) @@ -3477,14 +3516,11 @@ can be specified by passing `m,n` as the first arguments. ```jldoctest julia> spdiagm(-1 => [1,2,3,4], 1 => [4,3,2,1]) 5×5 SparseMatrixCSC{Int64,Int64} with 8 stored entries: - [2, 1] = 1 - [1, 2] = 4 - [3, 2] = 2 - [2, 3] = 3 - [4, 3] = 3 - [3, 4] = 2 - [5, 4] = 4 - [4, 5] = 1 + ⋅ 4 ⋅ ⋅ ⋅ + 1 ⋅ 3 ⋅ ⋅ + ⋅ 2 ⋅ 2 ⋅ + ⋅ ⋅ 3 ⋅ 1 + ⋅ ⋅ ⋅ 4 ⋅ ``` """ spdiagm(kv::Pair{<:Integer,<:AbstractVector}...) = _spdiagm(nothing, kv...) diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index aa91e0db6f9b7..e4add06a2ef98 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -828,6 +828,11 @@ end getindex(x::AbstractSparseVector, ::Colon) = copy(x) +function Base.isstored(x::AbstractSparseVector, i::Integer) + @boundscheck checkbounds(x, i) + return i in nonzeroinds(x) +end + ### show and friends function show(io::IO, ::MIME"text/plain", x::AbstractSparseVector) diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index efe5bca4c0c91..a5d71f0bcce7a 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -4,7 +4,7 @@ module SparseTests using Test using SparseArrays -using SparseArrays: getcolptr, nonzeroinds +using SparseArrays: getcolptr, nonzeroinds, _show_with_braille_patterns using LinearAlgebra using Printf: @printf # for debug using Random @@ -2267,54 +2267,92 @@ end @test A[1,1] == [1.0, 2.0, 3.0] end +@testset "isstored" begin + m = 5 + n = 4 + I = [1, 2, 5, 3] + J = [2, 3, 4, 2] + A = sparse(I, J, [1, 2, 3, 4], m, n) + stored_indices = [CartesianIndex(i, j) for (i, j) in zip(I, J)] + unstored_indices = [c for c in CartesianIndices((m, n)) if !(c in stored_indices)] + for c in stored_indices + @test Base.isstored(A, c[1], c[2]) == true + end + for c in unstored_indices + @test Base.isstored(A, c[1], c[2]) == false + end +end + @testset "show" begin io = IOBuffer() + show(io, MIME"text/plain"(), spzeros(Float64, Int64, 0, 0)) + @test String(take!(io)) == "0×0 SparseArrays.SparseMatrixCSC{Float64,Int64} with 0 stored entries" show(io, MIME"text/plain"(), sparse(Int64[1], Int64[1], [1.0])) - @test String(take!(io)) == "1×1 SparseArrays.SparseMatrixCSC{Float64,Int64} with 1 stored entry:\n [1, 1] = 1.0" + @test String(take!(io)) == "1×1 SparseArrays.SparseMatrixCSC{Float64,Int64} with 1 stored entry:\n 1.0" show(io, MIME"text/plain"(), spzeros(Float32, Int64, 2, 2)) - @test String(take!(io)) == "2×2 SparseArrays.SparseMatrixCSC{Float32,Int64} with 0 stored entries" - + @test String(take!(io)) == "2×2 SparseArrays.SparseMatrixCSC{Float32,Int64} with 0 stored entries:\n ⋅ ⋅ \n ⋅ ⋅ " + + A = sparse(Int64[1, 1], Int64[1, 2], [1.0, 2.0]) + show(io, MIME"text/plain"(), A) + @test String(take!(io)) == "1×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 2 stored entries:\n 1.0 2.0" + _show_with_braille_patterns(convert(IOContext, io), A) + @test String(take!(io)) == "⠉" + + # every 1-dot braille pattern + for (i, b) in enumerate(split("⠁⠂⠄⡀⠈⠐⠠⢀", "")) + A = spzeros(Int64, Int64, 4, 2) + A[i] = 1 + _show_with_braille_patterns(convert(IOContext, io), A) + @test String(take!(io)) == b + end + + # empty braille pattern Char(10240) + A = spzeros(Int64, Int64, 4, 2) + _show_with_braille_patterns(convert(IOContext, io), A) + @test String(take!(io)) == "" * Char(10240) + + A = sparse(Int64[1, 2, 4, 2, 3], Int64[1, 1, 1, 2, 2], Int64[1, 1, 1, 1, 1], 4, 2) + show(io, MIME"text/plain"(), A) + @test String(take!(io)) == "4×2 SparseArrays.SparseMatrixCSC{Int64,Int64} with 5 stored entries:\n 1 ⋅\n 1 1\n ⋅ 1\n 1 ⋅" + _show_with_braille_patterns(convert(IOContext, io), A) + @test String(take!(io)) == "⡳" + + A = sparse(Int64[1, 3, 2, 4], Int64[1, 1, 2, 2], Int64[1, 1, 1, 1], 7, 3) + show(io, MIME"text/plain"(), A) + @test String(take!(io)) == "7×3 SparseArrays.SparseMatrixCSC{Int64,Int64} with 4 stored entries:\n 1 ⋅ ⋅\n ⋅ 1 ⋅\n 1 ⋅ ⋅\n ⋅ 1 ⋅\n ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅" + _show_with_braille_patterns(convert(IOContext, io), A) + @test String(take!(io)) == "⢕" * Char(10240) * "\n" * Char(10240)^2 + + A = sparse(Int64[1:10;], Int64[1:10;], fill(Float64(1), 10)) + _show_with_braille_patterns(convert(IOContext, io), A) + brailleString = "⠑⢄" * Char(10240)^3 * "\n" * Char(10240)^2 * "⠑⢄" * Char(10240) * "\n" * Char(10240)^4 * "⠑" + @test String(take!(io)) == brailleString + + # Issue #30589 + @test repr("text/plain", sparse([true true])) == "1×2 SparseArrays.SparseMatrixCSC{Bool,$Int} with 2 stored entries:\n 1 1" + + function _filled_sparse(m::Integer, n::Integer) + C = CartesianIndices((m, n))[:] + Is = [Int64(x[1]) for x in C] + Js = [Int64(x[2]) for x in C] + return sparse(Is, Js, true, m, n) + end + + # vertical scaling ioc = IOContext(io, :displaysize => (5, 80), :limit => true) - show(ioc, MIME"text/plain"(), sparse(Int64[1], Int64[1], [1.0])) - @test String(take!(io)) == "1×1 SparseArrays.SparseMatrixCSC{Float64,Int64} with 1 stored entry:\n [1, 1] = 1.0" - show(ioc, MIME"text/plain"(), sparse(Int64[1, 1], Int64[1, 2], [1.0, 2.0])) - @test String(take!(io)) == "1×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 2 stored entries:\n ⋮" - - # even number of rows - ioc = IOContext(io, :displaysize => (8, 80), :limit => true) - show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4], Int64[1,1,2,2], [1.0,2.0,3.0,4.0])) - @test String(take!(io)) == string("4×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 4 stored entries:\n [1, 1]", - " = 1.0\n [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0") - - show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5], Int64[1,1,2,2,3], [1.0,2.0,3.0,4.0,5.0])) - @test String(take!(io)) == string("5×3 SparseArrays.SparseMatrixCSC{Float64,Int64} with 5 stored entries:\n [1, 1]", - " = 1.0\n ⋮\n [4, 2] = 4.0\n [5, 3] = 5.0") - - show(ioc, MIME"text/plain"(), sparse(fill(1.,5,3))) - @test String(take!(io)) == string("5×3 SparseArrays.SparseMatrixCSC{Float64,$Int} with 15 stored entries:\n [1, 1]", - " = 1.0\n ⋮\n [4, 3] = 1.0\n [5, 3] = 1.0") - - # odd number of rows - ioc = IOContext(io, :displaysize => (9, 80), :limit => true) - show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5], Int64[1,1,2,2,3], [1.0,2.0,3.0,4.0,5.0])) - @test String(take!(io)) == string("5×3 SparseArrays.SparseMatrixCSC{Float64,Int64} with 5 stored entries:\n [1, 1]", - " = 1.0\n [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0\n [5, 3] = 5.0") - - show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5,6], Int64[1,1,2,2,3,3], [1.0,2.0,3.0,4.0,5.0,6.0])) - @test String(take!(io)) == string("6×3 SparseArrays.SparseMatrixCSC{Float64,Int64} with 6 stored entries:\n [1, 1]", - " = 1.0\n [2, 1] = 2.0\n ⋮\n [5, 3] = 5.0\n [6, 3] = 6.0") - - show(ioc, MIME"text/plain"(), sparse(fill(1.,6,3))) - @test String(take!(io)) == string("6×3 SparseArrays.SparseMatrixCSC{Float64,$Int} with 18 stored entries:\n [1, 1]", - " = 1.0\n [2, 1] = 1.0\n ⋮\n [5, 3] = 1.0\n [6, 3] = 1.0") - - ioc = IOContext(io, :displaysize => (9, 80)) - show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5,6], Int64[1,1,2,2,3,3], [1.0,2.0,3.0,4.0,5.0,6.0])) - @test String(take!(io)) == string("6×3 SparseArrays.SparseMatrixCSC{Float64,Int64} with 6 stored entries:\n [1, 1] = 1.0\n", - " [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0\n [5, 3] = 5.0\n [6, 3] = 6.0") - - # issue #30589 - @test repr("text/plain", sparse([true true])) == "1×2 SparseArrays.SparseMatrixCSC{Bool,$Int} with 2 stored entries:\n [1, 1] = 1\n [1, 2] = 1" + _show_with_braille_patterns(ioc, _filled_sparse(10, 10)) + @test String(take!(io)) == "⣿⣿" + + _show_with_braille_patterns(ioc, _filled_sparse(20, 10)) + @test String(take!(io)) == "⣿" + + # horizontal scaling + ioc = IOContext(io, :displaysize => (80, 4), :limit => true) + _show_with_braille_patterns(ioc, _filled_sparse(8, 8)) + @test String(take!(io)) == "⣿⣿" + + _show_with_braille_patterns(ioc, _filled_sparse(8, 16)) + @test String(take!(io)) == "⠛⠛" end @testset "check buffers" for n in 1:3 diff --git a/stdlib/SparseArrays/test/sparsevector.jl b/stdlib/SparseArrays/test/sparsevector.jl index 3647551be2b18..975a9f2885476 100644 --- a/stdlib/SparseArrays/test/sparsevector.jl +++ b/stdlib/SparseArrays/test/sparsevector.jl @@ -35,6 +35,18 @@ x1_full[SparseArrays.nonzeroinds(spv_x1)] = nonzeros(spv_x1) @test count(SparseVector(8, [2, 5, 6], [true,false,true])) == 2 end +@testset "isstored" begin + x = spv_x1 + stored_inds = [2, 5, 6] + nonstored_inds = [1, 3, 4, 7, 8] + for i in stored_inds + @test Base.isstored(x, i) == true + end + for i in nonstored_inds + @test Base.isstored(x, i) == false + end +end + @testset "conversion to dense Array" begin for (x, xf) in [(spv_x1, x1_full)] @test isa(Array(x), Vector{Float64}) diff --git a/stdlib/SuiteSparse/src/cholmod.jl b/stdlib/SuiteSparse/src/cholmod.jl index 3cddb266a872f..801a5938a145c 100644 --- a/stdlib/SuiteSparse/src/cholmod.jl +++ b/stdlib/SuiteSparse/src/cholmod.jl @@ -1415,9 +1415,9 @@ true julia> P = sparse(1:3, C.p, ones(3)) 3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [3, 1] = 1.0 - [2, 2] = 1.0 - [1, 3] = 1.0 + ⋅ ⋅ 1.0 + ⋅ 1.0 ⋅ + 1.0 ⋅ ⋅ julia> P' * L * L' * P ≈ A true diff --git a/stdlib/SuiteSparse/src/spqr.jl b/stdlib/SuiteSparse/src/spqr.jl index ae6c2304e44d2..360afa6189964 100644 --- a/stdlib/SuiteSparse/src/spqr.jl +++ b/stdlib/SuiteSparse/src/spqr.jl @@ -165,10 +165,10 @@ solve least squares or underdetermined problems with [`\\`](@ref). The function ```jldoctest julia> A = sparse([1,2,3,4], [1,1,2,2], [1.0,1.0,1.0,1.0]) 4×2 SparseMatrixCSC{Float64,Int64} with 4 stored entries: - [1, 1] = 1.0 - [2, 1] = 1.0 - [3, 2] = 1.0 - [4, 2] = 1.0 + 1.0 ⋅ + 1.0 ⋅ + ⋅ 1.0 + ⋅ 1.0 julia> qr(A) Base.SparseArrays.SPQR.QRSparse{Float64,Int64} @@ -180,8 +180,8 @@ Q factor: -0.707107 0.0 0.0 0.707107 R factor: 2×2 SparseMatrixCSC{Float64,Int64} with 2 stored entries: - [1, 1] = -1.41421 - [2, 2] = -1.41421 + -1.41421 ⋅ + ⋅ -1.41421 Row permutation: 4-element Array{Int64,1}: 1 @@ -310,11 +310,10 @@ julia> F.Q julia> F.R 4×4 SparseMatrixCSC{Float64,Int64} with 5 stored entries: - [1, 1] = 3.0 - [2, 2] = 4.0 - [3, 3] = 5.0 - [2, 4] = 2.0 - [4, 4] = 1.0 + 3.0 ⋅ ⋅ ⋅ + ⋅ 4.0 ⋅ 2.0 + ⋅ ⋅ 5.0 ⋅ + ⋅ ⋅ ⋅ 1.0 julia> F.prow 4-element Array{Int64,1}: @@ -443,9 +442,10 @@ when the problem is underdetermined. ```jldoctest julia> A = sparse([1,2,4], [1,1,1], [1.0,1.0,1.0], 4, 2) 4×2 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 1] = 1.0 - [4, 1] = 1.0 + 1.0 ⋅ + 1.0 ⋅ + ⋅ ⋅ + 1.0 ⋅ julia> qr(A)\\fill(1.0, 4) 2-element Array{Float64,1}: diff --git a/test/show.jl b/test/show.jl index ade7cb361f8d6..5127be81ba28a 100644 --- a/test/show.jl +++ b/test/show.jl @@ -693,11 +693,11 @@ Base.zero(x::T12960) = T12960() let A = sparse(1.0I, 3, 3) B = similar(A, T12960) - @test sprint(show, B) == "\n [1, 1] = #undef\n [2, 2] = #undef\n [3, 3] = #undef" - @test sprint(print, B) == "\n [1, 1] = #undef\n [2, 2] = #undef\n [3, 3] = #undef" + @test sprint(show, B) == "\n #undef ⋅ ⋅ \n ⋅ #undef ⋅ \n ⋅ ⋅ #undef" + @test sprint(print, B) == "\n #undef ⋅ ⋅ \n ⋅ #undef ⋅ \n ⋅ ⋅ #undef" B[1,2] = T12960() - @test sprint(show, B) == "\n [1, 1] = #undef\n [1, 2] = T12960()\n [2, 2] = #undef\n [3, 3] = #undef" - @test sprint(print, B) == "\n [1, 1] = #undef\n [1, 2] = T12960()\n [2, 2] = #undef\n [3, 3] = #undef" + @test sprint(show, B) == "\n #undef T12960() ⋅ \n ⋅ #undef ⋅ \n ⋅ ⋅ #undef" + @test sprint(print, B) == "\n #undef T12960() ⋅ \n ⋅ #undef ⋅ \n ⋅ ⋅ #undef" end # issue #13127 From a55b12b5017da9275ffa89108ea818b71e50f083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Fri, 22 May 2020 20:52:25 +0100 Subject: [PATCH 019/232] Fix link in docstring of `InteractiveUtils.edit` (#35987) --- stdlib/InteractiveUtils/src/editless.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/InteractiveUtils/src/editless.jl b/stdlib/InteractiveUtils/src/editless.jl index 45eddd901806b..c92211220a910 100644 --- a/stdlib/InteractiveUtils/src/editless.jl +++ b/stdlib/InteractiveUtils/src/editless.jl @@ -186,7 +186,7 @@ Edit a file or directory optionally providing a line number to edit the file at. Return to the `julia` prompt when you quit the editor. The editor can be changed by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. -See also: (`define_editor`)[@ref] +See also: [`define_editor`](@ref) """ function edit(path::AbstractString, line::Integer=0) isempty(EDITOR_CALLBACKS) && define_default_editors() From 0bcd7fe0ed6016fdcc9fe5fdf5c7d3b92fc0cbea Mon Sep 17 00:00:00 2001 From: mbaz <1630034+mbaz@users.noreply.github.com> Date: Fri, 22 May 2020 19:51:00 -0400 Subject: [PATCH 020/232] Make it clear that `kwargs` is immutable (#35998) See https://github.com/JuliaLang/julia/issues/35985 --- doc/src/manual/functions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 549065b5384c8..aa7766f2b97a1 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -602,9 +602,9 @@ function f(x; y=0, kwargs...) end ``` -Inside `f`, `kwargs` will be a key-value iterator over a named tuple. Named -tuples (as well as dictionaries with keys of `Symbol`) can be passed as keyword -arguments using a semicolon in a call, e.g. `f(x, z=1; kwargs...)`. +Inside `f`, `kwargs` will be an immutable key-value iterator over a named tuple. +Named tuples (as well as dictionaries with keys of `Symbol`) can be passed as +keyword arguments using a semicolon in a call, e.g. `f(x, z=1; kwargs...)`. If a keyword argument is not assigned a default value in the method definition, then it is *required*: an [`UndefKeywordError`](@ref) exception will be thrown From 11c942f2b2050ff8a6b4827d7c1a00e5539294eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Sat, 23 May 2020 04:41:18 +0100 Subject: [PATCH 021/232] Bump Zlib BB release to `v1.2.11+10` which enables `-O3` optimisation (#35979) * Bump Zlib BB release to `v1.2.11+10` which enables `-O3` optimisation * Configure Zlib with CMake --- deps/Versions.make | 2 +- deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 | 1 + .../checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 | 1 + .../Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 | 1 + .../Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 | 1 + .../Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 | 1 + .../Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 | 1 + .../Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 | 1 + .../Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 | 1 + .../Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 | 1 + deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 | 1 + deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/md5 | 1 - .../Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/sha512 | 1 - .../checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/md5 | 1 - .../Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 | 1 - .../Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 | 1 - .../Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 | 1 - .../Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 | 1 - .../Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 | 1 - deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 | 1 - deps/zlib.mk | 2 +- 54 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 delete mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 diff --git a/deps/Versions.make b/deps/Versions.make index 6d0a5f633ed1c..c7cea9296fba4 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -33,7 +33,7 @@ LIBUV_BB_REL = 6 OBJCONV_VER = 2.49.0 OBJCONV_BB_REL = 0 ZLIB_VER = 1.2.11 -ZLIB_BB_REL = 6 +ZLIB_BB_REL = 10 P7ZIP_VER = 16.2.0 P7ZIP_BB_REL = 1 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..5cfb66f75df19 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +51358c64e42d3b8d923451278fc7aa18 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..54ce95ddbe8e0 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +4812a79c1abf7b6301f7d1b4d65116e1add5edcaee574c6797d5c6c75b97fc9557d4e321d69afcf2dc1cd7c86b5f23f26a5cffd4541235e43c3d929498793554 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..7190114e8c8a2 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +d967ee51bc241699462b50466208b7a1 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..cd3df5f660438 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +b534781b3aa10ed2d5b426c2e4773ebf02aa09dd3b99326b3d74361260459ca4aff8e0638f5bafc2bc7b1c5e9eb27d37815194667ba09bf75360489caac2dc07 diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000000000..c0c8feee7312a --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +4324accedf22fe4733323f91ec408eca diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..08588f645c5ae --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +e7bc261ef2ff5e1dad9135dd6278365db0865b99b75d4086a27793e8c45276a609556c8145188fdfa9e617f583ac1c8992d02854862453d40849e49c39ae4a2e diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000000000..f0c5f60add8fe --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +6544bd96203abd28b5a9ab4de809cfb3 diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..f2430d4cd58b6 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +cec06a2b7a1bd44f69c5281aa4db0f032506598b5d9b2f81716587f07321528aefc8f52c3ad686c16b923715fa13b0d7a0f182d7dacafafe55059346f1ea24cb diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..3c4eb40c8cde4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +760ea40b76d53f6de170f09a17fc3aec diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..eb388256010e0 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +550e950ddf02598eefa013c47f9c13ded8468fdf871602de85156f2c4654f515a56ba3b8881441144a78453ebed99b9ea4b580ad940b9d587208309b20da385c diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..b42db353b33e4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +390f2326a32f1c5ee44585a49a60ecf9 diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..65c0a0b6a155f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +357beb491a420dbbd6640df3ff8bb3e32be9fe0308d4b44c956b1c50f40f08dd6d77118c7ae46a4149e48772e6b545987542e7709d545cc486374710a6bc8cb3 diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..42c3fcf5122a1 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +0b4e00c3d2ef8a4aaf7154c18ada2348 diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..8949fa694c244 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +9bb1f68607b07c8f20f18d839d7b13ca87e45ac57a6c3bffcf27db26c11d99433e08af391939b61c13f49e5df5feddce2ff5d9d962ca0d7e89d850288f563172 diff --git a/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..746718fc13e8a --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +becb958e9f99b08a152824c8a44454d5 diff --git a/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..d0fb756d83e58 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +857225765e3af15463128b7d4aca87c358c34c802fbf7863cdeed06f958b2c3976750c8583bfcf0ef12c106519563248f80a676337d340fddfc005f7cab09b7c diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000000000..36752a8ff77b4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +b073327b80d77fcb321aa90ac054f3bc diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000000000..46257881f45d0 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +3e568123255eb2e3fd6b59395ebc72407743f596ead1cc2efaf1f2cbdb88f554ea53cf7caa3f062a8a6695eeb591a6f6ff1304bd0915b148fce4849c36e915e2 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..0223a5fba0345 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +9b387576651cf42d2cba762509603aa0 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..62af5484db9ae --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +31020590c9f122954eb545515adeb3b93894caa7098e6a12b70cbbd71a9e1b71f4d3185d0960f4f2e192fbe74a0d2c944d1cd897c715aa3e7182ea3cfc82a63f diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..7a9589bf3255d --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +8eb752c5fb3f1f013a91ba84db33f268 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..1b6ab531d9ebb --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +9c4b198cceffc5e3c87e91fa4f9223b973b090f5a343b1ea84425cbb445a7d9a34926ca55e4febae8783fbba3390b3a1aa2bd59b3177335a544c862bd55b8ec5 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000000000..7a2282753a6f8 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +bffe25dd8fb78bbf881785eef64202d5 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000000000..07ff8b9147ade --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +2215ee8ebd3415f35a4f488537b2bb525bc8fef74419b2182bb8bc5b2e236fa7fb637c6aaa234e840c94c3940a71441e5a2dc9e7fb5d674372175a30dbb5fa19 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..957c62c98ebf2 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +9efba92de9aa49b43822309fcdce046a diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..6e8d8f638743e --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +c76553c7b79c8f2ed20dcbfbb55ebe06636e238c3ba7028beef84547cf1ee82c2a2a293712c9241e7b60cb5cf6c907eec7046b272a0ef26aed805eae4947a9c6 diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 deleted file mode 100644 index 00da64ec9a6a0..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9f518cd093311ea2c8ff624b06aaa4cd diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index e8006fef420c6..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8ca001e4cdc0de7c3224b92b6e454102c511225757b1c54f32734c65eb11cd57cb2fffc50b7e4d12c9b9cb243bdf87f3b215a2fa3ce538336d1852820b241ebf diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 deleted file mode 100644 index 216075ed1144e..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5157dfaf0a62241553783d38d6fa565c diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 deleted file mode 100644 index 631e470856d08..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -30199abf69bcee900087ae548de0303ae3ac5fa1fba24436ef406ad81675407eefb7157922e4701b2297d139eb8b4e3630a92c01b309de6b0205a98d60f2d63b diff --git a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/md5 deleted file mode 100644 index 4485a55d43f41..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2d42d6a3899b974f9c56c4fabfa34459 diff --git a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/sha512 deleted file mode 100644 index 31a1f61e0ff23..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-gnueabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -138254be838d7a9e356632785e5af30a562b498605e366e932011d9a41778e5a81c9073aa698529f95aa1101f2a7642e7ae81449f734d5e7bf0e3b4f26fedcae diff --git a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/md5 deleted file mode 100644 index 4dbcf6944a4a2..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5db5f13979777caac31c830b66e75e8f diff --git a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/sha512 deleted file mode 100644 index c47463b86564c..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.armv7l-linux-musleabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6088b1a14ff372a2b7d28187edcfe85b613242b812ae23cbaea88da1e580105f38f65f70b2e80833711e2e99ac1683bdf451d55e5dc9283bb7e8dd582680acfb diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 deleted file mode 100644 index b5d6d15e0769a..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -46d03f5f9190cb787e867c3eb7edea78 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 008795089e144..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ca08a089148260c2b6d81a87a1e00a4e3a881c6d8bdad0d00b3e42a56cc91a7b72b542a748fc01a49ce207785053636587417916c6ec0d112fae49c55f34fb65 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 deleted file mode 100644 index 811bce75bde6b..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -34725ad0598db3cd7817e3578453f1d8 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 deleted file mode 100644 index 1936e8f8500e4..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b2706f68cecc21ab5013b07c226eda8fd1a68101bd17fcd79b991ac3b74d9b9e63e54f804a48a86ea448c5695c3284ac11c07592ae76a10b4fd6f5881c3ee277 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 2074ae57eb49f..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -89611cfd8f7c987de8b8c8cfd9eaeae6 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index 482a799d0b6a9..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -128f0a2db6045c7e886dc283c93a3bff3f7bd64d2b2bcea0e4e67aac6c8e76ef9abcdac10cc56f5880ae32160ffe5ee4b42fdc9bc1fd3e07104776e16dc05aa0 diff --git a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 deleted file mode 100644 index e7b88b4458ee3..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1c32a314230b46db6eb02e72bd8b8b47 diff --git a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 9be88af9676b5..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -69e785a356eec5c12cb4fe2d3b2a5b6ced16152d51e28c6e4eb646cad07a951d73cfcfd015c233f0164ed28d9ad772403230dcee581526fa4d34fefb95c2bb13 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 deleted file mode 100644 index 5316272ba1928..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -0e14dd815045e47dd722ff55434428d1 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 deleted file mode 100644 index 7b21de4c41657..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4111e99386cab30de2f1c625079991576e97446c8a85bc37c05629ddf7b867bc32a74b94ed5552c2b057c4cf14f85cbc06b62459ed800cd9faa5a40382a6e958 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 deleted file mode 100644 index 77123e338d051..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9fcc23ef8cf1740418a147905f7aa45c diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 089693b3315bd..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2da223cb96bcd1845c8b84b44f779c8d464fd8dc21a8c8d7b065da07e7d995db627b9a2df1dc5247f70d1cd9e23f31c118288a0b3047884b19edd886e1dd6f2b diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 deleted file mode 100644 index 538081546aba3..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b3e690fe03ab5f57118ae2cff0e82f35 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 deleted file mode 100644 index 74786b8dcd156..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -cea679067b995a5503922e81c50dda603b0b9634bb07b54073f74e2d0ccd638ec6c10ab26ae09c5a3566c44d4ee78419abc99f22da55c8131901aa72f790cc5c diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 deleted file mode 100644 index 85a8c96a236b6..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -34a32be8fed7b26b3cef2f7c367241b8 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 deleted file mode 100644 index 5b033470ec838..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ba351d5415c86457b487e0078b8a86f2429e8c2aef8f3edb48647de8a2b7d05accf9582e8b58f49b46a67684b59c1bfa9ebda6ec93724747128c93f9efd69fa1 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 011042642da29..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -66292bfea89453db4945639d454fa739 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index a9cdbe9cd278d..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2f2ec74977e5a713c2336a0ef5f2553ec5393285ae3a6914ab8d9b535337ad36d9f07fc180df10db302d96dc6b9ca2d7d178a9f42b906276b97b597953dc4782 diff --git a/deps/zlib.mk b/deps/zlib.mk index 0de264223ea06..cdefe1b81ab82 100644 --- a/deps/zlib.mk +++ b/deps/zlib.mk @@ -5,7 +5,7 @@ $(eval $(call git-external,zlib,ZLIB,,,$(SRCCACHE))) ifneq ($(USE_BINARYBUILDER_ZLIB), 1) $(BUILDDIR)/$(ZLIB_SRC_DIR)/build-configured: $(SRCCACHE)/$(ZLIB_SRC_DIR)/source-extracted mkdir -p $(dir $@) - cd $(dir $@) && $(dir $<)/configure --prefix=$(abspath $(build_prefix)) --libdir=$(abspath $(build_libdir)) + cd $(dir $@) && $(CMAKE) -DCMAKE_INSTALL_PREFIX=$(abspath $(build_prefix)) -DCMAKE_BUILD_TYPE=Release -DUNIX=true $(dir $<) echo 1 > $@ $(BUILDDIR)/$(ZLIB_SRC_DIR)/build-compiled: $(BUILDDIR)/$(ZLIB_SRC_DIR)/build-configured From 7f6dac08697b61670233eb50081ee4d95353d8ac Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 15 May 2020 07:33:56 -0500 Subject: [PATCH 022/232] Prevent more ! invalidations from IOContexts All of these "fields" are documented to be Bool, so this should not break any code and may improve error messages in case of user error. Usages like `if limit...`, `limit ? a : b`, or `limit && return nothing` do not need these fixes, since these are baked into the language rather than being handled by dispatch. --- base/Enums.jl | 2 +- base/arrayshow.jl | 2 +- base/channels.jl | 2 +- base/dict.jl | 2 +- base/range.jl | 1 - base/ryu/Ryu.jl | 2 +- base/show.jl | 14 +++++++------- stdlib/REPL/src/REPL.jl | 2 +- stdlib/SparseArrays/src/sparsematrix.jl | 2 +- stdlib/SparseArrays/src/sparsevector.jl | 2 +- 10 files changed, 15 insertions(+), 16 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index b4c02327dd5e2..41e9da63920ac 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -29,7 +29,7 @@ Base.print(io::IO, x::Enum) = print(io, Symbol(x)) function Base.show(io::IO, x::Enum) sym = Symbol(x) - if !get(io, :compact, false) + if !(get(io, :compact, false)::Bool) from = get(io, :module, Main) def = typeof(x).name.module if from === nothing || !Base.isvisible(sym, def, from) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index 338f511950d51..d57320dfdfb24 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -166,7 +166,7 @@ function print_matrix(io::IO, X::AbstractVecOrMat, vdots::AbstractString = "\u22ee", ddots::AbstractString = " \u22f1 ", hmod::Integer = 5, vmod::Integer = 5) - if !get(io, :limit, false) + if !(get(io, :limit, false)::Bool) screenheight = screenwidth = typemax(Int) else sz = displaysize(io) diff --git a/base/channels.jl b/base/channels.jl index 40f151e54f456..36c2f041a06e4 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -444,7 +444,7 @@ show(io::IO, c::Channel) = print(io, typeof(c), "(", c.sz_max, ")") function show(io::IO, ::MIME"text/plain", c::Channel) show(io, c) - if !get(io, :compact, false) + if !(get(io, :compact, false)::Bool) if !isopen(c) print(io, " (closed)") else diff --git a/base/dict.jl b/base/dict.jl index 109538e70cf4f..65eacd9b1962b 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -25,7 +25,7 @@ function show(io::IO, t::AbstractDict{K,V}) where V where K recur_io = IOContext(io, :SHOWN_SET => t, :typeinfo => eltype(t)) - limit::Bool = get(io, :limit, false) + limit = get(io, :limit, false)::Bool # show in a Julia-syntax-like form: Dict(k=>v, ...) print(io, typeinfo_prefix(io, t)[1]) print(io, '(') diff --git a/base/range.jl b/base/range.jl index 350c70ce3bead..243acb40660b9 100644 --- a/base/range.jl +++ b/base/range.jl @@ -457,7 +457,6 @@ function print_range(io::IO, r::AbstractRange, hdots::AbstractString = ",\u2026,") # horiz ellipsis # This function borrows from print_matrix() in show.jl # and should be called by show and display - limit = get(io, :limit, false) sz = displaysize(io) if !haskey(io, :compact) io = IOContext(io, :compact => true) diff --git a/base/ryu/Ryu.jl b/base/ryu/Ryu.jl index 62600236b20da..1d260fe9b3696 100644 --- a/base/ryu/Ryu.jl +++ b/base/ryu/Ryu.jl @@ -109,7 +109,7 @@ function writeexp(x::T, end function Base.show(io::IO, x::T, forceuntyped::Bool=false, fromprint::Bool=false) where {T <: Base.IEEEFloat} - compact = get(io, :compact, false) + compact = get(io, :compact, false)::Bool buf = Base.StringVector(neededdigits(T)) typed = !forceuntyped && !compact && get(io, :typeinfo, Any) != typeof(x) pos = writeshortest(buf, 1, x, false, false, true, -1, diff --git a/base/show.jl b/base/show.jl index 987a146d9ee5e..1ccdfd2138163 100644 --- a/base/show.jl +++ b/base/show.jl @@ -49,7 +49,7 @@ function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) summary(io, iter) isempty(iter) && return print(io, ". ", isa(iter,KeySet) ? "Keys" : "Values", ":") - limit::Bool = get(io, :limit, false) + limit = get(io, :limit, false)::Bool if limit sz = displaysize(io) rows, cols = sz[1] - 3, sz[2] @@ -79,7 +79,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} isempty(t) && return show(io, t) # show more descriptively, with one line per key/value pair recur_io = IOContext(io, :SHOWN_SET => t) - limit::Bool = get(io, :limit, false) + limit = get(io, :limit, false)::Bool if !haskey(io, :compact) recur_io = IOContext(recur_io, :compact => true) end @@ -149,7 +149,7 @@ function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T isempty(t) && return show(io, t) # show more descriptively, with one line per value recur_io = IOContext(io, :SHOWN_SET => t) - limit::Bool = get(io, :limit, false) + limit = get(io, :limit, false)::Bool summary(io, t) isempty(t) && return @@ -450,11 +450,11 @@ function show_function(io::IO, f::Function, compact::Bool) end end -show(io::IO, f::Function) = show_function(io, f, get(io, :compact, false)) +show(io::IO, f::Function) = show_function(io, f, get(io, :compact, false)::Bool) print(io::IO, f::Function) = show_function(io, f, true) function show(io::IO, f::Core.IntrinsicFunction) - if !get(io, :compact, false) + if !(get(io, :compact, false)::Bool) print(io, "Core.Intrinsics.") end print(io, nameof(f)) @@ -564,7 +564,7 @@ function show_type_name(io::IO, tn::Core.TypeName) sym = (globfunc ? globname : tn.name)::Symbol globfunc && print(io, "typeof(") quo = false - if !get(io, :compact, false) + if !(get(io, :compact, false)::Bool) # Print module prefix unless type is visible from module passed to # IOContext If :module is not set, default to Main. nothing can be used # to force printing prefix @@ -2129,7 +2129,7 @@ function alignment(io::IO, x::Pair) ctx = IOContext(io, :typeinfo => gettypeinfos(io, x)[1]) left = length(sprint(show, x.first, context=ctx, sizehint=0)) left += 2 * !isdelimited(ctx, x.first) # for parens around p.first - left += !get(io, :compact, false) # spaces are added around "=>" + left += !(get(io, :compact, false)::Bool) # spaces are added around "=>" (left+1, length(s)-left-1) # +1 for the "=" part of "=>" else (0, length(s)) # as for x::Any diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 30af00a6cdc06..5259a4fd717e8 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1161,7 +1161,7 @@ StreamREPL(stream::IO) = StreamREPL(stream, Base.text_colors[:green], Base.input run_repl(stream::IO) = run_repl(StreamREPL(stream)) outstream(s::StreamREPL) = s.stream -hascolor(s::StreamREPL) = get(s.stream, :color, false) +hascolor(s::StreamREPL) = get(s.stream, :color, false)::Bool answer_color(r::LineEditREPL) = r.envcolors ? Base.answer_color() : r.answer_color answer_color(r::StreamREPL) = r.answer_color diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index ba8d2fba823a5..4b64362f6d3e4 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -275,7 +275,7 @@ function _show_with_braille_patterns(io::IOContext, S::AbstractSparseMatrixCSC) end function Base.show(io::IOContext, S::AbstractSparseMatrixCSC) - if max(size(S)...) < 16 && !get(io, :compact, false) + if max(size(S)...) < 16 && !(get(io, :compact, false)::Bool) ioc = IOContext(io, :compact => true) println(ioc) Base.print_matrix(ioc, S) diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index e4add06a2ef98..2cc9268e6fea8 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -854,7 +854,7 @@ function show(io::IOContext, x::AbstractSparseVector) if isempty(nzind) return show(io, MIME("text/plain"), x) end - limit::Bool = get(io, :limit, false) + limit = get(io, :limit, false)::Bool half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) pad = ndigits(n) if !haskey(io, :compact) From 7237fca7c0fdbdac218286881933aecc1a84fd96 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 18 May 2020 08:08:26 -0500 Subject: [PATCH 023/232] Add some typeasserts to reduce invalidation --- base/Enums.jl | 1 + base/show.jl | 2 +- base/strings/basic.jl | 2 +- stdlib/LibGit2/src/gitcredential.jl | 6 +++--- stdlib/REPL/src/docview.jl | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 41e9da63920ac..fe15ea2110781 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -159,6 +159,7 @@ macro enum(T, syms...) else throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s))) end + s = s::Symbol if !Base.isidentifier(s) throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier")) end diff --git a/base/show.jl b/base/show.jl index 1ccdfd2138163..e0e5e8bef75f6 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1198,7 +1198,7 @@ function show_import_path(io::IO, ex, quote_level) if i > 1 && ex.args[i-1] !== :(.) print(io, '.') end - show_sym(io, ex.args[i], allow_macroname=(i==length(ex.args))) + show_sym(io, ex.args[i]::Symbol, allow_macroname=(i==length(ex.args))) end else show_unquoted(io, ex, 0, 0, quote_level) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index e36a7fe92504d..9eb301339fa7d 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -290,7 +290,7 @@ julia> cmp("b", "β") function cmp(a::AbstractString, b::AbstractString) a === b && return 0 a, b = Iterators.Stateful(a), Iterators.Stateful(b) - for (c, d) in zip(a, b) + for (c::AbstractChar, d::AbstractChar) in zip(a, b) c ≠ d && return ifelse(c < d, -1, 1) end isempty(a) && return ifelse(isempty(b), 0, -1) diff --git a/stdlib/LibGit2/src/gitcredential.jl b/stdlib/LibGit2/src/gitcredential.jl index 246ce075d24bd..0a442337531a7 100644 --- a/stdlib/LibGit2/src/gitcredential.jl +++ b/stdlib/LibGit2/src/gitcredential.jl @@ -102,11 +102,11 @@ end function Base.read!(io::IO, cred::GitCredential) # https://git-scm.com/docs/git-credential#IOFMT - while !eof(io) - key = readuntil(io, '=') + while !(eof(io)::Bool) + key::AbstractString = readuntil(io, '=') if key == "password" value = Base.SecretBuffer() - while !eof(io) && (c = read(io, UInt8)) != UInt8('\n') + while !(eof(io)::Bool) && (c = read(io, UInt8)) != UInt8('\n') write(value, c) end seekstart(value) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 89451e80023e0..1b85278cccc03 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -607,9 +607,9 @@ moduleusings(mod) = ccall(:jl_module_usings, Any, (Any,), mod) filtervalid(names) = filter(x->!occursin(r"#", x), map(string, names)) accessible(mod::Module) = - [filter!(s -> !Base.isdeprecated(mod, s), names(mod, all = true, imported = true)); - map(names, moduleusings(mod))...; - collect(keys(Base.Docs.keywords))] |> unique |> filtervalid + Symbol[filter!(s -> !Base.isdeprecated(mod, s), names(mod, all=true, imported=true)); + map(names, moduleusings(mod))...; + collect(keys(Base.Docs.keywords))] |> unique |> filtervalid doc_completions(name) = fuzzysort(name, accessible(Main)) doc_completions(name::Symbol) = doc_completions(string(name)) From d54e26f35187d9dcb56f7561ca270b5752ba192a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 18 May 2020 08:12:06 -0500 Subject: [PATCH 024/232] Add a defensive ambiguity-resolving method --- base/abstractarray.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 918c6c0a88ff8..3a5468842228a 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1064,6 +1064,7 @@ _getindex(::IndexStyle, A::AbstractArray, I...) = error("getindex for $(typeof(A)) with types $(typeof(I)) is not supported") ## IndexLinear Scalar indexing: canonical method is one Int +_getindex(::IndexLinear, A::AbstractVector, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) # ambiguity resolution in case packages specialize this (to be avoided if at all possible, but see Interpolations.jl) _getindex(::IndexLinear, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) function _getindex(::IndexLinear, A::AbstractArray, I::Vararg{Int,M}) where M @_inline_meta From 35524a906bc2c099f7892c45c6f391110ef58cb5 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 18 May 2020 08:11:45 -0500 Subject: [PATCH 025/232] Add `show` method for `RawFD` This prevents invalidation via `convert(::Type{Ptr{Cint}}, fd)`. --- base/libc.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/libc.jl b/base/libc.jl index 379c76dfd8c60..b8cca87ff6ce0 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -38,6 +38,8 @@ dup(src::RawFD, target::RawFD) = systemerror("dup", -1 == ccall((@static Sys.iswindows() ? :_dup2 : :dup2), Int32, (RawFD, RawFD), src, target)) +show(io::IO, fd::RawFD) = print(io, "RawFD(", bitcast(UInt32, fd), ')') # avoids invalidation via show_default + # Wrapper for an OS file descriptor (for Windows) if Sys.iswindows() primitive type WindowsRawSocket sizeof(Ptr) * 8 end # On Windows file descriptors are HANDLE's and 64-bit on 64-bit Windows From b5de2553f0d73ebf5558a48549f99bf1c8e602ae Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 18 May 2020 18:08:11 -0500 Subject: [PATCH 026/232] Ensure all `eof` methods return Bool --- base/io.jl | 2 +- stdlib/Base64/src/decode.jl | 2 +- stdlib/REPL/src/Terminals.jl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/io.jl b/base/io.jl index 811b94abb8673..178df80f30796 100644 --- a/base/io.jl +++ b/base/io.jl @@ -387,7 +387,7 @@ it is always safe to read one byte after seeing `eof` return `false`. `eof` will `false` as long as buffered data is still available, even if the remote end of a connection is closed. """ -eof(io::AbstractPipe) = eof(pipe_reader(io)) +eof(io::AbstractPipe) = eof(pipe_reader(io)::IO)::Bool reseteof(io::AbstractPipe) = reseteof(pipe_reader(io)) diff --git a/stdlib/Base64/src/decode.jl b/stdlib/Base64/src/decode.jl index 1a0a8570fc2de..f997daee286b2 100644 --- a/stdlib/Base64/src/decode.jl +++ b/stdlib/Base64/src/decode.jl @@ -123,7 +123,7 @@ function Base.readbytes!(pipe::Base64DecodePipe, data::AbstractVector{UInt8}, nb return filled end -Base.eof(pipe::Base64DecodePipe) = isempty(pipe.rest) && eof(pipe.io) +Base.eof(pipe::Base64DecodePipe) = isempty(pipe.rest) && eof(pipe.io)::Bool Base.close(pipe::Base64DecodePipe) = nothing # Decode data from (b1, b2, b3, b5, buffer, input) into (ptr, rest). diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index ac00f89b22b4c..5bea8c1a4b96d 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -96,8 +96,8 @@ disable_bracketed_paste(t::TextTerminal) = nothing abstract type UnixTerminal <: TextTerminal end -pipe_reader(t::UnixTerminal) = t.in_stream -pipe_writer(t::UnixTerminal) = t.out_stream +pipe_reader(t::UnixTerminal) = t.in_stream::IO +pipe_writer(t::UnixTerminal) = t.out_stream::IO mutable struct TerminalBuffer <: UnixTerminal out_stream::IO From e6813478a902ebabbb43d177d13e93dcd12a5fd1 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 23 May 2020 04:57:57 -0500 Subject: [PATCH 027/232] Fix inference of `Docs.meta(::Module)` --- base/docs/Docs.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 0d8665d0e54a4..91866e9db37d1 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -71,12 +71,13 @@ export doc const modules = Module[] const META = gensym(:meta) +const METAType = IdDict{Any,Any} -meta(m::Module) = isdefined(m, META) ? getfield(m, META) : IdDict() +meta(m::Module) = isdefined(m, META) ? getfield(m, META)::METAType : METAType() function initmeta(m::Module) if !isdefined(m, META) - Core.eval(m, :(const $META = $(IdDict()))) + Core.eval(m, :(const $META = $(METAType()))) push!(modules, m) end nothing @@ -204,9 +205,9 @@ mutable struct MultiDoc "Ordered (via definition order) vector of object signatures." order::Vector{Type} "Documentation for each object. Keys are signatures." - docs::IdDict{Any,Any} + docs::METAType - MultiDoc() = new(Type[], IdDict()) + MultiDoc() = new(Type[], METAType()) end # Docstring registration. From c51b0c8428609b46eed6381916092b181a0e803a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 23 May 2020 08:04:25 -0500 Subject: [PATCH 028/232] Prevent collect dispatch on Expr.args If you know you're building args of any Expr, you might as well start out collecting the arguments to a Vector{Any}, rather than using the clever type-narrowing machinery of collect/map/broadcast. --- base/abstractarray.jl | 2 ++ base/docs/Docs.jl | 13 +++++++------ base/expr.jl | 6 +++--- base/fastmath.jl | 2 +- base/views.jl | 8 ++++---- stdlib/Markdown/src/Julia/interp.jl | 2 +- stdlib/Markdown/src/Markdown.jl | 2 +- stdlib/Markdown/src/parse/config.jl | 2 +- 8 files changed, 20 insertions(+), 17 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 3a5468842228a..703895a96ba26 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2150,6 +2150,8 @@ end # map on collections map(f, A::AbstractArray) = collect_similar(A, Generator(f,A)) +mapany(f, itr) = map!(f, Vector{Any}(undef, length(itr)), itr) # convenient for Expr.args + # default to returning an Array for `map` on general iterators """ map(f, c...) -> collection diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 91866e9db37d1..8ba3949082008 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -62,7 +62,7 @@ include("bindings.jl") import .Base.Meta: quot, isexpr import .Base: Callable, with_output_color -using .Base: RefValue +using .Base: RefValue, mapany import ..CoreDocs: lazy_iterpolate export doc @@ -96,7 +96,7 @@ function signature!(tv, expr::Expr) push!(sig.args[end].args, argtype(arg)) end if isexpr(expr.args[1], :curly) && isempty(tv) - append!(tv, tvar.(expr.args[1].args[2:end])) + append!(tv, mapany(tvar, expr.args[1].args[2:end])) end for i = length(tv):-1:1 push!(sig.args, :(Tuple{$(tv[i].args[1])})) @@ -106,7 +106,7 @@ function signature!(tv, expr::Expr) end return sig elseif isexpr(expr, :where) - append!(tv, tvar.(expr.args[2:end])) + append!(tv, mapany(tvar, expr.args[2:end])) return signature!(tv, expr.args[1]) else return signature!(tv, expr.args[1]) @@ -332,13 +332,14 @@ function metadata(__source__, __module__, expr, ismodule) end if isexpr(expr, :struct) # Field docs for concrete types. - fields = [] + P = Pair{Any,Any} + fields = P[] last_docstr = nothing for each in expr.args[3].args if isa(each, Symbol) || isexpr(each, :(::)) # a field declaration if last_docstr !== nothing - push!(fields, (namify(each), last_docstr)) + push!(fields, P(namify(each), last_docstr)) last_docstr = nothing end elseif isexpr(each, :function) || isexpr(each, :(=)) @@ -349,7 +350,7 @@ function metadata(__source__, __module__, expr, ismodule) last_docstr = each end end - dict = :($(Dict)($([:($(Pair)($(quot(f)), $d)) for (f, d) in fields]...))) + dict = :($(Dict)($([(:($(P)($(quot(f)), $d)))::Expr for (f, d) in fields]...))) push!(args, :($(Pair)(:fields, $dict))) end return :($(Dict)($(args...))) diff --git a/base/expr.jl b/base/expr.jl index 4e7dd798cb21d..71eacaaacf4a0 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -265,7 +265,7 @@ function pushmeta!(ex::Expr, sym::Symbol, args::Any...) if isempty(args) tag = sym else - tag = Expr(sym, args...) + tag = Expr(sym, args...)::Expr end inner = ex @@ -277,7 +277,7 @@ function pushmeta!(ex::Expr, sym::Symbol, args::Any...) if idx != 0 push!(exargs[idx].args, tag) else - body::Expr = inner.args[2] + body = inner.args[2]::Expr pushfirst!(body.args, Expr(:meta, tag)) end ex @@ -333,7 +333,7 @@ end function findmeta(ex::Expr) if ex.head === :function || is_short_function_def(ex) || ex.head === :-> - body::Expr = ex.args[2] + body = ex.args[2]::Expr body.head === :block || error(body, " is not a block expression") return findmeta_block(ex.args) end diff --git a/base/fastmath.jl b/base/fastmath.jl index 8d36e6e7b5653..c066969349e61 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -118,7 +118,7 @@ function make_fastmath(expr::Expr) end end end - Expr(make_fastmath(expr.head), map(make_fastmath, expr.args)...) + Expr(make_fastmath(expr.head), Base.mapany(make_fastmath, expr.args)...) end function make_fastmath(symb::Symbol) fast_symb = get(fast_op, symb, :nothing) diff --git a/base/views.jl b/base/views.jl index eebd1f31b6980..b82513d73ed8b 100644 --- a/base/views.jl +++ b/base/views.jl @@ -148,10 +148,10 @@ function _views(ex::Expr) # but still use views for the args of the ref: lhs = ex.args[1] Expr(ex.head, Meta.isexpr(lhs, :ref) ? - Expr(:ref, _views.(lhs.args)...) : _views(lhs), + Expr(:ref, mapany(_views, lhs.args)...) : _views(lhs), _views(ex.args[2])) elseif ex.head === :ref - Expr(:call, maybeview, _views.(ex.args)...) + Expr(:call, maybeview, mapany(_views, ex.args)...) else h = string(ex.head) # don't use view on the lhs of an op-assignment a[i...] += ... @@ -182,9 +182,9 @@ function _views(ex::Expr) Expr(first(h) == '.' ? :(.=) : :(=), :($a[$(I...)]), Expr(:call, Symbol(h[1:end-1]), :($maybeview($a, $(I...))), - _views.(ex.args[2:end])...))) + mapany(_views, ex.args[2:end])...))) else - Expr(ex.head, _views.(ex.args)...) + Expr(ex.head, mapany(_views, ex.args)...) end end end diff --git a/stdlib/Markdown/src/Julia/interp.jl b/stdlib/Markdown/src/Julia/interp.jl index 50e51f0f90696..7cd8cdc050124 100644 --- a/stdlib/Markdown/src/Julia/interp.jl +++ b/stdlib/Markdown/src/Julia/interp.jl @@ -39,7 +39,7 @@ end toexpr(x) = x -toexpr(xs::Vector{Any}) = Expr(:call, GlobalRef(Base,:getindex), Any, map(toexpr, xs)...) +toexpr(xs::Vector{Any}) = Expr(:call, GlobalRef(Base,:getindex), Any, mapany(toexpr, xs)...) for T in Any[MD, Paragraph, Header, Link, Bold, Italic] @eval function toexpr(md::$T) diff --git a/stdlib/Markdown/src/Markdown.jl b/stdlib/Markdown/src/Markdown.jl index 8752542bbd445..781fcbdafddc8 100644 --- a/stdlib/Markdown/src/Markdown.jl +++ b/stdlib/Markdown/src/Markdown.jl @@ -5,7 +5,7 @@ Tools for working with the Markdown file format. Mainly for documentation. """ module Markdown -import Base: show, ==, with_output_color +import Base: show, ==, with_output_color, mapany using Base64: stringmime # Margin for printing in terminal. diff --git a/stdlib/Markdown/src/parse/config.jl b/stdlib/Markdown/src/parse/config.jl index 0daf36b3c2442..6cf2f145a6b63 100644 --- a/stdlib/Markdown/src/parse/config.jl +++ b/stdlib/Markdown/src/parse/config.jl @@ -76,7 +76,7 @@ const flavors = Dict{Symbol, Config}() macro flavor(name, features) quote - const $(esc(name)) = config($(map(esc,features.args)...)) + const $(esc(name)) = config($(mapany(esc,features.args)...)) flavors[$(Expr(:quote, name))] = $(esc(name)) end end From b4e51281f9134c9a1b198e95da7facfcbb9f538c Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 23 May 2020 08:04:47 -0500 Subject: [PATCH 029/232] Hide CoreLogging.catch_exceptions behind runtime dispatch --- base/logging.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/logging.jl b/base/logging.jl index a9618af5d94c9..9e1b29fd544fc 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -80,6 +80,7 @@ end _invoked_min_enabled_level(@nospecialize(logger)) = invoke(min_enabled_level, Tuple{typeof(logger)}, logger) +_invoked_catch_exceptions(@nospecialize(logger)) = invoke(catch_exceptions, Tuple{typeof(logger)}, logger) """ NullLogger() @@ -349,7 +350,7 @@ end # Report an error in log message creation (or in the logger itself). @noinline function logging_error(logger, level, _module, group, id, filepath, line, @nospecialize(err)) - if !catch_exceptions(logger) + if !_invoked_catch_exceptions(logger) rethrow(err) end try From 852ff2bdab0b0cac987c0f1ef552ef11202cd2d5 Mon Sep 17 00:00:00 2001 From: Kenta Sato Date: Sat, 23 May 2020 23:33:07 +0900 Subject: [PATCH 030/232] Add note on the complexity of length for strings (#35999) --- base/strings/basic.jl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index e36a7fe92504d..de92eec12d61e 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -346,15 +346,22 @@ isless(a::Symbol, b::Symbol) = cmp(a, b) < 0 length(s::AbstractString) -> Int length(s::AbstractString, i::Integer, j::Integer) -> Int -The number of characters in string `s` from indices `i` through `j`. This is -computed as the number of code unit indices from `i` to `j` which are valid -character indices. With only a single string argument, this computes the -number of characters in the entire string. With `i` and `j` arguments it +Return the number of characters in string `s` from indices `i` through `j`. + +This is computed as the number of code unit indices from `i` to `j` which are +valid character indices. With only a single string argument, this computes +the number of characters in the entire string. With `i` and `j` arguments it computes the number of indices between `i` and `j` inclusive that are valid indices in the string `s`. In addition to in-bounds values, `i` may take the out-of-bounds value `ncodeunits(s) + 1` and `j` may take the out-of-bounds value `0`. +!!! note + The time complexity of this operation is linear in general. That is, it + will take the time proportional to the number of bytes or characters in + the string because it counts the value on the fly. This is in contrast to + the method for arrays, which is a constant-time operation. + See also: [`isvalid`](@ref), [`ncodeunits`](@ref), [`lastindex`](@ref), [`thisind`](@ref), [`nextind`](@ref), [`prevind`](@ref) From f705e6026cb3913de7dbb3f7992bd96031428d84 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 23 May 2020 14:47:33 -0700 Subject: [PATCH 031/232] Implement reduce_empty(op, Union{}) (#35843) --- base/reduce.jl | 22 +++++++++++++--------- test/reduce.jl | 3 +++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 414bac5099f78..6d19fa50e3037 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -306,25 +306,29 @@ with reduction `op` over an empty array with element type of `T`. If not defined, this will throw an `ArgumentError`. """ -reduce_empty(op, T) = _empty_reduce_error() -reduce_empty(::typeof(+), T) = zero(T) +reduce_empty(op, ::Type{T}) where {T} = _empty_reduce_error() +reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(+), ::Type{T}) where {T} = zero(T) reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) -reduce_empty(::typeof(*), T) = one(T) +reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(*), ::Type{T}) where {T} = one(T) reduce_empty(::typeof(*), ::Type{<:AbstractChar}) = "" reduce_empty(::typeof(&), ::Type{Bool}) = true reduce_empty(::typeof(|), ::Type{Bool}) = false -reduce_empty(::typeof(add_sum), T) = reduce_empty(+, T) +reduce_empty(::typeof(add_sum), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(add_sum), ::Type{T}) where {T} = reduce_empty(+, T) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallSigned} = zero(Int) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallUnsigned} = zero(UInt) -reduce_empty(::typeof(mul_prod), T) = reduce_empty(*, T) +reduce_empty(::typeof(mul_prod), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(mul_prod), ::Type{T}) where {T} = reduce_empty(*, T) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallSigned} = one(Int) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallUnsigned} = one(UInt) -reduce_empty(op::BottomRF, T) = reduce_empty(op.rf, T) -reduce_empty(op::MappingRF, T) = mapreduce_empty(op.f, op.rf, T) -reduce_empty(op::FilteringRF, T) = reduce_empty(op.rf, T) -reduce_empty(op::FlipArgs, T) = reduce_empty(op.f, T) +reduce_empty(op::BottomRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) +reduce_empty(op::MappingRF, ::Type{T}) where {T} = mapreduce_empty(op.f, op.rf, T) +reduce_empty(op::FilteringRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) +reduce_empty(op::FlipArgs, ::Type{T}) where {T} = reduce_empty(op.f, T) """ Base.mapreduce_empty(f, op, T) diff --git a/test/reduce.jl b/test/reduce.jl index 69b8b1911e7ea..9501f41f8b77c 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -46,6 +46,8 @@ end @test reduce(max, [8 6 7 5 3 0 9]) == 9 @test reduce(+, 1:5; init=1000) == (1000 + 1 + 2 + 3 + 4 + 5) @test reduce(+, 1) == 1 +@test_throws ArgumentError reduce(*, ()) +@test_throws ArgumentError reduce(*, Union{}[]) # mapreduce @test mapreduce(-, +, [-10 -9 -3]) == ((10 + 9) + 3) @@ -133,6 +135,7 @@ fz = float(z) @test sum(z) === 136 @test sum(fz) === 136.0 +@test_throws ArgumentError sum(Union{}[]) @test_throws ArgumentError sum(sin, Int[]) @test sum(sin, 3) == sin(3.0) @test sum(sin, [3]) == sin(3.0) From dd3f173cdf66ce1b651a786149d7a6c21940a918 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 24 May 2020 04:48:38 +0200 Subject: [PATCH 032/232] add an optimized version of == for SubString{String} (#35973) --- base/strings/substring.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 312ca1941fbce..98ef1bf949192 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -99,6 +99,11 @@ isvalid(s::SubString{String}) = isvalid(String, s) thisind(s::SubString{String}, i::Int) = _thisind_str(s, i) nextind(s::SubString{String}, i::Int) = _nextind_str(s, i) +function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) + s = sizeof(a) + s == sizeof(b) && 0 == _memcmp(a, b, s) +end + function cmp(a::SubString{String}, b::SubString{String}) na = sizeof(a) nb = sizeof(b) From 0413ef0e4de83b41b637ba02cc63314da45fe56b Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 23 May 2020 19:53:58 -0700 Subject: [PATCH 033/232] =?UTF-8?q?Fix=20type-instability=20of=20=E2=88=98?= =?UTF-8?q?=20and=20Some=20when=20wrapping=20types=20(#35980)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base/operators.jl | 18 +++++++++++++++++- base/show.jl | 2 ++ base/some.jl | 2 ++ test/operators.jl | 2 ++ test/some.jl | 3 +++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/base/operators.jl b/base/operators.jl index c7998b51756c9..605c0775ad00f 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -873,10 +873,26 @@ julia> ∘(fs...)(3) ``` """ function ∘ end + +struct ComposedFunction{F,G} <: Function + f::F + g::G + ComposedFunction{F, G}(f, g) where {F, G} = new{F, G}(f, g) + ComposedFunction(f, g) = new{Core.Typeof(f),Core.Typeof(g)}(f, g) +end + +(c::ComposedFunction)(x...) = c.f(c.g(x...)) + ∘(f) = f -∘(f, g) = (x...)->f(g(x...)) +∘(f, g) = ComposedFunction(f, g) ∘(f, g, h...) = ∘(f ∘ g, h...) +function show(io::IO, c::ComposedFunction) + show(io, c.f) + print(io, " ∘ ") + show(io, c.g) +end + """ !f::Function diff --git a/base/show.jl b/base/show.jl index 987a146d9ee5e..72f84723c7dce 100644 --- a/base/show.jl +++ b/base/show.jl @@ -44,6 +44,8 @@ function show(io::IO, ::MIME"text/plain", f::Function) end end +show(io::IO, ::MIME"text/plain", c::ComposedFunction) = show(io, c) + function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) isempty(iter) && get(io, :compact, false) && return show(io, iter) summary(io, iter) diff --git a/base/some.jl b/base/some.jl index fbbc3aed0e284..82638e4250d2e 100644 --- a/base/some.jl +++ b/base/some.jl @@ -12,6 +12,8 @@ struct Some{T} value::T end +Some(::Type{T}) where {T} = Some{Type{T}}(T) + promote_rule(::Type{Some{T}}, ::Type{Some{S}}) where {T, S<:T} = Some{T} nonnothingtype(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Nothing) diff --git a/test/operators.jl b/test/operators.jl index 64c0ebab5ab37..d2bf82b11c993 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -133,6 +133,8 @@ Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 @test ∘(FreeMagma(1), FreeMagma(2)) === FreeMagma((1,2)) @test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3)) === FreeMagma(((1,2), 3)) @test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3), FreeMagma(4)) === FreeMagma((((1,2), 3), 4)) + + @test fieldtypes(typeof(Float64 ∘ Int)) == (Type{Float64}, Type{Int}) end @testset "function negation" begin diff --git a/test/some.jl b/test/some.jl index e368c7b27c5dc..224eb8600814c 100644 --- a/test/some.jl +++ b/test/some.jl @@ -98,3 +98,6 @@ using Base: notnothing # isnothing() @test !isnothing(1) @test isnothing(nothing) + +# type stability +@test fieldtype(typeof(Some(Int)), 1) === Type{Int} From 1747eacd3f15ee15c90b431e11936c58ee86d4b4 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Sun, 24 May 2020 23:36:48 +0200 Subject: [PATCH 034/232] add issuccess for CholeskyPivoted (#36002) * add issuccess for CholeskyPivoted * add tests, NEWS and compat --- NEWS.md | 2 +- stdlib/LinearAlgebra/src/cholesky.jl | 2 +- stdlib/LinearAlgebra/src/factorization.jl | 3 +++ stdlib/LinearAlgebra/test/cholesky.jl | 2 ++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index f8a7faae3f248..9bb3fc7e43f3e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,7 +47,7 @@ Standard library changes * All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). #### LinearAlgebra - +* New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]). #### Markdown diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index f42dda64ae548..33336b3d88d9d 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -447,7 +447,7 @@ end Base.propertynames(F::CholeskyPivoted, private::Bool=false) = (:U, :L, :p, :P, (private ? fieldnames(typeof(F)) : ())...) -issuccess(C::Cholesky) = C.info == 0 +issuccess(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0 function show(io::IO, mime::MIME{Symbol("text/plain")}, C::Cholesky{<:Any,<:AbstractMatrix}) if issuccess(C) diff --git a/stdlib/LinearAlgebra/src/factorization.jl b/stdlib/LinearAlgebra/src/factorization.jl index 737786a6df617..3e335ed391ad6 100644 --- a/stdlib/LinearAlgebra/src/factorization.jl +++ b/stdlib/LinearAlgebra/src/factorization.jl @@ -25,6 +25,9 @@ checknonsingular(info) = checknonsingular(info, Val{true}()) Test that a factorization of a matrix succeeded. +!!! compat "Julia 1.6" + `issuccess(::CholeskyPivoted)` requires Julia 1.6 or later. + ```jldoctest julia> F = cholesky([1 0; 0 1]); diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index c4dafe16dafd5..3d9cac4cc9a68 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -266,6 +266,8 @@ end @test_throws RankDeficientException cholesky!(copy(M), Val(true)) @test_throws RankDeficientException cholesky(M, Val(true); check = true) @test_throws RankDeficientException cholesky!(copy(M), Val(true); check = true) + @test !LinearAlgebra.issuccess(cholesky(M, Val(true); check = false)) + @test !LinearAlgebra.issuccess(cholesky!(copy(M), Val(true); check = false)) C = cholesky(M, Val(true); check = false) @test_throws RankDeficientException chkfullrank(C) C = cholesky!(copy(M), Val(true); check = false) From 10fd99176a7e17faa60bc91a64822782162b134c Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Sun, 24 May 2020 21:46:12 -0400 Subject: [PATCH 035/232] sparse linalg docs (#35814) * Add Sparse linear algebra section of the manual * Fix suitesparse doctests. --- doc/make.jl | 1 + stdlib/LinearAlgebra/docs/src/index.md | 9 +++---- stdlib/LinearAlgebra/src/cholesky.jl | 4 +-- stdlib/SuiteSparse/docs/src/index.md | 34 ++++++++++++++++++++++++++ stdlib/SuiteSparse/src/cholmod.jl | 24 +++++++++--------- stdlib/SuiteSparse/src/spqr.jl | 8 +++--- 6 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 stdlib/SuiteSparse/docs/src/index.md diff --git a/doc/make.jl b/doc/make.jl index 77dd9c159883d..15e381d7bed61 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -156,6 +156,7 @@ end # A few standard libraries need more than just the module itself in the DocTestSetup. # This overwrites the existing ones from above though, hence the warn=false. DocMeta.setdocmeta!(SparseArrays, :DocTestSetup, :(using SparseArrays, LinearAlgebra), recursive=true, warn=false) +DocMeta.setdocmeta!(SuiteSparse, :DocTestSetup, :(using SparseArrays, LinearAlgebra, SuiteSparse), recursive=true, warn=false) DocMeta.setdocmeta!(UUIDs, :DocTestSetup, :(using UUIDs, Random), recursive=true, warn=false) DocMeta.setdocmeta!(Pkg, :DocTestSetup, :(using Pkg, Pkg.Artifacts), recursive=true, warn=false) DocMeta.setdocmeta!(Pkg.BinaryPlatforms, :DocTestSetup, :(using Pkg, Pkg.BinaryPlatforms), recursive=true, warn=false) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index c5f0448bfa629..a195282261149 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -1,7 +1,7 @@ -# Linear Algebra +# [Linear Algebra](@id man-linalg) ```@meta -DocTestSetup = :(using LinearAlgebra) +DocTestSetup = :(using LinearAlgebra, SparseArrays, SuiteSparse) ``` In addition to (and as part of) its support for multi-dimensional arrays, Julia provides native implementations @@ -306,12 +306,9 @@ of the Linear Algebra documentation. | `Schur` | [Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition) | | `GeneralizedSchur` | [Generalized Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition#Generalized_Schur_decomposition) | - - ## Standard functions -Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). - Sparse factorizations call functions from [SuiteSparse](http://faculty.cse.tamu.edu/davis/suitesparse.html). +Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). Sparse matrix factorizations call functions from [SuiteSparse](http://suitesparse.com). Other sparse solvers are available as Julia packages. ```@docs Base.:*(::AbstractMatrix, ::AbstractMatrix) diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index 33336b3d88d9d..c9a28b2505001 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -222,7 +222,7 @@ end ### for StridedMatrices, check that matrix is symmetric/Hermitian """ - cholesky!(A, Val(false); check = true) -> Cholesky + cholesky!(A::StridedMatrix, Val(false); check = true) -> Cholesky The same as [`cholesky`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if @@ -269,7 +269,7 @@ cholesky!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; tol = 0.0, check::Bool ### for StridedMatrices, check that matrix is symmetric/Hermitian """ - cholesky!(A, Val(true); tol = 0.0, check = true) -> CholeskyPivoted + cholesky!(A::StridedMatrix, Val(true); tol = 0.0, check = true) -> CholeskyPivoted The same as [`cholesky`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the diff --git a/stdlib/SuiteSparse/docs/src/index.md b/stdlib/SuiteSparse/docs/src/index.md new file mode 100644 index 0000000000000..e8654caf943b4 --- /dev/null +++ b/stdlib/SuiteSparse/docs/src/index.md @@ -0,0 +1,34 @@ +# Sparse Linear Algebra + +```@meta +DocTestSetup = :(using LinearAlgebra, SparseArrays, SuiteSparse) +``` + +Sparse matrix solvers call functions from [SuiteSparse](http://suitesparse.com). The following factorizations are available: + +| Type | Description | +|:--------------------------------- |:--------------------------------------------- | +| `SuiteSparse.CHOLMOD.Factor` | Cholesky factorization | +| `SuiteSparse.UMFPACK.UmfpackLU` | LU factorization | +| `SuiteSparse.SPQR.QRSparse` | QR factorization | + +Other solvers such as [Pardiso.jl](https://github.com/JuliaSparse/Pardiso.jl/) are as external packages. [Arpack.jl](https://julialinearalgebra.github.io/Arpack.jl/stable/) provides `eigs` and `svds` for iterative solution of eigensystems and singular value decompositions. + +These factorizations are described in the [`Linear Algebra`](@ref man-linalg) section of the manual: +1. [`cholesky`](@ref) +2. [`ldlt`](@ref) +3. [`lu`](@ref) +4. [`qr`](@ref) + +```@docs +SuiteSparse.CHOLMOD.lowrankupdate +SuiteSparse.CHOLMOD.lowrankupdate! +SuiteSparse.CHOLMOD.lowrankdowndate +SuiteSparse.CHOLMOD.lowrankdowndate! +SuiteSparse.CHOLMOD.lowrankupdowndate! +``` + + +```@meta +DocTestSetup = nothing +``` diff --git a/stdlib/SuiteSparse/src/cholmod.jl b/stdlib/SuiteSparse/src/cholmod.jl index 801a5938a145c..fc7419e3e3296 100644 --- a/stdlib/SuiteSparse/src/cholmod.jl +++ b/stdlib/SuiteSparse/src/cholmod.jl @@ -1310,7 +1310,7 @@ function cholesky!(F::Factor{Tv}, A::Sparse{Tv}; end """ - cholesky!(F::Factor, A; shift = 0.0, check = true) -> CHOLMOD.Factor + cholesky!(F::CHOLMOD.Factor, A::SparseMatrixCSC; shift = 0.0, check = true) -> CHOLMOD.Factor Compute the Cholesky (``LL'``) factorization of `A`, reusing the symbolic factorization `F`. `A` must be a [`SparseMatrixCSC`](@ref) or a [`Symmetric`](@ref)/ @@ -1349,7 +1349,7 @@ function cholesky(A::Sparse; shift::Real=0.0, check::Bool = true, end """ - cholesky(A; shift = 0.0, check = true, perm = nothing) -> CHOLMOD.Factor + cholesky(A::SparseMatrixCSC; shift = 0.0, check = true, perm = nothing) -> CHOLMOD.Factor Compute the Cholesky factorization of a sparse positive definite matrix `A`. `A` must be a [`SparseMatrixCSC`](@ref) or a [`Symmetric`](@ref)/[`Hermitian`](@ref) @@ -1415,8 +1415,8 @@ true julia> P = sparse(1:3, C.p, ones(3)) 3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - ⋅ ⋅ 1.0 - ⋅ 1.0 ⋅ + ⋅ ⋅ 1.0 + ⋅ 1.0 ⋅ 1.0 ⋅ ⋅ julia> P' * L * L' * P ≈ A @@ -1474,7 +1474,7 @@ function ldlt!(F::Factor{Tv}, A::Sparse{Tv}; end """ - ldlt!(F::Factor, A; shift = 0.0, check = true) -> CHOLMOD.Factor + ldlt!(F::CHOLMOD.Factor, A::SparseMatrixCSC; shift = 0.0, check = true) -> CHOLMOD.Factor Compute the ``LDL'`` factorization of `A`, reusing the symbolic factorization `F`. `A` must be a [`SparseMatrixCSC`](@ref) or a [`Symmetric`](@ref)/[`Hermitian`](@ref) @@ -1518,7 +1518,7 @@ function ldlt(A::Sparse; shift::Real=0.0, check::Bool = true, end """ - ldlt(A; shift = 0.0, check = true, perm=nothing) -> CHOLMOD.Factor + ldlt(A::SparseMatrixCSC; shift = 0.0, check = true, perm=nothing) -> CHOLMOD.Factor Compute the ``LDL'`` factorization of a sparse matrix `A`. `A` must be a [`SparseMatrixCSC`](@ref) or a [`Symmetric`](@ref)/[`Hermitian`](@ref) @@ -1564,14 +1564,14 @@ ldlt(A::Union{SparseMatrixCSC{T},SparseMatrixCSC{Complex{T}}, ## Rank updates """ - lowrankupdowndate!(F::Factor, C::Sparse, update::Cint) + lowrankupdowndate!(F::CHOLMOD.Factor, C::Sparse, update::Cint) Update an `LDLt` or `LLt` Factorization `F` of `A` to a factorization of `A ± C*C'`. If sparsity preserving factorization is used, i.e. `L*L' == P*A*P'` then the new factor will be `L*L' == P*A*P' + C'*C` -update: `Cint(1)` for `A + CC'`, `Cint(0)` for `A - CC'` +`update`: `Cint(1)` for `A + CC'`, `Cint(0)` for `A - CC'` """ function lowrankupdowndate!(F::Factor{Tv}, C::Sparse{Tv}, update::Cint) where Tv<:VTypes lF = unsafe_load(pointer(F)) @@ -1590,7 +1590,7 @@ lowrank_reorder(V::AbstractArray,p) = Sparse(sparse(V[p,:])) lowrank_reorder(V::AbstractSparseArray,p) = Sparse(V[p,:]) """ - lowrankupdate!(F::Factor, C) + lowrankupdate!(F::CHOLMOD.Factor, C::AbstractArray) Update an `LDLt` or `LLt` Factorization `F` of `A` to a factorization of `A + C*C'`. @@ -1605,7 +1605,7 @@ function lowrankupdate!(F::Factor{Tv}, V::AbstractArray{Tv}) where Tv<:VTypes end """ - lowrankdowndate!(F::Factor, C) + lowrankdowndate!(F::CHOLMOD.Factor, C::AbstractArray) Update an `LDLt` or `LLt` Factorization `F` of `A` to a factorization of `A - C*C'`. @@ -1620,7 +1620,7 @@ function lowrankdowndate!(F::Factor{Tv}, V::AbstractArray{Tv}) where Tv<:VTypes end """ - lowrankupdate(F::Factor, C) -> FF::Factor + lowrankupdate(F::CHOLMOD.Factor, C::AbstractArray) -> FF::CHOLMOD.Factor Get an `LDLt` Factorization of `A + C*C'` given an `LDLt` or `LLt` factorization `F` of `A`. @@ -1632,7 +1632,7 @@ lowrankupdate(F::Factor{Tv}, V::AbstractArray{Tv}) where {Tv<:VTypes} = lowrankupdate!(copy(F), V) """ - lowrankupdate(F::Factor, C) -> FF::Factor + lowrankupdate(F::CHOLMOD.Factor, C::AbstractArray) -> FF::CHOLMOD.Factor Get an `LDLt` Factorization of `A + C*C'` given an `LDLt` or `LLt` factorization `F` of `A`. diff --git a/stdlib/SuiteSparse/src/spqr.jl b/stdlib/SuiteSparse/src/spqr.jl index 360afa6189964..bdfe3fb3795ef 100644 --- a/stdlib/SuiteSparse/src/spqr.jl +++ b/stdlib/SuiteSparse/src/spqr.jl @@ -149,7 +149,7 @@ _default_tol(A::SparseMatrixCSC) = 20*sum(size(A))*eps(real(eltype(A)))*maximum(norm(view(A, :, i)) for i in 1:size(A, 2)) """ - qr(A) -> QRSparse + qr(A::SparseMatrixCSC; tol=_default_tol(A), ordering=ORDERING_DEFAULT) -> QRSparse Compute the `QR` factorization of a sparse matrix `A`. Fill-reducing row and column permutations are used such that `F.R = F.Q'*A[F.prow,F.pcol]`. The main application of this type is to @@ -171,9 +171,9 @@ julia> A = sparse([1,2,3,4], [1,1,2,2], [1.0,1.0,1.0,1.0]) ⋅ 1.0 julia> qr(A) -Base.SparseArrays.SPQR.QRSparse{Float64,Int64} +SuiteSparse.SPQR.QRSparse{Float64,Int64} Q factor: -4×4 Base.SparseArrays.SPQR.QRSparseQ{Float64,Int64}: +4×4 SuiteSparse.SPQR.QRSparseQ{Float64,Int64}: -0.707107 0.0 0.0 -0.707107 0.0 -0.707107 -0.707107 0.0 0.0 -0.707107 0.707107 0.0 @@ -302,7 +302,7 @@ Extract factors of a QRSparse factorization. Possible values of `d` are julia> F = qr(sparse([1,3,2,3,4], [1,1,2,3,4], [1.0,2.0,3.0,4.0,5.0])); julia> F.Q -4×4 Base.SparseArrays.SPQR.QRSparseQ{Float64,Int64}: +4×4 SuiteSparse.SPQR.QRSparseQ{Float64,Int64}: 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 From 4714a869819f8f4dc96db4dd40e152d5125d8716 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Mon, 25 May 2020 03:47:51 +0200 Subject: [PATCH 036/232] Add lmul!(::Diagonal, ::AbstractVector) (#36012) --- stdlib/LinearAlgebra/src/diagonal.jl | 2 +- stdlib/LinearAlgebra/test/diagonal.jl | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 27e8ba4c80f3d..09ac42e34f0e2 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -193,7 +193,7 @@ function rmul!(A::AbstractMatrix, D::Diagonal) return A end -function lmul!(D::Diagonal, B::AbstractMatrix) +function lmul!(D::Diagonal, B::AbstractVecOrMat) require_one_based_indexing(B) B .= D.diag .* B return B diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 98b2fca354fd6..dd109cbb74890 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -173,11 +173,11 @@ Random.seed!(1) @test Array(D*a) ≈ DM*a @test Array(D/a) ≈ DM/a if relty <: BlasFloat - b = rand(elty,n,n) - b = sparse(b) - @test lmul!(copy(D), copy(b)) ≈ Array(D)*Array(b) - @test lmul!(transpose(copy(D)), copy(b)) ≈ transpose(Array(D))*Array(b) - @test lmul!(adjoint(copy(D)), copy(b)) ≈ Array(D)'*Array(b) + for b in (rand(elty,n,n), sparse(rand(elty,n,n)), rand(elty,n), sparse(rand(elty,n))) + @test lmul!(copy(D), copy(b)) ≈ Array(D)*Array(b) + @test lmul!(transpose(copy(D)), copy(b)) ≈ transpose(Array(D))*Array(b) + @test lmul!(adjoint(copy(D)), copy(b)) ≈ Array(D)'*Array(b) + end end end From 664e9ba4d79b8a9404efccccf594e34e1e1f728c Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 20 May 2020 16:14:06 +0200 Subject: [PATCH 037/232] Expose some additional LLVM APIs. --- src/llvm-api.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/llvm-api.cpp b/src/llvm-api.cpp index 03fc39cf091a1..1ec13fca7918d 100644 --- a/src/llvm-api.cpp +++ b/src/llvm-api.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include "julia.h" @@ -208,6 +210,28 @@ extern "C" JL_DLLEXPORT void LLVMExtraAddInternalizePassWithExportList( unwrap(PM)->add(createInternalizePass(PreserveFobj)); } +extern "C" JL_DLLEXPORT void LLVMExtraAppendToUsed(LLVMModuleRef Mod, + LLVMValueRef* Values, + size_t Count) { + SmallVector GlobalValues; + for (auto *Value : makeArrayRef(Values, Count)) + GlobalValues.push_back(cast(unwrap(Value))); + appendToUsed(*unwrap(Mod), GlobalValues); +} + +extern "C" JL_DLLEXPORT void LLVMExtraAppendToCompilerUsed(LLVMModuleRef Mod, + LLVMValueRef* Values, + size_t Count) { + SmallVector GlobalValues; + for (auto *Value : makeArrayRef(Values, Count)) + GlobalValues.push_back(cast(unwrap(Value))); + appendToCompilerUsed(*unwrap(Mod), GlobalValues); +} + +extern "C" JL_DLLEXPORT void LLVMExtraAddGenericAnalysisPasses(LLVMPassManagerRef PM) { + unwrap(PM)->add(createTargetTransformInfoWrapperPass(TargetIRAnalysis())); +} + // Awaiting D46627 From a3c68aeffb6551b9265dae456cbfa0a55cf1355a Mon Sep 17 00:00:00 2001 From: Paul Jurczak Date: Mon, 25 May 2020 07:45:47 -0600 Subject: [PATCH 038/232] Corrected grammar (#36021) --- doc/src/manual/variables-and-scoping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index bbcb7af3fccd7..7be28e4511889 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -641,7 +641,7 @@ julia> const z = 100 julia> z = 100 100 ``` -The last rule applies for immutable objects even if the variable binding would change, e.g.: +The last rule applies to immutable objects even if the variable binding would change, e.g.: ```julia-repl julia> const s1 = "1" "1" From 52d85a344056fb3ec0a752e90e01943ce69e8243 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 25 May 2020 16:54:33 +0200 Subject: [PATCH 039/232] allow SubString{String} to participate in filter optimization for String (#35975) --- base/strings/string.jl | 13 ------------- base/strings/substring.jl | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/base/strings/string.jl b/base/strings/string.jl index ee9abd745fd59..85695459b3438 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -348,16 +348,3 @@ function repeat(c::Char, r::Integer) end return s end - -function filter(f, s::String) - out = StringVector(sizeof(s)) - offset = 1 - for c in s - if f(c) - offset += __unsafe_string!(out, c, offset) - end - end - resize!(out, offset-1) - sizehint!(out, offset-1) - return String(out) -end diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 98ef1bf949192..bd2ae2b948335 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -222,4 +222,17 @@ function repeat(s::Union{String, SubString{String}}, r::Integer) return out end +function filter(f, s::Union{String, SubString{String}}) + out = StringVector(sizeof(s)) + offset = 1 + for c in s + if f(c) + offset += __unsafe_string!(out, c, offset) + end + end + resize!(out, offset-1) + sizehint!(out, offset-1) + return String(out) +end + getindex(s::AbstractString, r::UnitRange{<:Integer}) = SubString(s, r) From 8184684657fd3eddaa116db30def36959e1e26b6 Mon Sep 17 00:00:00 2001 From: Maxime Mouchet Date: Mon, 25 May 2020 21:32:11 +0200 Subject: [PATCH 040/232] Convert Base.alignment examples to jldoctests (#35961) --- base/show.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/base/show.jl b/base/show.jl index 72f84723c7dce..586c1bc41ea4b 100644 --- a/base/show.jl +++ b/base/show.jl @@ -2102,18 +2102,27 @@ end """ `alignment(io, X)` returns a tuple (left,right) showing how many characters are needed on either side of an alignment feature such as a decimal point. + +# Examples +```jldoctest +julia> Base.alignment(stdout, 42) +(2, 0) + +julia> Base.alignment(stdout, 4.23) +(1, 3) + +julia> Base.alignment(stdout, 1 + 10im) +(3, 5) +``` """ alignment(io::IO, x::Any) = (0, length(sprint(show, x, context=io, sizehint=0))) alignment(io::IO, x::Number) = (length(sprint(show, x, context=io, sizehint=0)), 0) -"`alignment(stdout, 42)` yields (2, 0)" alignment(io::IO, x::Integer) = (length(sprint(show, x, context=io, sizehint=0)), 0) -"`alignment(stdout, 4.23)` yields (1, 3) for `4` and `.23`" function alignment(io::IO, x::Real) m = match(r"^(.*?)((?:[\.eEfF].*)?)$", sprint(show, x, context=io, sizehint=0)) m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) : (length(m.captures[1]), length(m.captures[2])) end -"`alignment(stdout, 1 + 10im)` yields (3, 5) for `1 +` and `_10im` (plus sign on left, space on right)" function alignment(io::IO, x::Complex) m = match(r"^(.*[^ef][\+\-])(.*)$", sprint(show, x, context=io, sizehint=0)) m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) : From 575701248adc951038b9727e646230737e5b3c91 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 25 May 2020 13:42:44 -0700 Subject: [PATCH 041/232] =?UTF-8?q?Test=20show=20for=20=E2=88=98=20(#36009?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/operators.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/operators.jl b/test/operators.jl index d2bf82b11c993..fb0af5b552047 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -135,6 +135,9 @@ Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 @test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3), FreeMagma(4)) === FreeMagma((((1,2), 3), 4)) @test fieldtypes(typeof(Float64 ∘ Int)) == (Type{Float64}, Type{Int}) + + @test repr(uppercase ∘ first) == "uppercase ∘ first" + @test sprint(show, "text/plain", uppercase ∘ first) == "uppercase ∘ first" end @testset "function negation" begin From c3793643a02de92d0e2da438147f95ecf9220ea7 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 26 May 2020 04:09:44 -0500 Subject: [PATCH 042/232] Split `show_ir` so line printer can be used elsewhere (#35952) Currently JuliaInterpreter & Debugger need to print just a subset of CodeInfo statements (those surrounding the current program counter). Currently they print the whole thing to an IOBuffer, split the lines, and keep just a subset. Unfortunately, because some statements print over multiple lines, it's easy to get misaligned. This makes it possible to call the standard printer for a specified line. --- base/compiler/ssair/show.jl | 150 +++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 69 deletions(-) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index d3bd8e95bedd0..4f159478d9671 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -662,17 +662,14 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print end end -function show_ir(io::IO, code::CodeInfo, line_info_preprinter=DILineInfoPrinter(code.linetable), line_info_postprinter=default_expr_type_printer) - cols = displaysize(io)[2] - used = BitSet() +# Show a single statement, code.code[idx], in the context of the whole CodeInfo. +# Returns the updated value of bb_idx. +function show_ir_stmt(io::IO, code::CodeInfo, idx::Int, line_info_preprinter, line_info_postprinter, used::BitSet, cfg::CFG, bb_idx::Int) + ds = get(io, :displaysize, (24, 80))::Tuple{Int,Int} + cols = ds[2] stmts = code.code types = code.ssavaluetypes - cfg = compute_basic_blocks(stmts) max_bb_idx_size = length(string(length(cfg.blocks))) - for stmt in stmts - scan_ssa_use!(push!, used, stmt) - end - bb_idx = 1 if isempty(used) maxlength_idx = 0 @@ -680,73 +677,88 @@ function show_ir(io::IO, code::CodeInfo, line_info_preprinter=DILineInfoPrinter( maxused = maximum(used) maxlength_idx = length(string(maxused)) end - for idx in eachindex(stmts) - if !isassigned(stmts, idx) - # This is invalid, but do something useful rather - # than erroring, to make debugging easier - printstyled(io, "#UNDEF\n", color=:red) - continue - end - stmt = stmts[idx] - # Compute BB guard rail - if bb_idx > length(cfg.blocks) - # If invariants are violated, print a special leader - linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket - inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx]) - printstyled(io, "!!! ", "─"^max_bb_idx_size, color=:light_black) + + if !isassigned(stmts, idx) + # This is invalid, but do something useful rather + # than erroring, to make debugging easier + printstyled(io, "#UNDEF\n", color=:red) + return bb_idx + end + stmt = stmts[idx] + # Compute BB guard rail + if bb_idx > length(cfg.blocks) + # If invariants are violated, print a special leader + linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket + inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx]) + printstyled(io, "!!! ", "─"^max_bb_idx_size, color=:light_black) + else + bbrange = cfg.blocks[bb_idx].stmts + bbrange = bbrange.start:bbrange.stop + # Print line info update + linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=:light_black), context=io) + linestart *= " "^max_bb_idx_size + inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx]) + if idx == first(bbrange) + bb_idx_str = string(bb_idx) + bb_pad = max_bb_idx_size - length(bb_idx_str) + bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄" + printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=:light_black) + elseif idx == last(bbrange) # print separator + printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=:light_black) else - bbrange = cfg.blocks[bb_idx].stmts - bbrange = bbrange.start:bbrange.stop - # Print line info update - linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=:light_black), context=io) - linestart *= " "^max_bb_idx_size - inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx]) - if idx == first(bbrange) - bb_idx_str = string(bb_idx) - bb_pad = max_bb_idx_size - length(bb_idx_str) - bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄" - printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=:light_black) - elseif idx == last(bbrange) # print separator - printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=:light_black) - else - printstyled(io, "│ ", " "^max_bb_idx_size, color=:light_black) - end - if idx == last(bbrange) - bb_idx += 1 - end + printstyled(io, "│ ", " "^max_bb_idx_size, color=:light_black) end - print(io, inlining_indent, " ") - # convert statement index to labels, as expected by print_stmt - if stmt isa Expr - if stmt.head === :gotoifnot && length(stmt.args) == 2 && stmt.args[2] isa Int - stmt = GotoIfNot(stmt.args[1], block_for_inst(cfg, stmt.args[2]::Int)) - elseif stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int - stmt = Expr(:enter, block_for_inst(cfg, stmt.args[1]::Int)) - end - elseif isa(stmt, GotoIfNot) - stmt = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest)) - elseif stmt isa GotoNode - stmt = GotoNode(block_for_inst(cfg, stmt.label)) - elseif stmt isa PhiNode - e = stmt.edges - stmt = PhiNode(Any[block_for_inst(cfg, e[i]) for i in 1:length(e)], stmt.values) + if idx == last(bbrange) + bb_idx += 1 end - show_type = types isa Vector{Any} && should_print_ssa_type(stmt) - print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) - if types isa Vector{Any} # ignore types for pre-inference code - if !isassigned(types, idx) - # This is an error, but can happen if passes don't update their type information - printstyled(io, "::#UNDEF", color=:red) - elseif show_type - typ = types[idx] - line_info_postprinter(io, typ, idx in used) - end + end + print(io, inlining_indent, " ") + # convert statement index to labels, as expected by print_stmt + if stmt isa Expr + if stmt.head === :gotoifnot && length(stmt.args) == 2 && stmt.args[2] isa Int + stmt = GotoIfNot(stmt.args[1], block_for_inst(cfg, stmt.args[2]::Int)) + elseif stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int + stmt = Expr(:enter, block_for_inst(cfg, stmt.args[1]::Int)) end - println(io) + elseif isa(stmt, GotoIfNot) + stmt = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest)) + elseif stmt isa GotoNode + stmt = GotoNode(block_for_inst(cfg, stmt.label)) + elseif stmt isa PhiNode + e = stmt.edges + stmt = PhiNode(Any[block_for_inst(cfg, e[i]) for i in 1:length(e)], stmt.values) end - let linestart = " "^(max_bb_idx_size + 2) - line_info_preprinter(io, linestart, typemin(Int32)) + show_type = types isa Vector{Any} && should_print_ssa_type(stmt) + print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) + if types isa Vector{Any} # ignore types for pre-inference code + if !isassigned(types, idx) + # This is an error, but can happen if passes don't update their type information + printstyled(io, "::#UNDEF", color=:red) + elseif show_type + typ = types[idx] + line_info_postprinter(io, typ, idx in used) + end end + println(io) + return bb_idx +end + +function show_ir(io::IO, code::CodeInfo, line_info_preprinter=DILineInfoPrinter(code.linetable), line_info_postprinter=default_expr_type_printer) + ioctx = IOContext(io, :displaysize => displaysize(io)) + stmts = code.code + used = BitSet() + cfg = compute_basic_blocks(stmts) + for stmt in stmts + scan_ssa_use!(push!, used, stmt) + end + bb_idx = 1 + + for idx in eachindex(code.code) + bb_idx = show_ir_stmt(ioctx, code, idx, line_info_preprinter, line_info_postprinter, used, cfg, bb_idx) + end + + max_bb_idx_size = length(string(length(cfg.blocks))) + line_info_preprinter(io, " "^(max_bb_idx_size + 2), typemin(Int32)) nothing end From 7124472a3db233506fa580401e08bb2acad2ad5b Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 26 May 2020 13:18:55 -0400 Subject: [PATCH 043/232] more precise inference of `splatnew` (#35976) This allows constant-folding NamedTuple constructors, in turn allowing constant prop through keyword arguments. --- base/compiler/abstractinterpretation.jl | 11 +++++++++++ test/compiler/inference.jl | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 572ad43b72e51..66c75c4ae0164 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1028,6 +1028,17 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va end elseif e.head === :splatnew t = instanceof_tfunc(abstract_eval(interp, e.args[1], vtypes, sv))[1] + if length(e.args) == 2 && isconcretetype(t) && !t.mutable + at = abstract_eval(interp, e.args[2], vtypes, sv) + n = fieldcount(t) + if isa(at, Const) && isa(at.val, Tuple) && n == length(at.val) && + _all(i->at.val[i] isa fieldtype(t, i), 1:n) + t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val)) + elseif isa(at, PartialStruct) && at ⊑ Tuple && n == length(at.fields) && + _all(i->at.fields[i] ⊑ fieldtype(t, i), 1:n) + t = PartialStruct(t, at.fields) + end + end elseif e.head === :& abstract_eval(interp, e.args[1], vtypes, sv) t = Any diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index fec496f321b5c..525feb5dae2f7 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2588,3 +2588,16 @@ f() = _foldl_iter(step, (Missing[],), [0.0], 1) end @test Core.Compiler.typesubtract(Tuple{Union{Int,Char}}, Tuple{Char}) == Tuple{Int} @test Base.return_types(Issue35566.f) == [Val{:expected}] + +# constant prop through keyword arguments +_unstable_kw(;x=1,y=2) = x == 1 ? 0 : "" +_use_unstable_kw_1() = _unstable_kw(x = 2) +_use_unstable_kw_2() = _unstable_kw(x = 2, y = rand()) +@test Base.return_types(_use_unstable_kw_1) == Any[String] +@test Base.return_types(_use_unstable_kw_2) == Any[String] +@eval struct StructWithSplatNew + x::Int + StructWithSplatNew(t) = $(Expr(:splatnew, :StructWithSplatNew, :t)) +end +_construct_structwithsplatnew() = StructWithSplatNew(("",)) +@test Base.return_types(_construct_structwithsplatnew) == Any[StructWithSplatNew] From 39e360c5a83b6c9a1fef910e7afaa2f77b365ce4 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 26 May 2020 13:21:18 -0400 Subject: [PATCH 044/232] fix #34516, several issues with nospecialize on keyword arguments (#36019) - `at-nospecialize(a)` syntax was not allowed as a kwarg - include nospecialize annotations in the keyword sorter method - we were dropping nospecialize applied to keyword arguments --- src/julia-syntax.scm | 22 ++++++++++++++-------- test/keywordargs.jl | 10 +++++++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index d1e8ebcd8a911..27ac5d1e812aa 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -308,7 +308,7 @@ (let loop ((stmts body)) (if (eq? functionloc (cadr stmts)) (set-cdr! stmts (cddr stmts)) - (loop (cdr body))))) + (loop (cdr stmts))))) functionloc)) ;; construct the (method ...) expression for one primitive method definition, @@ -418,6 +418,8 @@ (define (keywords-method-def-expr name sparams argl body rett) (let* ((kargl (cdar argl)) ;; keyword expressions (= k v) + (annotations (map (lambda (a) `(meta ,(cadr a) ,(arg-name (cadr (caddr a))))) + (filter nospecialize-meta? kargl))) (kargl (map (lambda (a) (if (nospecialize-meta? a) (caddr a) a)) kargl)) @@ -457,8 +459,6 @@ keynames)) ;; list of function's initial line number and meta nodes (empty if none) (prologue (extract-method-prologue body)) - (annotations (map (lambda (a) `(meta ,(cadr a) ,(arg-name (cadr (caddr a))))) - (filter nospecialize-meta? kargl))) ;; body statements (stmts (cdr body)) (positional-sparams (filter-sparams (cons 'list pargl-all) sparams)) @@ -519,6 +519,11 @@ (call (core kwftype) ,ftype)) ,kw ,@pargl ,@vararg) `(block ,@(filter linenum? prologue) + ;; nospecialize meta for just positional args + ,@(map (lambda (m) + `(meta ,(cadr m) ,@(filter (lambda (v) (not (memq v keynames))) + (cddr m)))) + (filter nospecialize-meta? prologue)) ,(scopenest keynames (map (lambda (v dflt) @@ -653,13 +658,14 @@ (define (throw-unassigned-kw-args argl) (define (throw-unassigned argname) `(call (core throw) (call (core UndefKeywordError) (inert ,argname)))) + (define (to-kw x) + (cond ((symbol? x) `(kw ,x ,(throw-unassigned x))) + ((decl? x) `(kw ,x ,(throw-unassigned (cadr x)))) + ((nospecialize-meta? x) `(meta ,(cadr x) ,(to-kw (caddr x)))) + (else x))) (if (has-parameters? argl) (cons (cons 'parameters - (map (lambda (x) - (cond ((symbol? x) `(kw ,x ,(throw-unassigned x))) - ((decl? x) `(kw ,x ,(throw-unassigned (cadr x)))) - (else x))) - (cdar argl))) + (map to-kw (cdar argl))) (cdr argl)) argl)) diff --git a/test/keywordargs.jl b/test/keywordargs.jl index b2ae129ae0e01..f9be8edd80dc0 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -287,10 +287,18 @@ end @test g21147(((1,),), 2) === Int end @testset "issue #21510" begin - f21510(; Base.@nospecialize a = 2) = a + f21510(; @nospecialize a = 2) = a @test f21510(a=:b) == :b @test f21510() == 2 end +@testset "issue #34516" begin + f34516(; @nospecialize(x)) = 0 + f34516(y; @nospecialize(x::Any)) = 1 + @test_throws UndefKeywordError f34516() + @test_throws UndefKeywordError f34516(1) + g34516(@nospecialize(x); k=0) = 0 + @test first(methods(Core.kwfunc(g34516))).nospecialize != 0 +end @testset "issue #21518" begin a = 0 f21518(;kw=nothing) = kw From 2a7f152d27bf2d60a9ea4f93b26edc747f66c4c3 Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Wed, 27 May 2020 02:48:27 +0800 Subject: [PATCH 045/232] Add detailed machine-file format from getting-started.md (#36020) --- doc/src/manual/distributed-computing.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/src/manual/distributed-computing.md b/doc/src/manual/distributed-computing.md index a1f30e579b5ec..550dd6ed5f57d 100644 --- a/doc/src/manual/distributed-computing.md +++ b/doc/src/manual/distributed-computing.md @@ -236,7 +236,11 @@ The base Julia installation has in-built support for two types of clusters: * A local cluster specified with the `-p` option as shown above. * A cluster spanning machines using the `--machine-file` option. This uses a passwordless `ssh` login - to start Julia worker processes (from the same path as the current host) on the specified machines. + to start Julia worker processes (from the same path as the current host) on the specified machines. Each machine definition + takes the form `[count*][user@]host[:port] [bind_addr[:port]]`. `user` defaults to current user, + `port` to the standard ssh port. `count` is the number of workers to spawn on the node, and defaults + to 1. The optional `bind-to bind_addr[:port]` specifies the IP address and port that other workers + should use to connect to this worker. Functions [`addprocs`](@ref), [`rmprocs`](@ref), [`workers`](@ref), and others are available as a programmatic means of adding, removing and querying the processes in a cluster. From 3995a02e14db043ee337676cca00c9f3b55e1518 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Tue, 26 May 2020 14:52:34 -0500 Subject: [PATCH 046/232] Handle overflow correctly with DateTime ranges (#17633) --- stdlib/Dates/src/ranges.jl | 8 ++++++-- stdlib/Dates/test/ranges.jl | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/stdlib/Dates/src/ranges.jl b/stdlib/Dates/src/ranges.jl index 1e28afc88715e..018a3a46fd8b5 100644 --- a/stdlib/Dates/src/ranges.jl +++ b/stdlib/Dates/src/ranges.jl @@ -11,8 +11,12 @@ guess(a::Date, b::Date, c) = Int64(div(value(b - a), days(c))) len(a::Time, b::Time, c) = Int64(div(value(b - a), tons(c))) function len(a, b, c) lo, hi, st = min(a, b), max(a, b), abs(c) - i = guess(a, b, c) - 1 - while lo + st * i <= hi + i = guess(a, b, c) + v = lo + st * i + prev = v # Ensure `v` does not overflow + while v <= hi && prev <= v + prev = v + v += st i += 1 end return i - 1 diff --git a/stdlib/Dates/test/ranges.jl b/stdlib/Dates/test/ranges.jl index f10350c3cb6f5..6eb6371376867 100644 --- a/stdlib/Dates/test/ranges.jl +++ b/stdlib/Dates/test/ranges.jl @@ -582,4 +582,18 @@ a = Dates.Time(23, 1, 1) hash([Date("2018-1-03"), Date("2018-1-04"), Date("2018-1-05")]) == hash(Date("2018-1-03"):Day(1):Date("2018-1-05")) +@testset "range overflow" begin + # DateTime ranges interactions with overflow. If not handled correctly `Dates.len` could + # infinite loop + @test length(DateTime(0):typemax(Millisecond):DateTime(0)) == 1 + @test length(typemax(DateTime):typemax(Millisecond):typemax(DateTime)) == 1 + + # Overflow interaction is easier to comprehend with using UTM extremes + utm_typemin = DateTime(Dates.UTM(typemin(Int64))) + utm_typemax = DateTime(Dates.UTM(typemax(Int64))) + + @test length(utm_typemax:Millisecond(1):utm_typemax) == 1 + @test length(utm_typemin:-Millisecond(1):utm_typemin) == 1 end + +end # RangesTest module From 806dfca07d251e9d165b73d5d0d4aff59b7187cf Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 27 May 2020 09:21:33 -0400 Subject: [PATCH 047/232] fix `readavailable` for `IOStream` and improve its docstring (#36036) fix #36004, fix #16821 --- base/io.jl | 9 +++++++-- base/iostream.jl | 4 ++++ test/iostream.jl | 10 ++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/base/io.jl b/base/io.jl index 178df80f30796..61c686a38529b 100644 --- a/base/io.jl +++ b/base/io.jl @@ -68,8 +68,13 @@ function bytesavailable end """ readavailable(stream) -Read all available data on the stream, blocking the task only if no data is available. The -result is a `Vector{UInt8}`. +Read available buffered data from a stream. Actual I/O is performed only if no +data has already been buffered. The result is a `Vector{UInt8}`. + +!!! warning + The amount of data returned is implementation-dependent; for example it can +depend on the internal choice of buffer size. Other functions such as [`read`](@ref) +should generally be used instead. """ function readavailable end diff --git a/base/iostream.jl b/base/iostream.jl index f7cec7a329a57..fba5221b4fe91 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -379,6 +379,10 @@ bytesavailable(s::IOStream) = @_lock_ios s ccall(:jl_nb_available, Int32, (Ptr{C function readavailable(s::IOStream) lock(s.lock) nb = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) + if nb == 0 + ccall(:ios_fillbuf, Cssize_t, (Ptr{Cvoid},), s.ios) + nb = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) + end a = Vector{UInt8}(undef, nb) nr = ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, a, nb) if nr != nb diff --git a/test/iostream.jl b/test/iostream.jl index 932af3cf100a1..60af3c1605eb1 100644 --- a/test/iostream.jl +++ b/test/iostream.jl @@ -120,3 +120,13 @@ end @test peek(file) == 0x4c end end + +@testset "issue #36004" begin + f = tempname() + open(f, "w") do io + write(io, "test") + end + open(f, "r") do io + @test length(readavailable(io)) > 0 + end +end From 9559efc735a4772dc95c4176094fbc443202c88f Mon Sep 17 00:00:00 2001 From: Shan Sikdar Date: Wed, 27 May 2020 09:24:15 -0400 Subject: [PATCH 048/232] change uuid to use Random.RandomDevice() instead of Random.default_rng() (#35872) Co-authored-by: Rafael Fourquet --- NEWS.md | 3 +++ stdlib/UUIDs/src/UUIDs.jl | 26 ++++++++++++++++++++++---- stdlib/UUIDs/test/runtests.jl | 8 ++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9bb3fc7e43f3e..f95768f41a5a0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -74,6 +74,9 @@ Standard library changes #### Distributed +#### UUIDs +* Change `uuid1` and `uuid4` to use `Random.RandomDevice()` as default random number generator ([#35872]). + Deprecated or removed --------------------- diff --git a/stdlib/UUIDs/src/UUIDs.jl b/stdlib/UUIDs/src/UUIDs.jl index ee2064605c7c8..7fb83a393be00 100644 --- a/stdlib/UUIDs/src/UUIDs.jl +++ b/stdlib/UUIDs/src/UUIDs.jl @@ -36,12 +36,21 @@ const namespace_oid = UUID(0x6ba7b8129dad11d180b400c04fd430c8) # 6ba7b812-9dad- const namespace_x500 = UUID(0x6ba7b8149dad11d180b400c04fd430c8) # 6ba7b814-9dad-11d1-80b4-00c04fd430c8 """ - uuid1([rng::AbstractRNG=GLOBAL_RNG]) -> UUID + uuid1([rng::AbstractRNG]) -> UUID Generates a version 1 (time-based) universally unique identifier (UUID), as specified by RFC 4122. Note that the Node ID is randomly generated (does not identify the host) according to section 4.5 of the RFC. +The default rng used by `uuid1` is not `GLOBAL_RNG` and every invocation of `uuid1()` without +an argument should be expected to return a unique identifier. Importantly, the outputs of +`uuid1` do not repeat even when `Random.seed!(seed)` is called. Currently (as of Julia 1.6), +`uuid1` uses `Random.RandomDevice` as the default rng. However, this is an implementation +detail that may change in the future. + +!!! compat "Julia 1.6" + The output of `uuid1` does not depend on `GLOBAL_RNG` as of Julia 1.6. + # Examples ```jldoctest; filter = r"[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}" julia> rng = MersenneTwister(1234); @@ -50,7 +59,7 @@ julia> uuid1(rng) UUID("cfc395e8-590f-11e8-1f13-43a2532b2fa8") ``` """ -function uuid1(rng::AbstractRNG=Random.default_rng()) +function uuid1(rng::AbstractRNG=Random.RandomDevice()) u = rand(rng, UInt128) # mask off clock sequence and node @@ -74,11 +83,20 @@ function uuid1(rng::AbstractRNG=Random.default_rng()) end """ - uuid4([rng::AbstractRNG=GLOBAL_RNG]) -> UUID + uuid4([rng::AbstractRNG]) -> UUID Generates a version 4 (random or pseudo-random) universally unique identifier (UUID), as specified by RFC 4122. +The default rng used by `uuid4` is not `GLOBAL_RNG` and every invocation of `uuid4()` without +an argument should be expected to return a unique identifier. Importantly, the outputs of +`uuid4` do not repeat even when `Random.seed!(seed)` is called. Currently (as of Julia 1.6), +`uuid4` uses `Random.RandomDevice` as the default rng. However, this is an implementation +detail that may change in the future. + +!!! compat "Julia 1.6" + The output of `uuid4` does not depend on `GLOBAL_RNG` as of Julia 1.6. + # Examples ```jldoctest julia> rng = MersenneTwister(1234); @@ -87,7 +105,7 @@ julia> uuid4(rng) UUID("196f2941-2d58-45ba-9f13-43a2532b2fa8") ``` """ -function uuid4(rng::AbstractRNG=Random.default_rng()) +function uuid4(rng::AbstractRNG=Random.RandomDevice()) u = rand(rng, UInt128) u &= 0xffffffffffff0fff3fffffffffffffff u |= 0x00000000000040008000000000000000 diff --git a/stdlib/UUIDs/test/runtests.jl b/stdlib/UUIDs/test/runtests.jl index 8258ac3f27d68..483b7f1d56938 100644 --- a/stdlib/UUIDs/test/runtests.jl +++ b/stdlib/UUIDs/test/runtests.jl @@ -54,3 +54,11 @@ for (init_uuid, next_uuid) in standard_namespace_uuids result = uuid5(init_uuid, "julia") @test next_uuid == result end + +# Issue 35860 +Random.seed!(Random.GLOBAL_RNG, 10) +u1 = uuid1() +u4 = uuid4() +Random.seed!(Random.GLOBAL_RNG, 10) +@test u1 != uuid1() +@test u4 != uuid4() From fb9be71efb2875929bb63437f29c5e93e30c7764 Mon Sep 17 00:00:00 2001 From: Will Shand <17100608+kernelmethod@users.noreply.github.com> Date: Wed, 27 May 2020 09:00:16 -0600 Subject: [PATCH 049/232] Add clearer documentation for asynchronous I/O to the manual. (#35412) This adds a section to the end of the "networking and streams" page that explicitly mentions asynchronous I/O and provides a couple of code examples using the @sync / @async macros. It uses a Sockets-based example for demonstrating the usage of the @sync / @async macros, since it does a better job of demonstrating the asynchronous nature of I/O functions. --- doc/src/manual/networking-and-streams.md | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/doc/src/manual/networking-and-streams.md b/doc/src/manual/networking-and-streams.md index e5bd1d0788a27..163716c583804 100644 --- a/doc/src/manual/networking-and-streams.md +++ b/doc/src/manual/networking-and-streams.md @@ -312,3 +312,42 @@ resolution: julia> getaddrinfo("google.com") ip"74.125.226.225" ``` + +## Asynchronous I/O + + +All I/O operations exposed by [`Base.read`](@ref) and [`Base.write`](@ref) can be performed +asynchronously through the use of [coroutines](@ref man-tasks). You can create a new coroutine to +read from or write to a stream using the [`@async`](@ref) macro: + +```julia-repl +julia> task = @async open("foo.txt", "w") do io + write(io, "Hello, World!") + end; + +julia> wait(task) + +julia> readlines("foo.txt") +1-element Array{String,1}: + "Hello, World!" +``` + +It's common to run into situations where you want to perform multiple asynchronous operations +concurrently and wait until they've all completed. You can use the [`@sync`](@ref) macro to cause +your program to block until all of the coroutines it wraps around have exited: + +```julia-repl +julia> using Sockets + +julia> @sync for hostname in ("google.com", "github.com", "julialang.org") + @async begin + conn = connect(hostname, 80) + write(conn, "GET / HTTP/1.1\r\nHost:$(hostname)\r\n\r\n") + readline(conn, keep=true) + println("Finished connection to $(hostname)") + end + end +Finished connection to google.com +Finished connection to julialang.org +Finished connection to github.com +``` From 9a431f3afb8246a1c82f090281115690245535e9 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 27 May 2020 08:02:04 -0700 Subject: [PATCH 050/232] Fix env handling in Cmd (#36039) Matches behaviour in docs, adds test, fixes #32454. --- base/cmd.jl | 2 +- test/ambiguous.jl | 2 +- test/spawn.jl | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/base/cmd.jl b/base/cmd.jl index 4890af1c4c7cd..2728905e4cbb8 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -223,7 +223,7 @@ byteenv(env::AbstractArray{<:AbstractString}) = byteenv(env::AbstractDict) = String[cstr(string(k)*"="*string(v)) for (k,v) in env] byteenv(env::Nothing) = nothing -byteenv(env::Union{AbstractVector{Pair{T}}, Tuple{Vararg{Pair{T}}}}) where {T<:AbstractString} = +byteenv(env::Union{AbstractVector{Pair{T,V}}, Tuple{Vararg{Pair{T,V}}}}) where {T<:AbstractString,V} = String[cstr(k*"="*string(v)) for (k,v) in env] """ diff --git a/test/ambiguous.jl b/test/ambiguous.jl index d68d3334e5a50..c9f2d74d9fbdb 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -312,7 +312,7 @@ end pop!(need_to_handle_undef_sparam, first(methods(Base.same_names))) @test_broken need_to_handle_undef_sparam == Set() pop!(need_to_handle_undef_sparam, which(Base._cat, Tuple{Any, AbstractArray})) - pop!(need_to_handle_undef_sparam, which(Base.byteenv, (Union{AbstractArray{Pair{T}, 1}, Tuple{Vararg{Pair{T}}}} where T<:AbstractString,))) + pop!(need_to_handle_undef_sparam, which(Base.byteenv, (Union{AbstractArray{Pair{T,V}, 1}, Tuple{Vararg{Pair{T,V}}}} where {T<:AbstractString,V},))) pop!(need_to_handle_undef_sparam, which(Base._cat, (Any, SparseArrays._TypedDenseConcatGroup{T} where T))) pop!(need_to_handle_undef_sparam, which(Base.float, Tuple{AbstractArray{Union{Missing, T},N} where {T, N}})) pop!(need_to_handle_undef_sparam, which(Base.zero, Tuple{Type{Union{Missing, T}} where T})) diff --git a/test/spawn.jl b/test/spawn.jl index fc59d02c6b930..09648e7e02806 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -456,6 +456,13 @@ end @test Set([``, echocmd]) != Set([``, ``]) @test Set([echocmd, ``, ``, echocmd]) == Set([echocmd, ``]) +# env handling (#32454) +@test Cmd(`foo`, env=Dict("A"=>true)).env == ["A=true"] +@test Cmd(`foo`, env=["A=true"]).env == ["A=true"] +@test Cmd(`foo`, env=("A"=>true,)).env == ["A=true"] +@test Cmd(`foo`, env=["A"=>true]).env == ["A=true"] +@test Cmd(`foo`, env=nothing).env == nothing + # test for interpolation of Cmd let c = setenv(`x`, "A"=>true) @test (`$c a`).env == String["A=true"] From d0b2be14687ce58a45d82c46886fbdc56c31907f Mon Sep 17 00:00:00 2001 From: Irhum Shafkat <13271155+irhum@users.noreply.github.com> Date: Wed, 27 May 2020 22:33:00 +0600 Subject: [PATCH 051/232] Faster blockdiag for uniform input value-index types (#36013) --- stdlib/SparseArrays/src/sparsematrix.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 4b64362f6d3e4..6cb72428247e3 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -3318,16 +3318,25 @@ julia> blockdiag(sparse(2I, 3, 3), sparse(4I, 2, 2)) ⋅ ⋅ ⋅ ⋅ 4 ``` """ +blockdiag() = spzeros(promote_type(), Int, 0, 0) + +function blockdiag(X::AbstractSparseMatrixCSC{Tv, Ti}...) where {Tv, Ti <: Integer} + _blockdiag(Tv, Ti, X...) +end + function blockdiag(X::AbstractSparseMatrixCSC...) + Tv = promote_type(map(x->eltype(nonzeros(x)), X)...) + Ti = promote_type(map(x->eltype(rowvals(x)), X)...) + _blockdiag(Tv, Ti, X...) +end + +function _blockdiag(::Type{Tv}, ::Type{Ti}, X::AbstractSparseMatrixCSC...) where {Tv, Ti <: Integer} num = length(X) mX = Int[ size(x, 1) for x in X ] nX = Int[ size(x, 2) for x in X ] m = sum(mX) n = sum(nX) - Tv = promote_type(map(x->eltype(nonzeros(x)), X)...) - Ti = isempty(X) ? Int : promote_type(map(x->eltype(rowvals(x)), X)...) - colptr = Vector{Ti}(undef, n+1) nnzX = Int[ nnz(x) for x in X ] nnz_res = sum(nnzX) From 052315f3776e1daf419c473c8bbbbb5029029e69 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 27 May 2020 14:54:51 -0400 Subject: [PATCH 052/232] show failed Tasks using the same style as `TaskFailedException` (#35967) It doesn't really make sense to construct a `CapturedException` just to show a Task. --- base/show.jl | 2 +- base/task.jl | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/base/show.jl b/base/show.jl index 354c76c8da615..17aa231a2b5c3 100644 --- a/base/show.jl +++ b/base/show.jl @@ -204,7 +204,7 @@ function show(io::IO, ::MIME"text/plain", t::Task) show(io, t) if t.state === :failed println(io) - showerror(io, CapturedException(t.result, t.backtrace)) + show_task_exception(io, t) end end diff --git a/base/task.jl b/base/task.jl index b717a31bdb156..32ad88be032c5 100644 --- a/base/task.jl +++ b/base/task.jl @@ -67,17 +67,19 @@ struct TaskFailedException <: Exception end function showerror(io::IO, ex::TaskFailedException) + println(io, "TaskFailedException:") + show_task_exception(io, ex.task) +end + +function show_task_exception(io::IO, t::Task) stacks = [] - while isa(ex.task.exception, TaskFailedException) - pushfirst!(stacks, ex.task.backtrace) - ex = ex.task.exception + while isa(t.exception, TaskFailedException) + pushfirst!(stacks, t.backtrace) + t = t.exception.task end - println(io, "TaskFailedException:") - showerror(io, ex.task.exception, ex.task.backtrace) - if !isempty(stacks) - for bt in stacks - show_backtrace(io, bt) - end + showerror(io, t.exception, t.backtrace) + for bt in stacks + show_backtrace(io, bt) end end From ebd4991a1dd321b4851c04a93de8568ad919ef0d Mon Sep 17 00:00:00 2001 From: KristofferC Date: Wed, 27 May 2020 22:24:53 +0200 Subject: [PATCH 053/232] microoptimize UUID(::AbstractString) --- base/parse.jl | 29 +++++++++--------- base/uuid.jl | 55 ++++++++++++++++++++++++----------- stdlib/UUIDs/test/runtests.jl | 11 +++++++ 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/base/parse.jl b/base/parse.jl index bfbcafb1fe8d2..1097e8a19b804 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -89,6 +89,20 @@ function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos: return sgn, base, j end +@inline function __convert_digit(_c::UInt32, base) + _0 = UInt32('0') + _9 = UInt32('9') + _A = UInt32('A') + _a = UInt32('a') + _Z = UInt32('Z') + _z = UInt32('z') + a::UInt32 = base <= 36 ? 10 : 36 + d = _0 <= _c <= _9 ? _c-_0 : + _A <= _c <= _Z ? _c-_A+ UInt32(10) : + _a <= _c <= _z ? _c-_a+a : UInt32(base) +end + + function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) where T<:Integer sgn, base, i = parseint_preamble(T<:Signed, Int(base_), s, startpos, endpos) if sgn == 0 && base == 0 && i == 0 @@ -115,19 +129,10 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: base == 16 ? div(typemax(T) - T(15), T(16)) : div(typemax(T) - base + 1, base) n::T = 0 - a::Int = base <= 36 ? 10 : 36 - _0 = UInt32('0') - _9 = UInt32('9') - _A = UInt32('A') - _a = UInt32('a') - _Z = UInt32('Z') - _z = UInt32('z') while n <= m # Fast path from `UInt32(::Char)`; non-ascii will be >= 0x80 _c = reinterpret(UInt32, c) >> 24 - d::T = _0 <= _c <= _9 ? _c-_0 : - _A <= _c <= _Z ? _c-_A+ UInt32(10) : - _a <= _c <= _z ? _c-_a+a : base + d::T = __convert_digit(_c, base) if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) return nothing @@ -145,9 +150,7 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: while !isspace(c) # Fast path from `UInt32(::Char)`; non-ascii will be >= 0x80 _c = reinterpret(UInt32, c) >> 24 - d::T = _0 <= _c <= _9 ? _c-_0 : - _A <= _c <= _Z ? _c-_A+ UInt32(10) : - _a <= _c <= _z ? _c-_a+a : base + d::T = __convert_digit(_c, base) if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) return nothing diff --git a/base/uuid.jl b/base/uuid.jl index c72bfd9dff500..eb997fc5d2cac 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -30,24 +30,45 @@ end UInt128(u::UUID) = u.value -let groupings = [1:8; 10:13; 15:18; 20:23; 25:36] - global UUID - function UUID(s::AbstractString) - s = lowercase(s) - - if !occursin(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$", s) - throw(ArgumentError("Malformed UUID string: $(repr(s))")) - end +let +@noinline throw_malformed_uuid(s) = throw(ArgumentError("Malformed UUID string: $(repr(s))")) +@inline function uuid_kernel(s, i, u) + _c = UInt32(@inbounds codeunit(s, i)) + d = __convert_digit(_c, UInt32(16)) + d >= 16 && throw_malformed_uuid(s) + u <<= 4 + u |= d +end - u = UInt128(0) - for i in groupings - u <<= 4 - d = s[i] - '0' - u |= 0xf & (d - 39*(d > 9)) - end - return UUID(u) +global UUID +function UUID(s::AbstractString) + u = UInt128(0) + ncodeunits(s) != 36 && throw_malformed_uuid(s) + for i in 1:8 + u = uuid_kernel(s, i, u) + end + @inbounds codeunit(s, 9) == UInt8('-') || @goto error + for i in 10:13 + u = uuid_kernel(s, i, u) end + @inbounds codeunit(s, 14) == UInt8('-') || @goto error + for i in 15:18 + u = uuid_kernel(s, i, u) + end + @inbounds codeunit(s, 19) == UInt8('-') || @goto error + for i in 20:23 + u = uuid_kernel(s, i, u) + end + @inbounds codeunit(s, 24) == UInt8('-') || @goto error + for i in 25:36 + u = uuid_kernel(s, i, u) + end + return Base.UUID(u) + @label error + throw_malformed_uuid(s) end +end + let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] global string @@ -55,10 +76,10 @@ let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] u = u.value a = Base.StringVector(36) for i in groupings - a[i] = hex_chars[1 + u & 0xf] + @inbounds a[i] = hex_chars[1 + u & 0xf] u >>= 4 end - a[24] = a[19] = a[14] = a[9] = '-' + @inbounds a[24] = a[19] = a[14] = a[9] = '-' return String(a) end end diff --git a/stdlib/UUIDs/test/runtests.jl b/stdlib/UUIDs/test/runtests.jl index 483b7f1d56938..5085fa33e8573 100644 --- a/stdlib/UUIDs/test/runtests.jl +++ b/stdlib/UUIDs/test/runtests.jl @@ -62,3 +62,14 @@ u4 = uuid4() Random.seed!(Random.GLOBAL_RNG, 10) @test u1 != uuid1() @test u4 != uuid4() + +@test_throws ArgumentError UUID("22b4a8a1ae548-4eeb-9270-60426d66a48e") +@test_throws ArgumentError UUID("22b4a8a1-e548a4eeb-9270-60426d66a48e") +@test_throws ArgumentError UUID("22b4a8a1-e548-4eeba9270-60426d66a48e") +@test_throws ArgumentError UUID("22b4a8a1-e548-4eeb-9270a60426d66a48e") +str = "22b4a8a1-e548-4eeb-9270-60426d66a48e" +@test UUID(uppercase(str)) == UUID(str) + +for r in rand(UInt128, 10^3) + @test UUID(r) == UUID(string(UUID(r))) +end From ddc65bb898486acd113640aa1af42f7e6a8ec02f Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 27 May 2020 14:03:50 -0700 Subject: [PATCH 054/232] BigInt printf fixes Relative and octal printing used incorrect arguments. Fixes #36031. --- stdlib/Printf/src/Printf.jl | 19 ++++++++++--------- stdlib/Printf/test/runtests.jl | 5 ++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index ee40edd32c8b8..2fd98b7a4959e 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -942,10 +942,9 @@ function decode_0ct(x::BigInt, digits) pt = Base.ndigits0z(x, 8) + 1 length(digits) < pt+1 && resize!(digits, pt+1) neg && (x.size = -x.size) - p = convert(Ptr{UInt8}, digits) + 1 - GMP.MPZ.get_str!(p, 8, x) + GMP.MPZ.get_str!(pointer(digits, 2), 8, x) neg && (x.size = -x.size) - return neg, Int32(pt), Int32(pt) + return Int32(pt), Int32(pt), neg end ### decoding functions directly used by printf generated code ### @@ -1059,13 +1058,15 @@ function ini_dec(x::BigInt, n::Int, digits) end d = Base.ndigits0z(x) if d <= n - info = decode_dec(x) - d == n && return info - p = convert(Ptr{Cvoid}, digits) + info[2] - ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), p, '0', n - info[2]) - return info + len,pt,neg = decode_dec(x, digits) + if d < n + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), pointer(digits, pt+1), '0', n - pt) + end + return (len,pt,neg) + else + _,_,neg = decode_dec(round(BigInt,x/big(10)^(d-n)), digits) + return (n, d, neg) end - return (n, d, decode_dec(round(BigInt,x/big(10)^(d-n)))[3]) end diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index e2b1e9ff4962d..562d478ef4676 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -39,11 +39,10 @@ for (fmt, val) in (("%i", "42"), ("%20a"," 0x2.ap+4"), ("%-20a","0x2.ap+4 "), ("%f", "42.000000"), - ("%g", "42")), + ("%g", "42"), + ("%e", "4.200000e+01")), num in (UInt16(42), UInt32(42), UInt64(42), UInt128(42), Int16(42), Int32(42), Int64(42), Int128(42), big"42") - #big"42" causes stack overflow on %a ; gh #14409 - num isa BigInt && fmt in ["%a", "%#o", "%g"] && continue @test @eval(@sprintf($fmt, $num) == $val) end From adf6d521afd125f2de12a3209b342148d78981c2 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 27 May 2020 14:07:08 -0700 Subject: [PATCH 055/232] Revert "BigInt printf fixes" This reverts commit ddc65bb898486acd113640aa1af42f7e6a8ec02f. --- stdlib/Printf/src/Printf.jl | 19 +++++++++---------- stdlib/Printf/test/runtests.jl | 5 +++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 2fd98b7a4959e..ee40edd32c8b8 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -942,9 +942,10 @@ function decode_0ct(x::BigInt, digits) pt = Base.ndigits0z(x, 8) + 1 length(digits) < pt+1 && resize!(digits, pt+1) neg && (x.size = -x.size) - GMP.MPZ.get_str!(pointer(digits, 2), 8, x) + p = convert(Ptr{UInt8}, digits) + 1 + GMP.MPZ.get_str!(p, 8, x) neg && (x.size = -x.size) - return Int32(pt), Int32(pt), neg + return neg, Int32(pt), Int32(pt) end ### decoding functions directly used by printf generated code ### @@ -1058,15 +1059,13 @@ function ini_dec(x::BigInt, n::Int, digits) end d = Base.ndigits0z(x) if d <= n - len,pt,neg = decode_dec(x, digits) - if d < n - ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), pointer(digits, pt+1), '0', n - pt) - end - return (len,pt,neg) - else - _,_,neg = decode_dec(round(BigInt,x/big(10)^(d-n)), digits) - return (n, d, neg) + info = decode_dec(x) + d == n && return info + p = convert(Ptr{Cvoid}, digits) + info[2] + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), p, '0', n - info[2]) + return info end + return (n, d, decode_dec(round(BigInt,x/big(10)^(d-n)))[3]) end diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index 562d478ef4676..e2b1e9ff4962d 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -39,10 +39,11 @@ for (fmt, val) in (("%i", "42"), ("%20a"," 0x2.ap+4"), ("%-20a","0x2.ap+4 "), ("%f", "42.000000"), - ("%g", "42"), - ("%e", "4.200000e+01")), + ("%g", "42")), num in (UInt16(42), UInt32(42), UInt64(42), UInt128(42), Int16(42), Int32(42), Int64(42), Int128(42), big"42") + #big"42" causes stack overflow on %a ; gh #14409 + num isa BigInt && fmt in ["%a", "%#o", "%g"] && continue @test @eval(@sprintf($fmt, $num) == $val) end From 0ad6dcfe66bdf27fc22ffef2c0c6813d9528fe5a Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Thu, 28 May 2020 09:10:46 -0400 Subject: [PATCH 056/232] Add a pointer to GPUs in parallel computing (#36043) * Add a pointer to GPUs * Update parallel-computing.md * Update parallel-computing.md * Update parallel-computing.md * Update doc/src/manual/parallel-computing.md Co-authored-by: Dilum Aluthge * whitespace fix * Update parallel-computing.md * whitespace fix * whitespace fix * Update doc/src/manual/parallel-computing.md Co-authored-by: Jonas Schulze Co-authored-by: Dilum Aluthge Co-authored-by: Jonas Schulze --- doc/src/manual/parallel-computing.md | 42 ++++++++++++++++++---------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 3e4a48d7a9c16..71c3ba5354c1f 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -1,20 +1,34 @@ # Parallel Computing -Julia supports three main categories of features for concurrent and parallel programming: +Julia supports these four categories of concurrent and parallel programming: -1. Asynchronous "tasks", or coroutines -2. Multi-threading -3. Distributed computing +1. **Asynchronous "tasks", or coroutines**: -Julia Tasks allow suspending and resuming computations -for I/O, event handling, producer-consumer processes, and similar patterns. -Tasks can synchronize through operations like [`wait`](@ref) and [`fetch`](@ref), and -communicate via [`Channel`](@ref)s. + Julia Tasks allow suspending and resuming computations + for I/O, event handling, producer-consumer processes, and similar patterns. + Tasks can synchronize through operations like [`wait`](@ref) and [`fetch`](@ref), and + communicate via [`Channel`](@ref)s. While strictly not parallel computing by themselves, + Julia lets you schedule `Task`s on several threads. -Multi-threading functionality builds on tasks by allowing them to run simultaneously -on more than one thread or CPU core, sharing memory. +2. **Multi-threading**: -Finally, distributed computing runs multiple processes with separate memory spaces, -potentially on different machines. -This functionality is provided by the `Distributed` standard library as well as -external packages like `MPI.jl` and `DistributedArrays.jl`. + Julia's [multi-threading](@ref man-multithreading) provides the ability to schedule Tasks + simultaneously on more than one thread or CPU core, sharing memory. This is usually the easiest way + to get parallelism on one's PC or on a single large multi-core server. Julia's multi-threading + is composable. When one multi-threaded function calls another multi-threaded function, Julia + will schedule all the threads globally on available resources, without oversubscribing. + +3. **Distributed computing**: + + Distributed computing runs multiple Julia processes with separate memory spaces. These can be on the same + computer or multiple computers. The `Distributed` standard library provides the capability for remote execution + of a Julia function. With this basic building block, it is possible to build many different kinds of + distributed computing abstractions. Packages like [`DistributedArrays.jl`](https://github.com/JuliaParallel/DistributedArrays.jl) + are an example of such an abstraction. On the other hand, packages like [`MPI.jl`](https://github.com/JuliaParallel/MPI.jl) and + [`Elemental.jl`](https://github.com/JuliaParallel/Elemental.jl) provide access to the existing MPI ecosystem of libraries. + +4. **GPU computing**: + + The Julia GPU compiler provides the ability to run Julia code natively on GPUs. There + is a rich ecosystem of Julia packages that target GPUs. The [JuliaGPU.org](https://juliagpu.org) + website provides a list of capabilities, supported GPUs, related packages and documentation. From 8068e7651616bf44c0ce3912e29809af66f2d062 Mon Sep 17 00:00:00 2001 From: nathanrboyer <65452054+nathanrboyer@users.noreply.github.com> Date: Thu, 28 May 2020 09:14:10 -0400 Subject: [PATCH 057/232] Add arrows to the punctuation list. (#35990) * Add arrow punctuation to the list. My first time using Github. I don't know what I'm doing. * Update punctuation.md * Update doc/src/base/punctuation.md Thanks for your help! Co-authored-by: Matt Bauman * Update doc/src/base/punctuation.md Co-authored-by: Matt Bauman * Removed all end of line periods for consistency. Co-authored-by: Matt Bauman --- doc/src/base/punctuation.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/src/base/punctuation.md b/doc/src/base/punctuation.md index 50e4161947a28..c91df5e3f2ea6 100644 --- a/doc/src/base/punctuation.md +++ b/doc/src/base/punctuation.md @@ -12,7 +12,7 @@ Extended documentation for mathematical symbols & functions is [here](@ref math- | `=#` | end a multi-line comment by immediately preceding the number sign with an equals sign | | `$` | the dollar sign is used for [string](@ref string-interpolation) and [expression](@ref man-expression-interpolation) interpolation | | [`%`](@ref rem) | the percent symbol is the remainder operator | -| [`^`](@ref) | the caret is the exponentiation operator. | +| [`^`](@ref) | the caret is the exponentiation operator | | [`&`](@ref) | single ampersand is bitwise and | | [`&&`](@ref)| double ampersands is short-circuiting boolean and | | [`\|`](@ref)| single pipe character is bitwise or | @@ -53,4 +53,6 @@ Extended documentation for mathematical symbols & functions is [here](@ref math- | [`>:`](@ref)| supertype operator (reverse of subtype operator) | | `=` | single equals sign is [assignment](@ref man-variables) | | [`==`](@ref)| double equals sign is value equality comparison | -| [`===`](@ref) | triple equals sign is programmatically identical equality comparison. | +| [`===`](@ref) | triple equals sign is programmatically identical equality comparison | +| [`=>`](@ref Pair) | right arrow using an equals sign defines a [`Pair`](@ref) typically used to populate [dictionaries](@ref Dictionaries) | +| `->` | right arrow using a hyphen defines an [anonymous function](@ref man-anonymous-functions) on a single line | From 150311f89d9e4bdb683d4bba397c822fabab272a Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Thu, 28 May 2020 15:55:03 +0200 Subject: [PATCH 058/232] fix #36031: Printf bug for BigInt (#36033) * fix #36031 * Apply suggestions from code review Co-authored-by: Simon Byrne --- stdlib/Printf/src/Printf.jl | 23 ++++++++++++++--------- stdlib/Printf/test/runtests.jl | 5 ++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index ee40edd32c8b8..ac4d3d4e7dff3 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -942,10 +942,12 @@ function decode_0ct(x::BigInt, digits) pt = Base.ndigits0z(x, 8) + 1 length(digits) < pt+1 && resize!(digits, pt+1) neg && (x.size = -x.size) - p = convert(Ptr{UInt8}, digits) + 1 - GMP.MPZ.get_str!(p, 8, x) + GC.@preserve digits begin + p = pointer(digits,2) + GMP.MPZ.get_str!(p, 8, x) + end neg && (x.size = -x.size) - return neg, Int32(pt), Int32(pt) + return Int32(pt), Int32(pt), neg end ### decoding functions directly used by printf generated code ### @@ -1059,13 +1061,16 @@ function ini_dec(x::BigInt, n::Int, digits) end d = Base.ndigits0z(x) if d <= n - info = decode_dec(x) - d == n && return info - p = convert(Ptr{Cvoid}, digits) + info[2] - ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), p, '0', n - info[2]) - return info + len,pt,neg = decode_dec(x, digits) + d == n && return (len,pt,neg) + + GC.@preserve digits begin + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), pointer(digits, pt+1), '0', n - pt) + end + return (len,pt,neg) end - return (n, d, decode_dec(round(BigInt,x/big(10)^(d-n)))[3]) + _, _, neg = decode_dec(round(BigInt,x/big(10)^(d-n)), digits) + return (n, d, neg) end diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index e2b1e9ff4962d..562d478ef4676 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -39,11 +39,10 @@ for (fmt, val) in (("%i", "42"), ("%20a"," 0x2.ap+4"), ("%-20a","0x2.ap+4 "), ("%f", "42.000000"), - ("%g", "42")), + ("%g", "42"), + ("%e", "4.200000e+01")), num in (UInt16(42), UInt32(42), UInt64(42), UInt128(42), Int16(42), Int32(42), Int64(42), Int128(42), big"42") - #big"42" causes stack overflow on %a ; gh #14409 - num isa BigInt && fmt in ["%a", "%#o", "%g"] && continue @test @eval(@sprintf($fmt, $num) == $val) end From 39fc4eec54fc97efda268c9975348121d0d225f5 Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Thu, 28 May 2020 12:58:41 -0400 Subject: [PATCH 059/232] add `Threads.foreach` for convenient multithreaded Channel consumption (#34543) Co-authored-by: Takafumi Arakaki Co-authored-by: Alex Arslan Co-authored-by: Valentin Churavy --- NEWS.md | 4 ++- base/Base.jl | 1 + base/threadingconstructs.jl | 8 ++++++ base/threads_overloads.jl | 51 +++++++++++++++++++++++++++++++++++++ test/threads_exec.jl | 35 +++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 base/threads_overloads.jl diff --git a/NEWS.md b/NEWS.md index f95768f41a5a0..9966811f8e1c7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,7 +14,6 @@ Language changes Compiler/Runtime improvements ----------------------------- - * All platforms can now use `@executable_path` within `jl_load_dynamic_library()`. This allows executable-relative paths to be embedded within executables on all platforms, not just MacOS, which the syntax is borrowed from. ([#35627]) @@ -33,7 +32,9 @@ Build system changes New library functions --------------------- + * New function `Base.kron!` and corresponding overloads for various matrix types for performing Kronecker product in-place. ([#31069]). +* New function `Base.Threads.foreach(f, channel::Channel)` for multithreaded `Channel` consumption. ([#34543]). New library features -------------------- @@ -41,6 +42,7 @@ New library features Standard library changes ------------------------ + * The `nextprod` function now accepts tuples and other array types for its first argument ([#35791]). * The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]). * `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). diff --git a/base/Base.jl b/base/Base.jl index 9c1cbe735e4fd..777e07bd30715 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -223,6 +223,7 @@ include("threads.jl") include("lock.jl") include("channels.jl") include("task.jl") +include("threads_overloads.jl") include("weakkeydict.jl") # Logging diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index aa122044a7881..56c4cbb13db5c 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -180,3 +180,11 @@ macro spawn(expr) end end end + +# This is a stub that can be overloaded for downstream structures like `Channel` +function foreach end + +# Scheduling traits that can be employed for downstream overloads +abstract type AbstractSchedule end +struct StaticSchedule <: AbstractSchedule end +struct FairSchedule <: AbstractSchedule end diff --git a/base/threads_overloads.jl b/base/threads_overloads.jl new file mode 100644 index 0000000000000..3e6ad06760747 --- /dev/null +++ b/base/threads_overloads.jl @@ -0,0 +1,51 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Threads.foreach(f, channel::Channel; + schedule::Threads.AbstractSchedule=Threads.FairSchedule(), + ntasks=Threads.nthreads()) + +Similar to `foreach(f, channel)`, but iteration over `channel` and calls to +`f` are split across `ntasks` tasks spawned by `Threads.@spawn`. This function +will wait for all internally spawned tasks to complete before returning. + +If `schedule isa FairSchedule`, `Threads.foreach` will attempt to spawn tasks in a +manner that enables Julia's scheduler to more freely load-balance work items across +threads. This approach generally has higher per-item overhead, but may perform +better than `StaticSchedule` in concurrence with other multithreaded workloads. + +If `schedule isa StaticSchedule`, `Threads.foreach` will spawn tasks in a manner +that incurs lower per-item overhead than `FairSchedule`, but is less amenable +to load-balancing. This approach thus may be more suitable for fine-grained, +uniform workloads, but may perform worse than `FairSchedule` in concurrence +with other multithreaded workloads. + +!!! compat "Julia 1.6" + This function requires Julia 1.6 or later. +""" +function Threads.foreach(f, channel::Channel; + schedule::Threads.AbstractSchedule=Threads.FairSchedule(), + ntasks=Threads.nthreads()) + apply = _apply_for_schedule(schedule) + stop = Threads.Atomic{Bool}(false) + @sync for _ in 1:ntasks + Threads.@spawn try + for item in channel + $apply(f, item) + # do `stop[] && break` after `f(item)` to avoid losing `item`. + # this isn't super comprehensive since a task could still get + # stuck on `take!` at `for item in channel`. We should think + # about a more robust mechanism to avoid dropping items. See also: + # https://github.com/JuliaLang/julia/pull/34543#discussion_r422695217 + stop[] && break + end + catch + stop[] = true + rethrow() + end + end + return nothing +end + +_apply_for_schedule(::Threads.StaticSchedule) = (f, x) -> f(x) +_apply_for_schedule(::Threads.FairSchedule) = (f, x) -> wait(Threads.@spawn f(x)) diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 9022ce9f05ba0..691fca2fb2afa 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -845,3 +845,38 @@ fib34666(x) = f(x) end @test fib34666(25) == 75025 + +function jitter_channel(f, k, delay, ntasks, schedule) + x = Channel(ch -> foreach(i -> put!(ch, i), 1:k), 1) + y = Channel(k) do ch + g = i -> begin + iseven(i) && sleep(delay) + put!(ch, f(i)) + end + Threads.foreach(g, x; schedule=schedule, ntasks=ntasks) + end + return y +end + +@testset "Threads.foreach(f, ::Channel)" begin + k = 50 + delay = 0.01 + expected = sin.(1:k) + ordered_fair = collect(jitter_channel(sin, k, delay, 1, Threads.FairSchedule())) + ordered_static = collect(jitter_channel(sin, k, delay, 1, Threads.StaticSchedule())) + @test expected == ordered_fair + @test expected == ordered_static + + unordered_fair = collect(jitter_channel(sin, k, delay, 10, Threads.FairSchedule())) + unordered_static = collect(jitter_channel(sin, k, delay, 10, Threads.StaticSchedule())) + @test expected != unordered_fair + @test expected != unordered_static + @test Set(expected) == Set(unordered_fair) + @test Set(expected) == Set(unordered_static) + + ys = Channel() do ys + inner = Channel(xs -> foreach(i -> put!(xs, i), 1:3)) + Threads.foreach(x -> put!(ys, x), inner) + end + @test sort!(collect(ys)) == 1:3 +end From 7e60e1bf13cc94c21a7088eb528ba46463c5d033 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 28 May 2020 11:45:22 -0700 Subject: [PATCH 060/232] Update armv7l `-d16` -> `+d32` feature change for LLVM 9+ --- src/features_aarch32.h | 4 ++-- src/processor_arm.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features_aarch32.h b/src/features_aarch32.h index 803d576c61548..d160ea7464c42 100644 --- a/src/features_aarch32.h +++ b/src/features_aarch32.h @@ -4,11 +4,11 @@ // hwcap JL_FEATURE_DEF(neon, 12, 0) JL_FEATURE_DEF(vfp3, 13, 0) -// JL_FEATURE_DEF(vfpv3d16, 14, 0) // d16 +// JL_FEATURE_DEF(vfpv3d16, 14, 0) // -d32 JL_FEATURE_DEF(vfp4, 16, 0) JL_FEATURE_DEF_NAME(hwdiv_arm, 17, 0, "hwdiv-arm") JL_FEATURE_DEF(hwdiv, 18, 0) -JL_FEATURE_DEF(d32, 19, 0) // -d16 +JL_FEATURE_DEF(d32, 19, 0) // hwcap2 JL_FEATURE_DEF(crypto, 32 + 0, 0) diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index 96ca1789f5937..9ce1f2638acfd 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -1226,7 +1226,7 @@ get_llvm_target_noext(const TargetData &data) const char *fename_str = fename.name; bool enable = test_nbit(features, fename.bit); bool disable = test_nbit(data.dis.features, fename.bit); -#ifdef _CPU_ARM_ +#if defined(_CPU_ARM_) && JL_LLVM_VERSION < 90000 if (fename.bit == Feature::d32) { if (enable) { feature_strs.push_back("-d16"); From c974bc65f964cf838f1b6764dfc10e5f9545e1ed Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 28 May 2020 23:00:30 +0200 Subject: [PATCH 061/232] no longer color the exception provided error message as red (#36024) this doesnt compose well with exceptions trying to provide colors in their error messages --- base/errorshow.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index a4583bc819856..25e2b54b5b171 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -86,9 +86,7 @@ end function showerror(io::IO, ex, bt; backtrace=true) try - with_output_color(get(io, :color, false) ? error_color() : :nothing, io) do io - showerror(io, ex) - end + showerror(io, ex) finally backtrace && show_backtrace(io, bt) end From dd41e9f2a7ae0adfd2c8784ef68b03ac3e026489 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Thu, 28 May 2020 17:04:21 -0400 Subject: [PATCH 062/232] Relax complex function signatures to make them ForwardDiff compatible (#36030) Ref: https://github.com/JuliaDiff/ForwardDiff.jl/pull/455 --- base/complex.jl | 79 ++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/base/complex.jl b/base/complex.jl index 49ac018096144..dc828eb6ca2e0 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -464,7 +464,7 @@ function inv(w::ComplexF64) return ComplexF64(p*s,q*s) # undo scaling end -function ssqs(x::T, y::T) where T<:AbstractFloat +function ssqs(x::T, y::T) where T<:Real k::Int = 0 ρ = x*x + y*y if !isfinite(ρ) && (isinf(x) || isinf(y)) @@ -478,7 +478,8 @@ function ssqs(x::T, y::T) where T<:AbstractFloat ρ, k end -function sqrt(z::Complex{<:AbstractFloat}) +function sqrt(z::Complex) + z = float(z) x, y = reim(z) if x==y==0 return Complex(zero(x),y) @@ -503,7 +504,6 @@ function sqrt(z::Complex{<:AbstractFloat}) end Complex(ξ,η) end -sqrt(z::Complex) = sqrt(float(z)) # function sqrt(z::Complex) # rz = float(real(z)) @@ -560,10 +560,12 @@ julia> rad2deg(angle(-1 - im)) """ angle(z::Complex) = atan(imag(z), real(z)) -function log(z::Complex{T}) where T<:AbstractFloat - T1::T = 1.25 - T2::T = 3 - ln2::T = log(convert(T,2)) #0.6931471805599453 +function log(z::Complex) + z = float(z) + T = typeof(real(z)) + T1 = convert(T,5)/convert(T,4) + T2 = convert(T,3) + ln2 = log(convert(T,2)) #0.6931471805599453 x, y = reim(z) ρ, k = ssqs(x,y) ax = abs(x) @@ -580,7 +582,6 @@ function log(z::Complex{T}) where T<:AbstractFloat end Complex(ρρ, angle(z)) end -log(z::Complex) = log(float(z)) # function log(z::Complex) # ar = abs(real(z)) @@ -681,24 +682,27 @@ function log1p(z::Complex{T}) where T end end -function exp2(z::Complex{T}) where T<:AbstractFloat +function exp2(z::Complex{T}) where T + z = float(z) er = exp2(real(z)) - theta = imag(z) * log(convert(T, 2)) + theta = imag(z) * log(convert(float(T), 2)) s, c = sincos(theta) Complex(er * c, er * s) end -exp2(z::Complex) = exp2(float(z)) -function exp10(z::Complex{T}) where T<:AbstractFloat +function exp10(z::Complex{T}) where T + z = float(z) er = exp10(real(z)) - theta = imag(z) * log(convert(T, 10)) + theta = imag(z) * log(convert(float(T), 10)) s, c = sincos(theta) Complex(er * c, er * s) end -exp10(z::Complex) = exp10(float(z)) # _cpow helper function to avoid method ambiguity with ^(::Complex,::Real) -function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:AbstractFloat} +function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where T + z = float(z) + p = float(p) + Tf = float(T) if isreal(p) pᵣ = real(p) if isinteger(pᵣ) && abs(pᵣ) < typemax(Int32) @@ -706,14 +710,14 @@ function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:Abstrac # when converting p to Int, and it also turns out to be roughly # the crossover point for exp(p*log(z)) or similar to be faster. if iszero(pᵣ) # fix signs of imaginary part for z^0 - zer = flipsign(copysign(zero(T),pᵣ), imag(z)) - return Complex(one(T), zer) + zer = flipsign(copysign(zero(Tf),pᵣ), imag(z)) + return Complex(one(Tf), zer) end ip = convert(Int, pᵣ) if isreal(z) zᵣ = real(z) if ip < 0 - iszero(z) && return Complex(T(NaN),T(NaN)) + iszero(z) && return Complex(Tf(NaN),Tf(NaN)) re = power_by_squaring(inv(zᵣ), -ip) im = -imag(z) else @@ -729,7 +733,7 @@ function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:Abstrac # (note: if both z and p are complex with ±0.0 imaginary parts, # the sign of the ±0.0 imaginary part of the result is ambiguous) if iszero(real(z)) - return pᵣ > 0 ? complex(z) : Complex(T(NaN),T(NaN)) # 0 or NaN+NaN*im + return pᵣ > 0 ? complex(z) : Complex(Tf(NaN),Tf(NaN)) # 0 or NaN+NaN*im elseif real(z) > 0 return Complex(real(z)^pᵣ, z isa Real ? ifelse(real(z) < 1, -imag(p), imag(p)) : flipsign(imag(z), pᵣ)) else @@ -741,8 +745,8 @@ function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:Abstrac # improved here, but it's not clear if it's worth it… return rᵖ * complex(cospi(pᵣ), flipsign(sinpi(pᵣ),imag(z))) else - iszero(rᵖ) && return zero(Complex{T}) # no way to get correct signs of 0.0 - return Complex(T(NaN),T(NaN)) # non-finite phase angle or NaN input + iszero(rᵖ) && return zero(Complex{Tf}) # no way to get correct signs of 0.0 + return Complex(Tf(NaN),Tf(NaN)) # non-finite phase angle or NaN input end end else @@ -750,7 +754,7 @@ function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:Abstrac ϕ = pᵣ*angle(z) end elseif isreal(z) - iszero(z) && return real(p) > 0 ? complex(z) : Complex(T(NaN),T(NaN)) # 0 or NaN+NaN*im + iszero(z) && return real(p) > 0 ? complex(z) : Complex(Tf(NaN),Tf(NaN)) # 0 or NaN+NaN*im zᵣ = real(z) pᵣ, pᵢ = reim(p) if zᵣ > 0 @@ -758,7 +762,7 @@ function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:Abstrac ϕ = pᵢ*log(zᵣ) else r = -zᵣ - θ = copysign(T(π),imag(z)) + θ = copysign(Tf(π),imag(z)) rᵖ = r^pᵣ * exp(-pᵢ*θ) ϕ = pᵣ*θ + pᵢ*log(r) end @@ -773,11 +777,10 @@ function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:Abstrac if isfinite(ϕ) return rᵖ * cis(ϕ) else - iszero(rᵖ) && return zero(Complex{T}) # no way to get correct signs of 0.0 - return Complex(T(NaN),T(NaN)) # non-finite phase angle or NaN input + iszero(rᵖ) && return zero(Complex{Tf}) # no way to get correct signs of 0.0 + return Complex(Tf(NaN),Tf(NaN)) # non-finite phase angle or NaN input end end -_cpow(z, p) = _cpow(float(z), float(p)) ^(z::Complex{T}, p::Complex{T}) where T<:Real = _cpow(z, p) ^(z::Complex{T}, p::T) where T<:Real = _cpow(z, p) ^(z::T, p::Complex{T}) where T<:Real = _cpow(z, p) @@ -859,7 +862,8 @@ function asin(z::Complex) Complex(ξ,η) end -function acos(z::Complex{<:AbstractFloat}) +function acos(z::Complex) + z = float(z) zr, zi = reim(z) if isnan(zr) if isinf(zi) return Complex(zr, -zi) @@ -880,7 +884,6 @@ function acos(z::Complex{<:AbstractFloat}) if isinf(zr) && isinf(zi) ξ -= oftype(η,pi)/4 * sign(zr) end Complex(ξ,η) end -acos(z::Complex) = acos(float(z)) function atan(z::Complex) w = atanh(Complex(-imag(z),real(z))) @@ -898,13 +901,15 @@ function cosh(z::Complex) cos(Complex(zi,-zr)) end -function tanh(z::Complex{T}) where T<:AbstractFloat - Ω = prevfloat(typemax(T)) +function tanh(z::Complex{T}) where T + z = float(z) + Tf = float(T) + Ω = prevfloat(typemax(Tf)) ξ, η = reim(z) if isnan(ξ) && η==0 return Complex(ξ, η) end if 4*abs(ξ) > asinh(Ω) #Overflow? - Complex(copysign(one(T),ξ), - copysign(zero(T),η*(isfinite(η) ? sin(2*abs(η)) : one(η)))) + Complex(copysign(one(Tf),ξ), + copysign(zero(Tf),η*(isfinite(η) ? sin(2*abs(η)) : one(η)))) else t = tan(η) β = 1+t*t #sec(η)^2 @@ -917,7 +922,6 @@ function tanh(z::Complex{T}) where T<:AbstractFloat end end end -tanh(z::Complex) = tanh(float(z)) function asinh(z::Complex) w = asin(Complex(-imag(z),real(z))) @@ -943,8 +947,10 @@ function acosh(z::Complex) Complex(ξ, η) end -function atanh(z::Complex{T}) where T<:AbstractFloat - Ω = prevfloat(typemax(T)) +function atanh(z::Complex{T}) where T + z = float(z) + Tf = float(T) + Ω = prevfloat(typemax(Tf)) θ = sqrt(Ω)/4 ρ = 1/θ x, y = reim(z) @@ -963,7 +969,7 @@ function atanh(z::Complex{T}) where T<:AbstractFloat end return Complex(real(1/z), copysign(oftype(y,pi)/2, y)) end - β = copysign(one(T), x) + β = copysign(one(Tf), x) z *= β x, y = reim(z) if x == 1 @@ -986,7 +992,6 @@ function atanh(z::Complex{T}) where T<:AbstractFloat end β * Complex(ξ, η) end -atanh(z::Complex) = atanh(float(z)) #Rounding complex numbers #Requires two different RoundingModes for the real and imaginary components From 54cf29c7587890777b4d38720df0986d37f608cf Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 28 May 2020 17:08:01 -0400 Subject: [PATCH 063/232] give an error for EOF before complete input to the stream REPL (#35997) --- base/client.jl | 22 ++++++++++------------ test/cmdlineargs.jl | 11 +++++++++++ test/syntax.jl | 12 ------------ 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/base/client.jl b/base/client.jl index b22a837c378bd..5b70e6575ff88 100644 --- a/base/client.jl +++ b/base/client.jl @@ -182,17 +182,6 @@ function parse_input_line(s::String; filename::String="none", depwarn=true) end parse_input_line(s::AbstractString) = parse_input_line(String(s)) -function parse_input_line(io::IO) - s = "" - while !eof(io) - s *= readline(io, keep=true) - e = parse_input_line(s) - if !(isa(e,Expr) && e.head === :incomplete) - return e - end - end -end - # detect the reason which caused an :incomplete expression # from the error message # NOTE: the error messages are defined in src/julia-parser.scm @@ -424,7 +413,16 @@ function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_fil flush(stdout) end try - eval_user_input(stderr, parse_input_line(input), true) + line = "" + ex = nothing + while !eof(input) + line *= readline(input, keep=true) + ex = parse_input_line(line) + if !(isa(ex, Expr) && ex.head === :incomplete) + break + end + end + eval_user_input(stderr, ex, true) catch err isa(err, InterruptException) ? print("\n\n") : rethrow() end diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 5bb6ec3b9a8d9..de2d3593de0ce 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -687,6 +687,17 @@ let exename = `$(Base.julia_cmd()) --startup-file=no` end end +# incomplete inputs to stream REPL +let exename = `$(Base.julia_cmd()) --startup-file=no` + in = Pipe(); out = Pipe(); err = Pipe() + proc = run(pipeline(exename, stdin = in, stdout = out, stderr = err), wait=false) + write(in, "f(\n") + close(in) + close(err.in) + txt = readline(err) + @test startswith(txt, "ERROR: syntax: incomplete") +end + # Issue #29855 for yn in ("no", "yes") exename = `$(Base.julia_cmd()) --inline=no --startup-file=no --inline=$yn` diff --git a/test/syntax.jl b/test/syntax.jl index 40151b62f3bf0..9e23d97fdd2ad 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -341,18 +341,6 @@ test_parseerror("0x1.0p", "invalid numeric constant \"0x1.0\"") @X19861 """)::Expr) == 23341 -# test parse_input_line for a streaming IO input -let b = IOBuffer(""" - let x = x - x - end - f() - """) - @test Base.parse_input_line(b).args[end] == Expr(:let, Expr(:(=), :x, :x), Expr(:block, LineNumberNode(2, :none), :x)) - @test Base.parse_input_line(b).args[end] == Expr(:call, :f) - @test Base.parse_input_line(b) === nothing -end - # issue #15763 test_parseerror("if\nfalse\nend", "missing condition in \"if\" at none:1") test_parseerror("if false\nelseif\nend", "missing condition in \"elseif\" at none:2") From 2e77a0220dd4673d3949459c061793c89be78a2d Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Thu, 28 May 2020 22:12:21 +0100 Subject: [PATCH 064/232] Add boolean operators to Mathematical Operators (#35996) --- doc/src/manual/mathematical-operations.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 1526102df8bb3..5227c8821c1be 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -63,6 +63,19 @@ julia> false * Inf This is useful for preventing the propagation of `NaN` values in quantities that are known to be zero. See [Knuth (1992)](https://arxiv.org/abs/math/9205211) for motivation. +## Boolean Operators + +The following [Boolean operators](https://en.wikipedia.org/wiki/Boolean_algebra#Operations) are supported on `Bool`: + +| Expression | Name | +|:---------- |:--------------------------------------| +| `!x` | not | +| `x && y` | [short-circuiting and][short-circuit] | +| `x \|\| y` | [short-circuiting or][short-circuit] | + +Note that `Bool` is an integer type and all the usual promotion rules and numeric operators are also defined on it. + +[short-circuit]: https://docs.julialang.org/en/v1/manual/control-flow/#Short-Circuit-Evaluation-1 ## Bitwise Operators The following [bitwise operators](https://en.wikipedia.org/wiki/Bitwise_operation#Bitwise_operators) From acd0e83b8ba152094f4548a71e4bbd56f97726c2 Mon Sep 17 00:00:00 2001 From: Per Rutquist Date: Thu, 28 May 2020 23:41:04 +0200 Subject: [PATCH 065/232] Link to "noteworthy differences" early in docs (#36056) * Link to "noteworthy differences" early in docs From discourse: https://discourse.julialang.org/t/the-speed-of-light-is-an-integer-why-should-we-care/40108/51?u=per It frequently happens that newcomers to Julia are tripped up by some aspect of the language, such as integer overflow, that is not present in their current favourite programming language. The list of noteworthy differences points out many of these pitfalls, so getting people to read it can reduce user frustration. * Fix references --- doc/src/manual/getting-started.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index 6eae27635d0cc..7c493c2b59498 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -3,6 +3,8 @@ Julia installation is straightforward, whether using precompiled binaries or compiling from source. Download and install Julia by following the instructions at [https://julialang.org/downloads/](https://julialang.org/downloads/). +If you are coming to Julia from one of the following languages, then you should start by reading the section on noteworthy differences from [MATLAB](@ref Noteworthy-differences-from-MATLAB), [R](@ref Noteworthy-differences-from-R), [Python](@ref Noteworthy-differences-from-Python), [C/C++](@ref Noteworthy-differences-from-C/C) or [Common Lisp](@ref Noteworthy-differences-from-Common-Lisp). This will help you avoid some common pitfalls since Julia differs from those languages in many subtle ways. + The easiest way to learn and experiment with Julia is by starting an interactive session (also known as a read-eval-print loop or "REPL") by double-clicking the Julia executable or running `julia` from the command line: From 59a315cac72f048847b85e285ae19ef2dc9514c3 Mon Sep 17 00:00:00 2001 From: Jonas Witschel Date: Fri, 29 May 2020 03:05:15 +0000 Subject: [PATCH 066/232] LibGit2: add resolve_url to RemoteCallbacksStruct for LibGit2 0.99.0 (#35232) * LibGit2: amend GitError enum Since upstream commit https://github.com/libgit2/libgit2/commit/e9cef7c4b16b2cb572ac19fcd39217f7934cfa99 ("http: introduce GIT_ERROR_HTTP") an invalid content type yields a GIT_ERROR_HTTP instead of a GIT_ERROR_NET error. Update the enum to include this new error so that the unit test for LibGit2 doesn't fail with "invalid value for Enum Class: 34". * LibGit2: add resolve_url to RemoteCallbacksStruct for LibGit2 0.99.0 Upstream commit https://github.com/libgit2/libgit2/commit/59647e1ad095f80112918971b7bbe05adfaf8c3c ("remote: add callback to resolve URLs before connecting") introduced a new callback "resolve_url" in LibGit2 0.99.0. Even though it is not currently used in Julia, it needs to be accounted for to get the correct size for FetchOptionsStruct. An incorrectly aligned FetchOptionsStruct leads to error messages like "invalid version 0 on git_proxy_options" when trying to use the latest LibGit2 version. * LibGit2: update error message checking for LibGit2 0.99.0 Upstream commit https://github.com/libgit2/libgit2/commit/b9c5b15a7958ab4ecb83504a6d858efd18610297 ("http: use the new httpclient") changed the error message to include additional quotes. Relax the unit test to allow these if present. Co-authored-by: Milan Bouchet-Valat --- stdlib/LibGit2/src/error.jl | 7 ++++++- stdlib/LibGit2/src/types.jl | 3 +++ stdlib/LibGit2/test/libgit2.jl | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/stdlib/LibGit2/src/error.jl b/stdlib/LibGit2/src/error.jl index 4d1e915cef314..61eff41855ae4 100644 --- a/stdlib/LibGit2/src/error.jl +++ b/stdlib/LibGit2/src/error.jl @@ -58,7 +58,12 @@ export GitError Callback, CherryPick, Describe, - Rebase) + Rebase, + Filesystem, + Patch, + WorkTree, + SHA1, + HTTP) struct ErrorStruct message::Ptr{UInt8} diff --git a/stdlib/LibGit2/src/types.jl b/stdlib/LibGit2/src/types.jl index 6ffbe67ea2775..e00a0f161e79b 100644 --- a/stdlib/LibGit2/src/types.jl +++ b/stdlib/LibGit2/src/types.jl @@ -223,6 +223,9 @@ end push_negotiation::Ptr{Cvoid} = C_NULL transport::Ptr{Cvoid} = C_NULL payload::Ptr{Cvoid} = C_NULL + @static if LibGit2.VERSION >= v"0.99.0" + resolve_url::Ptr{Cvoid} = C_NULL + end end """ diff --git a/stdlib/LibGit2/test/libgit2.jl b/stdlib/LibGit2/test/libgit2.jl index 8f890ab0478ca..65b8f6b9e26c2 100644 --- a/stdlib/LibGit2/test/libgit2.jl +++ b/stdlib/LibGit2/test/libgit2.jl @@ -3048,7 +3048,7 @@ mktempdir() do dir deserialize(f) end @test err.code == LibGit2.Error.ERROR - @test lowercase(err.msg) == lowercase("invalid Content-Type: text/plain") + @test occursin(r"invalid content-type: '?text/plain'?"i, err.msg) end # OpenSSL s_server should still be running From 7c980c6af5adb0932f0920b014ec0a8711e9c9b1 Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Fri, 29 May 2020 10:06:07 +0200 Subject: [PATCH 067/232] noteworthy-differences: remove mention of pre-1.0 "change of syntax" (#36068) This mention of how the syntax has changed is not relevant for current newcomers-from-c(++), and it makes Julia look a bit unstable. Also add a reference to array construction manual. --- doc/src/manual/noteworthy-differences.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index d5f074fe0c67e..5ca2bfe6f6bad 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -254,7 +254,7 @@ For users coming to Julia from R, these are some noteworthy differences: * Julia arrays are indexed with square brackets, and can have more than one dimension `A[i,j]`. This syntax is not just syntactic sugar for a reference to a pointer or address as in C/C++. See - the Julia documentation for the syntax for array construction (it has changed between versions). + [the manual entry about array construction](@ref man-multi-dim-arrays). * In Julia, indexing of arrays, strings, etc. is 1-based not 0-based. * Julia arrays are not copied when assigned to another variable. After `A = B`, changing elements of `B` will modify `A` as well. Updating operators like `+=` do not operate in-place, they are equivalent to `A = A + B` From 3c63934a5386934a14465971a86a8a072ca6205b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 29 May 2020 10:23:23 -0400 Subject: [PATCH 068/232] Minor LibGit2 cleanup (#35466) * Fix spelling of committish The official git glossary (https://git-scm.com/docs/gitglossary), spells this commit-ish or committish. Since we can't do the former, use the latter consistently. We had three different spellings. * Minor LibGit2 API improvement Use the _bypath function in GitTree getindex rather than opencoding it using the tree walker. --- stdlib/LibGit2/src/LibGit2.jl | 2 +- stdlib/LibGit2/src/commit.jl | 2 + stdlib/LibGit2/src/merge.jl | 6 +-- stdlib/LibGit2/src/repository.jl | 20 +++++----- stdlib/LibGit2/src/tree.jl | 63 ++++++++++++++++---------------- stdlib/LibGit2/src/types.jl | 2 + 6 files changed, 50 insertions(+), 45 deletions(-) diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 627922e138a5a..4bfde5a8ee3ac 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -728,7 +728,7 @@ function merge!(repo::GitRepo; "There is no fetch reference for this branch.")) end Base.map(fh->GitAnnotated(repo,fh), fheads) - else # merge commitish + else # merge committish [GitAnnotated(repo, committish)] end else diff --git a/stdlib/LibGit2/src/commit.jl b/stdlib/LibGit2/src/commit.jl index b817dc8740553..5d3c666af4bbb 100644 --- a/stdlib/LibGit2/src/commit.jl +++ b/stdlib/LibGit2/src/commit.jl @@ -1,5 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +repository(c::GitCommit) = c.owner + """ message(c::GitCommit, raw::Bool=false) diff --git a/stdlib/LibGit2/src/merge.jl b/stdlib/LibGit2/src/merge.jl index 09ff4163aa768..0b2ddab1e8512 100644 --- a/stdlib/LibGit2/src/merge.jl +++ b/stdlib/LibGit2/src/merge.jl @@ -4,7 +4,7 @@ GitAnnotated(repo::GitRepo, commit_id::GitHash) GitAnnotated(repo::GitRepo, ref::GitReference) GitAnnotated(repo::GitRepo, fh::FetchHead) - GitAnnotated(repo::GitRepo, comittish::AbstractString) + GitAnnotated(repo::GitRepo, committish::AbstractString) An annotated git commit carries with it information about how it was looked up and why, so that rebase or merge operations have more information about the context of @@ -40,8 +40,8 @@ function GitAnnotated(repo::GitRepo, fh::FetchHead) return GitAnnotated(repo, ann_ref_ref[]) end -function GitAnnotated(repo::GitRepo, comittish::AbstractString) - obj = GitObject(repo, comittish) +function GitAnnotated(repo::GitRepo, committish::AbstractString) + obj = GitObject(repo, committish) cmt = peel(GitCommit, obj) return GitAnnotated(repo, GitHash(cmt)) end diff --git a/stdlib/LibGit2/src/repository.jl b/stdlib/LibGit2/src/repository.jl index 96249bd9f69dd..eef9e8dba889e 100644 --- a/stdlib/LibGit2/src/repository.jl +++ b/stdlib/LibGit2/src/repository.jl @@ -263,32 +263,32 @@ end peel(obj::GitObject) = peel(GitObject, obj) """ - LibGit2.GitDescribeResult(commitish::GitObject; kwarg...) + LibGit2.GitDescribeResult(committish::GitObject; kwarg...) -Produce a `GitDescribeResult` of the `commitish` `GitObject`, which +Produce a `GitDescribeResult` of the `committish` `GitObject`, which contains detailed information about it based on the keyword argument: * `options::DescribeOptions=DescribeOptions()` -A git description of a `commitish` object looks for the tag (by default, annotated, -although a search of all tags can be performed) which can be reached from `commitish` -which is most recent. If the tag is pointing to `commitish`, then only the tag is +A git description of a `committish` object looks for the tag (by default, annotated, +although a search of all tags can be performed) which can be reached from `committish` +which is most recent. If the tag is pointing to `committish`, then only the tag is included in the description. Otherwise, a suffix is included which contains the -number of commits between `commitish` and the most recent tag. If there is no such +number of commits between `committish` and the most recent tag. If there is no such tag, the default behavior is for the description to fail, although this can be changed through `options`. -Equivalent to `git describe `. See [`DescribeOptions`](@ref) for more +Equivalent to `git describe `. See [`DescribeOptions`](@ref) for more information. """ -function GitDescribeResult(commitish::GitObject; +function GitDescribeResult(committish::GitObject; options::DescribeOptions=DescribeOptions()) ensure_initialized() result_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_describe_commit, :libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{DescribeOptions}), - result_ptr_ptr, commitish.ptr, Ref(options)) - return GitDescribeResult(commitish.owner, result_ptr_ptr[]) + result_ptr_ptr, committish.ptr, Ref(options)) + return GitDescribeResult(committish.owner, result_ptr_ptr[]) end """ diff --git a/stdlib/LibGit2/src/tree.jl b/stdlib/LibGit2/src/tree.jl index 0b4778166f3f3..1ef8a2eb75003 100644 --- a/stdlib/LibGit2/src/tree.jl +++ b/stdlib/LibGit2/src/tree.jl @@ -1,5 +1,11 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +function GitTree(c::GitCommit) + tree_out = Ref{Ptr{Cvoid}}(C_NULL) + @check ccall((:git_commit_tree, :libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), tree_out, c) + GitTree(repository(c), tree_out[]) +end + """ treewalk(f, tree::GitTree, post::Bool=false) @@ -122,14 +128,14 @@ tree_entry = tree[1] blob = LibGit2.GitBlob(tree_entry) ``` """ -function GitObject(e::GitTreeEntry) end +GitObject(e::GitTreeEntry) function (::Type{T})(te::GitTreeEntry) where T<:GitObject ensure_initialized() repo = repository(te) obj_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_tree_entry_to_object, :libgit2), Cint, - (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ref{Nothing}), - obj_ptr_ptr, repo.ptr, te.ptr) + (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}), + obj_ptr_ptr, repo, te) return T(repo, obj_ptr_ptr[]) end @@ -146,6 +152,23 @@ function Base.show(io::IO, tree::GitTree) println(io, "Number of entries: ", count(tree)) end +function _getindex(tree::GitTree, target::AbstractString) + if basename(target) == "" + # get rid of any trailing separator + target = dirname(target) + end + if isempty(target) || target == "/" + return tree + end + + entry = Ref{Ptr{Cvoid}}(C_NULL) + err = ccall((:git_tree_entry_bypath, :libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), entry, tree, target) + err == Int(Error.ENOTFOUND) && return nothing + err < 0 && throw(Error.GitError(err)) + entry = GitTreeEntry(tree, entry[], true #= N.B.: Most other lookups need false here =#) + return GitObject(entry) +end + """ getindex(tree::GitTree, target::AbstractString) -> GitObject @@ -161,33 +184,11 @@ runtests = subtree["runtests.jl"] ``` """ function Base.getindex(tree::GitTree, target::AbstractString) - if basename(target) == "" - # get rid of any trailing separator - target = dirname(target) - end - if target == "" || target == "/" - return tree - end + e = _getindex(tree, target) + e === nothing && throw(KeyError(target)) + return e +end - local oid = nothing - function _getindex_callback(root::String, entry::GitTreeEntry)::Cint - path = joinpath(root, filename(entry)) - if path == target - # we found the target, save the oid and stop the walk - oid = entryid(entry) - # workaround for issue: https://github.com/libgit2/libgit2/issues/4693 - ensure_initialized() - ccall((:giterr_set_str, :libgit2), Cvoid, - (Cint, Cstring), Cint(Error.Callback), - "git_tree_walk callback returned -1") - return -1 - elseif entrytype(entry) == GitTree && !startswith(target, path) - # this subtree isn't relevant, so skip it - return 1 - end - return 0 - end - treewalk(_getindex_callback, tree) - oid === nothing && throw(KeyError(target)) - return GitObject(repository(tree), oid) +function Base.haskey(tree::GitTree, target::AbstractString) + return _getindex(tree, target) !== nothing end diff --git a/stdlib/LibGit2/src/types.jl b/stdlib/LibGit2/src/types.jl index e00a0f161e79b..6276544100888 100644 --- a/stdlib/LibGit2/src/types.jl +++ b/stdlib/LibGit2/src/types.jl @@ -1010,6 +1010,7 @@ for (typ, owntyp, sup, cname) in [ return obj end end + @eval Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::$typ) = x.ptr else @eval mutable struct $typ <: $sup owner::$owntyp @@ -1024,6 +1025,7 @@ for (typ, owntyp, sup, cname) in [ return obj end end + @eval Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::$typ) = x.ptr if isa(owntyp, Expr) && owntyp.args[1] === :Union && owntyp.args[3] === :Nothing @eval begin $typ(ptr::Ptr{Cvoid}, fin::Bool=true) = $typ(nothing, ptr, fin) From 73858763671d84d87c8007b6228ef67c0ff61d15 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Fri, 29 May 2020 16:43:43 +0200 Subject: [PATCH 069/232] Fix equality for one-element ranges --- base/range.jl | 27 +++++++++++++++++++++------ test/ranges.jl | 4 +++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/base/range.jl b/base/range.jl index 243acb40660b9..2319372847c16 100644 --- a/base/range.jl +++ b/base/range.jl @@ -741,14 +741,29 @@ show(io::IO, r::AbstractRange) = print(io, repr(first(r)), ':', repr(step(r)), ' show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") -==(r::T, s::T) where {T<:AbstractRange} = - (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s))) -==(r::OrdinalRange, s::OrdinalRange) = - (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s))) +function ==(r::T, s::T) where {T<:AbstractRange} + isempty(r) && return isempty(s) + _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +end + +function ==(r::OrdinalRange, s::OrdinalRange) + isempty(r) && return isempty(s) + _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +end + ==(r::T, s::T) where {T<:Union{StepRangeLen,LinRange}} = (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (length(r) == length(s)) & (last(r) == last(s))) -==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} = - (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s))) + +function ==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} + isempty(r) && return isempty(s) + _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +end + +_has_length_one(r::OrdinalRange) = first(r) == last(r) +_has_length_one(r::AbstractRange) = isone(length(r)) function ==(r::AbstractRange, s::AbstractRange) lr = length(r) diff --git a/test/ranges.jl b/test/ranges.jl index 77f925b5af337..3f83bb3d76661 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -819,7 +819,9 @@ end 0.0:0.1:1.0, map(Float32,0.0:0.1:1.0),map(Float32,LinRange(0.0, 1.0, 11)), 1.0:eps():1.0 .+ 10eps(), 9007199254740990.:1.0:9007199254740994, range(0, stop=1, length=20), map(Float32, range(0, stop=1, length=20)), - 3:2, 5:-2:7, range(0.0, step=2.0, length=0), 3//2:3//2:0//1, LinRange(2,3,0)] + 3:2, 5:-2:7, range(0.0, step=2.0, length=0), 3//2:3//2:0//1, LinRange(2,3,0), + Base.OneTo(1), 1:1, 1:-3:1, 1//1:1//3:1//1, range(1.0, step=2.5, length=1), + LinRange(1,1,1), LinRange(1,1,2)] for r in Rs local r ar = Vector(r) From 38a373af126ac203f81d2930a4f06a73b5f99043 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Fri, 29 May 2020 13:04:12 -0400 Subject: [PATCH 070/232] Mention how to get the basetype of an enum (#36057) * Mention how to get the basetype of an enum Fix #28778 * Mention Integer() instead of internal method --- base/Enums.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/Enums.jl b/base/Enums.jl index fe15ea2110781..38453ea92c4f5 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -106,7 +106,8 @@ end `BaseType`, which defaults to [`Int32`](@ref), must be a primitive subtype of `Integer`. Member values can be converted between the enum type and `BaseType`. `read` and `write` -perform these conversions automatically. +perform these conversions automatically. In case the enum is created with a non-default +`BaseType`, `Integer(value1)` will return the integer `value1` with the type `BaseType`. To list all the instances of an enum use `instances`, e.g. From 2324b38ad822e7c1096d274baf66312219fdc859 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 29 May 2020 17:28:16 -0400 Subject: [PATCH 071/232] skip `Union{}` arguments to gc_preserve in codegen --- src/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index e35c7fc4da999..ab4b8522c46e5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3820,7 +3820,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) std::vector vals; for (size_t i = 0; i < nargs; ++i) { const jl_cgval_t &ai = argv[i]; - if (ai.constant) + if (ai.constant || ai.typ == jl_bottom_type) continue; if (ai.isboxed) { vals.push_back(ai.Vboxed); From ba6b8991b774a516faa1f9f9367c16a1bddc36ec Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 29 May 2020 18:09:04 -0400 Subject: [PATCH 072/232] allow constant propagation to break call cycles in inference (#36058) --- base/compiler/abstractinterpretation.jl | 8 +++++--- test/compiler/inference.jl | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 66c75c4ae0164..f82380cecaa57 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -129,7 +129,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end # try constant propagation if only 1 method is inferred to non-Bottom # this is in preparation for inlining, or improving the return result - if nonbot > 0 && seen == napplicable && !edgecycle && isa(rettype, Type) && InferenceParams(interp).ipo_constant_propagation + is_unused = call_result_unused(sv) + if nonbot > 0 && seen == napplicable && (!edgecycle || !is_unused) && isa(rettype, Type) && InferenceParams(interp).ipo_constant_propagation # if there's a possibility we could constant-propagate a better result # (hopefully without doing too much work), try to do that now # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge @@ -139,7 +140,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), rettype = const_rettype end end - if call_result_unused(sv) && !(rettype === Bottom) + if is_unused && !(rettype === Bottom) # We're mainly only here because the optimizer might want this code, # but we ourselves locally don't typically care about it locally # (beyond checking if it always throws). @@ -268,7 +269,8 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp typeinf(interp, frame) || return Any end result = inf_result.result - isa(result, InferenceState) && return Any # TODO: unexpected, is this recursive constant inference? + # if constant inference hits a cycle, just bail out + isa(result, InferenceState) && return Any add_backedge!(inf_result.linfo, sv) return result end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 525feb5dae2f7..9fadf28bdff51 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2601,3 +2601,25 @@ _use_unstable_kw_2() = _unstable_kw(x = 2, y = rand()) end _construct_structwithsplatnew() = StructWithSplatNew(("",)) @test Base.return_types(_construct_structwithsplatnew) == Any[StructWithSplatNew] + +# case where a call cycle can be broken by constant propagation +struct NotQRSparse + x::Matrix{Float64} + n::Int +end +@inline function getprop(F::NotQRSparse, d::Symbol) + if d === :Q + return NotQRSparse(getprop(F, :B), _size_ish(F, 2)) + elseif d === :A + return Dict() + elseif d === :B + return rand(2,2) + elseif d === :C + return "" + else + error() + end +end +_size_ish(F::NotQRSparse, i::Integer) = size(getprop(F, :B), 1) +_call_size_ish(x) = _size_ish(x,1) +@test Base.return_types(_call_size_ish, (NotQRSparse,)) == Any[Int] From 2ab654ce5b42622742237e05c4293d070cc12911 Mon Sep 17 00:00:00 2001 From: Shan Sikdar Date: Fri, 29 May 2020 22:42:19 -0400 Subject: [PATCH 073/232] Change getaddrinfo(host::AbstractString) to work with ipv6 localhost string "::1" (#36029) * fix getaddrinfo(host::AbstractString) to work for ipv6 hostname * fix getaddrinfo add tests * clean up getaddrinfo * when getaddrinfo gets passed localhost choosing the ipv6 instead ipv4 result causes the tests to fail * fix downstream test --- stdlib/Sockets/src/addrinfo.jl | 8 +++++++- stdlib/Sockets/test/runtests.jl | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/stdlib/Sockets/src/addrinfo.jl b/stdlib/Sockets/src/addrinfo.jl index 95a7731a5ea55..ed2fba32039aa 100644 --- a/stdlib/Sockets/src/addrinfo.jl +++ b/stdlib/Sockets/src/addrinfo.jl @@ -136,7 +136,13 @@ function getaddrinfo(host::String, T::Type{<:IPAddr}) throw(DNSError(host, UV_EAI_NONAME)) end getaddrinfo(host::AbstractString, T::Type{<:IPAddr}) = getaddrinfo(String(host), T) -getaddrinfo(host::AbstractString) = getaddrinfo(String(host), IPv4) +function getaddrinfo(host::AbstractString) + addrs = getalladdrinfo(String(host)) + if !isempty(addrs) + return addrs[begin] + end + throw(DNSError(host, UV_EAI_NONAME)) +end function uv_getnameinfocb(req::Ptr{Cvoid}, status::Cint, hostname::Cstring, service::Cstring) data = uv_req_data(req) diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index ef5b5f4e46041..e8dad4d95ee4b 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -218,6 +218,8 @@ end end @testset "getaddrinfo" begin + @test getaddrinfo("127.0.0.1") == ip"127.0.0.1" + @test getaddrinfo("::1") == ip"::1" let localhost = getnameinfo(ip"127.0.0.1")::String @test !isempty(localhost) && localhost != "127.0.0.1" @test !isempty(getalladdrinfo(localhost)::Vector{IPAddr}) @@ -255,10 +257,10 @@ end end # test connecting to a named port -let localhost = getaddrinfo("localhost") +let localhost = ip"127.0.0.1" global randport randport, server = listenany(localhost, defaultport) - @async connect("localhost", randport) + @async connect(localhost, randport) s1 = accept(server) @test_throws ErrorException("client TCPSocket is not in initialization state") accept(server, s1) @test_throws Base._UVError("listen", Base.UV_EADDRINUSE) listen(randport) From a5c08c49ceda1962ef49d680d29fc26dc3d0c049 Mon Sep 17 00:00:00 2001 From: natema Date: Sat, 30 May 2020 16:31:50 +0200 Subject: [PATCH 074/232] Removing wrong remark from Implementation section (#36083) See also [this post](https://discourse.julialang.org/t/wrong-use-of-asymptotic-notation-in-the-multi-dimensional-arrays-page/40464?u=mathematics) in the community forum. --- doc/src/manual/arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 19b049d9e5649..e7f4e9fc55e4c 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -969,7 +969,7 @@ be quite different from conventional arrays. For example, elements might be comp rather than stored. However, any concrete `AbstractArray{T,N}` type should generally implement at least [`size(A)`](@ref) (returning an `Int` tuple), [`getindex(A,i)`](@ref) and [`getindex(A,i1,...,iN)`](@ref getindex); mutable arrays should also implement [`setindex!`](@ref). It is recommended that these operations -have nearly constant time complexity, or technically Õ(1) complexity, as otherwise some array +have nearly constant time complexity, as otherwise some array functions may be unexpectedly slow. Concrete types should also typically provide a [`similar(A,T=eltype(A),dims=size(A))`](@ref) method, which is used to allocate a similar array for [`copy`](@ref) and other out-of-place operations. No matter how an `AbstractArray{T,N}` is represented internally, `T` is the type of From 7301dc61bdeb5d66e94e15bdfcd4c54f7c90f068 Mon Sep 17 00:00:00 2001 From: Michele Zaffalon Date: Sat, 30 May 2020 17:02:34 +0200 Subject: [PATCH 075/232] Update gethostname example (#36082) Add `GC.@preserve` to code and a short explanation --- doc/src/manual/calling-c-and-fortran-code.md | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index c418abf0ce04b..fb804e7e5aa2f 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -142,19 +142,24 @@ function gethostname() hostname, sizeof(hostname)) Base.systemerror("gethostname", err != 0) hostname[end] = 0 # ensure null-termination - return unsafe_string(pointer(hostname)) + return GC.@preserve hostname unsafe_string(pointer(hostname)) end ``` This example first allocates an array of bytes. It then calls the C library function `gethostname` -to populate the array with the hostname. Finally, it takes a pointer to the hostname buffer, and converts the -pointer to a Julia string, assuming that it is a NUL-terminated C string. It is common for C libraries -to use this pattern of requiring the caller to allocate memory to be passed to the callee and -populated. Allocation of memory from Julia like this is generally accomplished by creating an -uninitialized array and passing a pointer to its data to the C function. This is why we don't -use the `Cstring` type here: as the array is uninitialized, it could contain NUL bytes. Converting -to a `Cstring` as part of the [`ccall`](@ref) checks for contained NUL bytes and could therefore -throw a conversion error. +to populate the array with the hostname. Finally, it takes a pointer to the hostname buffer, and +converts the pointer to a Julia string, assuming that it is a NUL-terminated C string. + +It is common for C libraries to use this pattern of requiring the caller to allocate memory to be +passed to the callee and populated. Allocation of memory from Julia like this is generally +accomplished by creating an uninitialized array and passing a pointer to its data to the C function. +This is why we don't use the `Cstring` type here: as the array is uninitialized, it could contain +NUL bytes. Converting to a `Cstring` as part of the [`ccall`](@ref) checks for contained NUL bytes +and could therefore throw a conversion error. + +Deferencing `pointer(hostname)` with `unsafe_string` is an unsafe operation as it requires access to +the memory allocated for `hostname` that may have been in the meanwhile garbage collected. The macro +[`GC.@preserve`](@ref) prevents this from happening and therefore accessing an invalid memory location. ## Creating C-Compatible Julia Function Pointers From 968ccfc2409eeb7cdf1debf6e8061bfd2bb470f4 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 28 May 2020 14:33:14 +0200 Subject: [PATCH 076/232] Error when compiling invalid AddrSpacePtrs. --- src/cgutils.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index d23bf9c20e7e6..57f4b623863ab 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -591,16 +591,14 @@ static Type *bitstype_to_llvm(jl_value_t *bt, bool llvmcall = false) if (bt == (jl_value_t*)jl_float64_type) return T_float64; if (jl_is_addrspace_ptr_type(bt)) { - int as = 0; - - jl_datatype_t *typ = (jl_datatype_t*)bt; - jl_value_t *as_param = jl_svecref(typ->parameters, 1); - + jl_value_t *as_param = jl_tparam1(bt); + int as; if (jl_is_int32(as_param)) as = jl_unbox_int32(as_param); else if (jl_is_int64(as_param)) as = jl_unbox_int64(as_param); - + else + jl_error("invalid pointer address space"); return PointerType::get(T_int8, as); } int nb = jl_datatype_size(bt); From a6354d9898ea213dfec0085408eac7538c0708f3 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 28 May 2020 18:35:45 +0200 Subject: [PATCH 077/232] Rename AddrSpacePtr to LLVMPtr. --- base/refpointer.jl | 12 ++++++++---- src/builtins.c | 2 +- src/ccall.cpp | 2 +- src/cgutils.cpp | 2 +- src/jltypes.c | 12 ++++++------ src/julia.h | 9 +++++---- src/staticdata.c | 4 ++-- test/llvmpasses/llvmcall.jl | 2 +- 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/base/refpointer.jl b/base/refpointer.jl index 88e59ae6a3cc5..0a67a8bb240cf 100644 --- a/base/refpointer.jl +++ b/base/refpointer.jl @@ -144,9 +144,13 @@ setindex!(b::RefArray, x) = (b.x[b.i] = x; b) ### """ - AddrSpacePtr{T, AS} + LLVMPtr{T, AS} -When passed as a `ccall` argument with the `llvmcall` calling convention, an `AddrSpacePtr` will be converted to an LLVM pointer type with the correct address space. -This type is mainly used to ensure Julia's codegen uses the correct address space when calling LLVM intrinsics. +A pointer type that more closely resembles LLVM semantics: It includes the pointer address +space, and will be passed as an actual pointer instead of an integer. + +This type is mainly used to interface with code that has strict requirements about pointers, +e.g., intrinsics that are selected based on the address space, or back-ends that require +pointers to be identifiable by their types. """ -Core.AddrSpacePtr +Core.LLVMPtr diff --git a/src/builtins.c b/src/builtins.c index c89745c48ea93..1629e46cdaf94 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1570,7 +1570,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("CodeInfo", (jl_value_t*)jl_code_info_type); add_builtin("Ref", (jl_value_t*)jl_ref_type); add_builtin("Ptr", (jl_value_t*)jl_pointer_type); - add_builtin("AddrSpacePtr", (jl_value_t*)jl_addrspace_pointer_type); + add_builtin("LLVMPtr", (jl_value_t*)jl_llvmpointer_type); add_builtin("Task", (jl_value_t*)jl_task_type); add_builtin("AbstractArray", (jl_value_t*)jl_abstractarray_type); diff --git a/src/ccall.cpp b/src/ccall.cpp index d41876f3f0fa2..31cfedb9bd03c 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1185,7 +1185,7 @@ std::string generate_func_sig(const char *fname) t = T_pint8; isboxed = false; } - else if (llvmcall && jl_is_addrspace_ptr_type(tti)) { + else if (llvmcall && jl_is_llvmpointer_type(tti)) { t = bitstype_to_llvm(tti, true); tti = (jl_value_t*)jl_voidpointer_type; isboxed = false; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 57f4b623863ab..8cdaf9c5bd2f4 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -590,7 +590,7 @@ static Type *bitstype_to_llvm(jl_value_t *bt, bool llvmcall = false) return T_float32; if (bt == (jl_value_t*)jl_float64_type) return T_float64; - if (jl_is_addrspace_ptr_type(bt)) { + if (jl_is_llvmpointer_type(bt)) { jl_value_t *as_param = jl_tparam1(bt); int as; if (jl_is_int32(as_param)) diff --git a/src/jltypes.c b/src/jltypes.c index 50278be9d44a4..194aacc576a56 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -119,8 +119,8 @@ jl_datatype_t *jl_lineinfonode_type; jl_unionall_t *jl_ref_type; jl_unionall_t *jl_pointer_type; jl_typename_t *jl_pointer_typename; -jl_unionall_t *jl_addrspace_pointer_type; -jl_typename_t *jl_addrspace_pointer_typename; +jl_unionall_t *jl_llvmpointer_type; +jl_typename_t *jl_llvmpointer_typename; jl_datatype_t *jl_void_type; // deprecated jl_datatype_t *jl_nothing_type; jl_datatype_t *jl_voidpointer_type; @@ -2238,14 +2238,14 @@ void jl_init_types(void) JL_GC_DISABLED sizeof(void*)*8)->name->wrapper; jl_pointer_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_pointer_type))->name; - // AddrSpacePtr{T, AS} where {T, AS} + // LLVMPtr{T, AS} where {T, AS} tv = jl_svec2(tvar("T"), tvar("AS")); jl_svec_t *tv_base = jl_svec1(tvar("T")); - jl_addrspace_pointer_type = (jl_unionall_t*) - jl_new_primitivetype((jl_value_t*)jl_symbol("AddrSpacePtr"), core, + jl_llvmpointer_type = (jl_unionall_t*) + jl_new_primitivetype((jl_value_t*)jl_symbol("LLVMPtr"), core, (jl_datatype_t*)jl_apply_type((jl_value_t*)jl_ref_type, jl_svec_data(tv_base), 1), tv, sizeof(void*)*8)->name->wrapper; - jl_addrspace_pointer_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_addrspace_pointer_type))->name; + jl_llvmpointer_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_llvmpointer_type))->name; // Type{T} where T<:Tuple tttvar = jl_new_typevar(jl_symbol("T"), diff --git a/src/julia.h b/src/julia.h index 6b35bb01ebe63..a6b10f84287a3 100644 --- a/src/julia.h +++ b/src/julia.h @@ -636,10 +636,10 @@ extern JL_DLLEXPORT jl_datatype_t *jl_signed_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_voidpointer_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_uint8pointer_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_pointer_type JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_unionall_t *jl_addrspace_pointer_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_unionall_t *jl_llvmpointer_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_ref_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_typename_t *jl_pointer_typename JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_typename_t *jl_addrspace_pointer_typename JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_typename_t *jl_llvmpointer_typename JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_typename_t *jl_namedtuple_typename JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_namedtuple_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_task_type JL_GLOBALLY_ROOTED; @@ -1090,6 +1090,7 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #define jl_is_cpointer(v) jl_is_cpointer_type(jl_typeof(v)) #define jl_is_pointer(v) jl_is_cpointer_type(jl_typeof(v)) #define jl_is_uint8pointer(v)jl_typeis(v,jl_uint8pointer_type) +#define jl_is_llvmpointer(v) jl_typeis(v,jl_llvmpointer_type) #define jl_is_intrinsic(v) jl_typeis(v,jl_intrinsic_type) #define jl_array_isbitsunion(a) (!(((jl_array_t*)(a))->flags.ptrarray) && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) @@ -1154,10 +1155,10 @@ STATIC_INLINE int jl_is_cpointer_type(jl_value_t *t) JL_NOTSAFEPOINT ((jl_datatype_t*)(t))->name == ((jl_datatype_t*)jl_pointer_type->body)->name); } -STATIC_INLINE int jl_is_addrspace_ptr_type(jl_value_t *t) JL_NOTSAFEPOINT +STATIC_INLINE int jl_is_llvmpointer_type(jl_value_t *t) JL_NOTSAFEPOINT { return (jl_is_datatype(t) && - ((jl_datatype_t*)(t))->name == jl_addrspace_pointer_typename); + ((jl_datatype_t*)(t))->name == jl_llvmpointer_typename); } STATIC_INLINE int jl_is_abstract_ref_type(jl_value_t *t) JL_NOTSAFEPOINT diff --git a/src/staticdata.c b/src/staticdata.c index 47fdfa3c5d1a2..6f436fcdc339c 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -41,7 +41,7 @@ static void *const _tags[] = { &jl_linenumbernode_type, &jl_lineinfonode_type, &jl_gotonode_type, &jl_quotenode_type, &jl_pinode_type, &jl_phinode_type, &jl_phicnode_type, &jl_upsilonnode_type, - &jl_type_type, &jl_bottom_type, &jl_ref_type, &jl_pointer_type, &jl_addrspace_pointer_type, + &jl_type_type, &jl_bottom_type, &jl_ref_type, &jl_pointer_type, &jl_llvmpointer_type, &jl_vararg_type, &jl_abstractarray_type, &jl_densearray_type, &jl_nothing_type, &jl_function_type, &jl_typeofbottom_type, &jl_unionall_type, &jl_typename_type, &jl_builtin_type, &jl_code_info_type, @@ -57,7 +57,7 @@ static void *const _tags[] = { &jl_float16_type, &jl_float32_type, &jl_float64_type, &jl_floatingpoint_type, &jl_number_type, &jl_signed_type, // special typenames - &jl_tuple_typename, &jl_pointer_typename, &jl_addrspace_pointer_typename, &jl_array_typename, &jl_type_typename, + &jl_tuple_typename, &jl_pointer_typename, &jl_llvmpointer_typename, &jl_array_typename, &jl_type_typename, &jl_vararg_typename, &jl_namedtuple_typename, &jl_vecelement_typename, // special exceptions diff --git a/test/llvmpasses/llvmcall.jl b/test/llvmpasses/llvmcall.jl index c00cbeb66b66a..c9cdf4db1fc38 100644 --- a/test/llvmpasses/llvmcall.jl +++ b/test/llvmpasses/llvmcall.jl @@ -23,7 +23,7 @@ emit(foo, NTuple{2, Float16}) emit(foo, NTuple{2, VecElement{Float16}}) # CHECK: call i8 addrspace(3)* @foo(i8 addrspace(3)* %{{[0-9]+}}) -emit(foo, Core.AddrSpacePtr{Float32, 3}) +emit(foo, Core.LLVMPtr{Float32, 3}) # CHECK: call { i32, i32 } @foo({ i32, i32 } %{{[0-9]+}}) emit(foo, Foo) From 932a1ecbe92ebccf026d3bd4b5bc904390e28f9a Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 1 Jun 2020 08:06:34 -0700 Subject: [PATCH 078/232] Fix a bug with break/continue/return in at-testset begin end (#36046) --- stdlib/Test/src/Test.jl | 6 ++++-- stdlib/Test/test/runtests.jl | 11 +++++++++++ stdlib/Test/test/test_pop_testset_exec.jl | 6 ++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 stdlib/Test/test/test_pop_testset_exec.jl diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 3fb64bdff5bd3..2081425563780 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1100,6 +1100,7 @@ function testset_beginend(args, tests, source) # action (such as reporting the results) ex = quote _check_testset($testsettype, $(QuoteNode(testsettype.args[1]))) + local ret local ts = $(testsettype)($desc; $options...) push_testset(ts) # we reproduce the logic of guardseed, but this function @@ -1120,9 +1121,10 @@ function testset_beginend(args, tests, source) record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source)))) finally copy!(RNG, oldrng) + pop_testset() + ret = finish(ts) end - pop_testset() - finish(ts) + ret end # preserve outer location if possible if tests isa Expr && tests.head === :block && !isempty(tests.args) && tests.args[1] isa LineNumberNode diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index f3c0d4969ddfe..3307fd48e0d3c 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -919,3 +919,14 @@ end # Issue 20620 @test @inferred(.![true, false]) == [false, true] @test @inferred([3, 4] .- [1, 2] .+ [-2, -2]) == [0, 0] + +@testset "push/pop_testset invariance (Issue 32937)" begin + io = IOBuffer() + path = joinpath(@__DIR__(), "test_pop_testset_exec.jl") + cmd = `$(Base.julia_cmd()) $path` + ok = !success(pipeline(cmd; stdout = io, stderr = io)) + if !ok + @error "push/pop_testset invariance test failed" cmd Text(String(take!(io))) + end + @test ok +end diff --git a/stdlib/Test/test/test_pop_testset_exec.jl b/stdlib/Test/test/test_pop_testset_exec.jl new file mode 100644 index 0000000000000..3c5fde63f4ad4 --- /dev/null +++ b/stdlib/Test/test/test_pop_testset_exec.jl @@ -0,0 +1,6 @@ +using Test + +@testset begin + @test false + return +end From cfb9b5534967c05a29ff39ae212837543819dd6a Mon Sep 17 00:00:00 2001 From: Klaus Crusius Date: Mon, 1 Jun 2020 18:09:10 +0200 Subject: [PATCH 079/232] multiplication of sparse triangular matrices #35609 #35610 #35642 (#35659) * multiplication of sparse triangular matrices * removed disambiguity and improved test cases * test cases for `nnz, nzrange, rowvals, nonzeros` --- stdlib/SparseArrays/src/linalg.jl | 137 ++++++++++++++--------- stdlib/SparseArrays/src/sparsematrix.jl | 17 ++- stdlib/SparseArrays/src/sparsevector.jl | 36 ++++-- stdlib/SparseArrays/test/sparse.jl | 80 +++++++++++++ stdlib/SparseArrays/test/sparsevector.jl | 15 +++ 5 files changed, 223 insertions(+), 62 deletions(-) diff --git a/stdlib/SparseArrays/src/linalg.jl b/stdlib/SparseArrays/src/linalg.jl index c54a6c760c0fd..75587b203f6af 100644 --- a/stdlib/SparseArrays/src/linalg.jl +++ b/stdlib/SparseArrays/src/linalg.jl @@ -44,9 +44,9 @@ function mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVe end C end -*(A::AbstractSparseMatrixCSC{TA}, x::StridedVector{Tx}) where {TA,Tx} = +*(A::SparseMatrixCSCUnion{TA}, x::StridedVector{Tx}) where {TA,Tx} = (T = promote_op(matprod, TA, Tx); mul!(similar(x, T, size(A, 1)), A, x, true, false)) -*(A::AbstractSparseMatrixCSC{TA}, B::AdjOrTransStridedOrTriangularMatrix{Tx}) where {TA,Tx} = +*(A::SparseMatrixCSCUnion{TA}, B::AdjOrTransStridedOrTriangularMatrix{Tx}) where {TA,Tx} = (T = promote_op(matprod, TA, Tx); mul!(similar(B, T, (size(A, 1), size(B, 2))), A, B, true, false)) function mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}, α::Number, β::Number) @@ -125,7 +125,7 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedOrTriangularMatrix, A::Abs end C end -*(X::AdjOrTransStridedOrTriangularMatrix, A::AbstractSparseMatrixCSC{TvA}) where {TvA} = +*(X::AdjOrTransStridedOrTriangularMatrix, A::SparseMatrixCSCUnion{TvA}) where {TvA} = (T = promote_op(matprod, eltype(X), TvA); mul!(similar(X, T, (size(X, 1), size(A, 2))), X, A, true, false)) function mul!(C::StridedVecOrMat, X::AdjOrTransStridedOrTriangularMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) @@ -182,11 +182,20 @@ end # Sparse matrix multiplication as described in [Gustavson, 1978]: # http://dl.acm.org/citation.cfm?id=355796 -*(A::AbstractSparseMatrixCSC, B::AbstractSparseMatrixCSC) = spmatmul(A,B) -*(A::AbstractSparseMatrixCSC, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(A, copy(B)) -*(A::AbstractSparseMatrixCSC, B::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(A, copy(B)) -*(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::AbstractSparseMatrixCSC) = spmatmul(copy(A), B) -*(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::AbstractSparseMatrixCSC) = spmatmul(copy(A), B) +const SparseTriangular{Tv,Ti} = Union{UpperTriangular{Tv,<:SparseMatrixCSCUnion{Tv,Ti}},LowerTriangular{Tv,<:SparseMatrixCSCUnion{Tv,Ti}}} +const SparseOrTri{Tv,Ti} = Union{SparseMatrixCSCUnion{Tv,Ti},SparseTriangular{Tv,Ti}} + +*(A::SparseOrTri, B::AbstractSparseVector) = spmatmulv(A, B) +*(A::SparseOrTri, B::SparseColumnView) = spmatmulv(A, B) +*(A::SparseOrTri, B::SparseVectorView) = spmatmulv(A, B) +*(A::SparseMatrixCSCUnion, B::SparseMatrixCSCUnion) = spmatmul(A,B) +*(A::SparseTriangular, B::SparseMatrixCSCUnion) = spmatmul(A,B) +*(A::SparseMatrixCSCUnion, B::SparseTriangular) = spmatmul(A,B) +*(A::SparseTriangular, B::SparseTriangular) = spmatmul1(A,B) +*(A::SparseOrTri, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(A, copy(B)) +*(A::SparseOrTri, B::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(A, copy(B)) +*(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::SparseOrTri) = spmatmul(copy(A), B) +*(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::SparseOrTri) = spmatmul(copy(A), B) *(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(copy(A), copy(B)) *(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(copy(A), copy(B)) @@ -198,16 +207,15 @@ end # done by a quicksort of the row indices or by a full scan of the dense result vector. # The last is faster, if more than ≈ 1/32 of the result column is nonzero. # TODO: extend to SparseMatrixCSCUnion to allow for SubArrays (view(X, :, r)). -function spmatmul(A::AbstractSparseMatrixCSC{TvA,TiA}, B::AbstractSparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB} +function spmatmul(A::SparseOrTri{TvA,TiA}, B::Union{SparseOrTri{TvB,TiB},SparseVectorUnion{TvB,TiB},SubArray{TvB,<:Any,<:AbstractSparseArray{TvB,TiB}}}) where {TvA,TiA,TvB,TiB} + Tv = promote_op(matprod, TvA, TvB) Ti = promote_type(TiA, TiB) mA, nA = size(A) nB = size(B, 2) nA == size(B, 1) || throw(DimensionMismatch()) - rowvalA = rowvals(A); nzvalA = nonzeros(A) - rowvalB = rowvals(B); nzvalB = nonzeros(B) - nnzC = max(estimate_mulsize(mA, nnz(A), nA, nnz(B), nB) * 11 ÷ 10, mA) + nnzC = min(estimate_mulsize(mA, nnz(A), nA, nnz(B), nB) * 11 ÷ 10 + mA, mA*nB) colptrC = Vector{Ti}(undef, nB+1) rowvalC = Vector{Ti}(undef, nnzC) nzvalC = Vector{Tv}(undef, nnzC) @@ -221,45 +229,8 @@ function spmatmul(A::AbstractSparseMatrixCSC{TvA,TiA}, B::AbstractSparseMatrixCS resize!(rowvalC, nnzC) resize!(nzvalC, nnzC) end - colptrC[i] = ip0 = ip - k0 = ip - 1 - for jp in nzrange(B, i) - nzB = nzvalB[jp] - j = rowvalB[jp] - for kp in nzrange(A, j) - nzC = nzvalA[kp] * nzB - k = rowvalA[kp] - if xb[k] - nzvalC[k+k0] += nzC - else - nzvalC[k+k0] = nzC - xb[k] = true - rowvalC[ip] = k - ip += 1 - end - end - end - if ip > ip0 - if prefer_sort(ip-k0, mA) - # in-place sort of indices. Effort: O(nnz*ln(nnz)). - sort!(rowvalC, ip0, ip-1, QuickSort, Base.Order.Forward) - for vp = ip0:ip-1 - k = rowvalC[vp] - xb[k] = false - nzvalC[vp] = nzvalC[k+k0] - end - else - # scan result vector (effort O(mA)) - for k = 1:mA - if xb[k] - xb[k] = false - rowvalC[ip0] = k - nzvalC[ip0] = nzvalC[k+k0] - ip0 += 1 - end - end - end - end + colptrC[i] = ip + ip = spcolmul!(rowvalC, nzvalC, xb, i, ip, A, B) end colptrC[nB+1] = ip end @@ -272,6 +243,68 @@ function spmatmul(A::AbstractSparseMatrixCSC{TvA,TiA}, B::AbstractSparseMatrixCS return C end +# process single rhs column +function spcolmul!(rowvalC, nzvalC, xb, i, ip, A, B) + rowvalA = rowvals(A); nzvalA = nonzeros(A) + rowvalB = rowvals(B); nzvalB = nonzeros(B) + mA = size(A, 1) + ip0 = ip + k0 = ip - 1 + @inbounds begin + for jp in nzrange(B, i) + nzB = nzvalB[jp] + j = rowvalB[jp] + for kp in nzrange(A, j) + nzC = nzvalA[kp] * nzB + k = rowvalA[kp] + if xb[k] + nzvalC[k+k0] += nzC + else + nzvalC[k+k0] = nzC + xb[k] = true + rowvalC[ip] = k + ip += 1 + end + end + end + if ip > ip0 + if prefer_sort(ip-k0, mA) + # in-place sort of indices. Effort: O(nnz*ln(nnz)). + sort!(rowvalC, ip0, ip-1, QuickSort, Base.Order.Forward) + for vp = ip0:ip-1 + k = rowvalC[vp] + xb[k] = false + nzvalC[vp] = nzvalC[k+k0] + end + else + # scan result vector (effort O(mA)) + for k = 1:mA + if xb[k] + xb[k] = false + rowvalC[ip0] = k + nzvalC[ip0] = nzvalC[k+k0] + ip0 += 1 + end + end + end + end + end + return ip +end + +# special cases of same twin Upper/LowerTriangular +spmatmul1(A, B) = spmatmul(A, B) +function spmatmul1(A::UpperTriangular, B::UpperTriangular) + UpperTriangular(spmatmul(A, B)) +end +function spmatmul1(A::LowerTriangular, B::LowerTriangular) + LowerTriangular(spmatmul(A, B)) +end +# exploit spmatmul for sparse vectors and column views +function spmatmulv(A, B) + spmatmul(A, B)[:,1] +end + # estimated number of non-zeros in matrix product # it is assumed, that the non-zero indices are distributed independently and uniformly # in both matrices. Over-estimation is possible if that is not the case. @@ -765,7 +798,7 @@ function _ldiv!(U::UpperTriangularWrapped, B::StridedVecOrMat) end (\)(L::TriangularSparse, B::AbstractSparseMatrixCSC) = ldiv!(L, Array(B)) -(*)(L::TriangularSparse, B::AbstractSparseMatrixCSC) = lmul!(L, Array(B)) +#(*)(L::TriangularSparse, B::AbstractSparseMatrixCSC) = lmul!(L, Array(B)) ## end of triangular diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 6cb72428247e3..78ad80101dd8e 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -109,8 +109,15 @@ julia> nnz(A) ``` """ nnz(S::AbstractSparseMatrixCSC) = Int(getcolptr(S)[size(S, 2) + 1] - 1) -nnz(S::ReshapedArray{T,1,<:AbstractSparseMatrixCSC}) where T = nnz(parent(S)) -count(pred, S::AbstractSparseMatrixCSC) = count(pred, nzvalview(S)) + pred(zero(eltype(S)))*(prod(size(S)) - nnz(S)) +nnz(S::ReshapedArray{<:Any,1,<:AbstractSparseMatrixCSC}) = nnz(parent(S)) +nnz(S::UpperTriangular{<:Any,<:AbstractSparseMatrixCSC}) = nnz1(S) +nnz(S::LowerTriangular{<:Any,<:AbstractSparseMatrixCSC}) = nnz1(S) +nnz(S::SparseMatrixCSCView) = nnz1(S) +nnz1(S) = sum(length.(nzrange.(Ref(S), axes(S, 2)))) + +function count(pred, S::AbstractSparseMatrixCSC) + count(pred, nzvalview(S)) + pred(zero(eltype(S)))*(prod(size(S)) - nnz(S)) +end """ nonzeros(A) @@ -138,6 +145,8 @@ julia> nonzeros(A) """ nonzeros(S::SparseMatrixCSC) = getfield(S, :nzval) nonzeros(S::SparseMatrixCSCView) = nonzeros(S.parent) +nonzeros(S::UpperTriangular{<:Any,<:SparseMatrixCSCUnion}) = nonzeros(S.data) +nonzeros(S::LowerTriangular{<:Any,<:SparseMatrixCSCUnion}) = nonzeros(S.data) """ rowvals(A::AbstractSparseMatrixCSC) @@ -164,6 +173,8 @@ julia> rowvals(A) """ rowvals(S::SparseMatrixCSC) = getfield(S, :rowval) rowvals(S::SparseMatrixCSCView) = rowvals(S.parent) +rowvals(S::UpperTriangular{<:Any,<:SparseMatrixCSCUnion}) = rowvals(S.data) +rowvals(S::LowerTriangular{<:Any,<:SparseMatrixCSCUnion}) = rowvals(S.data) """ nzrange(A::AbstractSparseMatrixCSC, col::Integer) @@ -186,6 +197,8 @@ column. In conjunction with [`nonzeros`](@ref) and """ nzrange(S::AbstractSparseMatrixCSC, col::Integer) = getcolptr(S)[col]:(getcolptr(S)[col+1]-1) nzrange(S::SparseMatrixCSCView, col::Integer) = nzrange(S.parent, S.indices[2][col]) +nzrange(S::UpperTriangular{<:Any,<:SparseMatrixCSCUnion}, i::Integer) = nzrangeup(S.data, i) +nzrange(S::LowerTriangular{<:Any,<:SparseMatrixCSCUnion}, i::Integer) = nzrangelo(S.data, i) function Base.isstored(A::AbstractSparseMatrixCSC, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index 2cc9268e6fea8..077bb11583330 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -32,16 +32,35 @@ SparseVector(n::Integer, nzind::Vector{Ti}, nzval::Vector{Tv}) where {Tv,Ti} = # Define an alias for a view of a whole column of a SparseMatrixCSC. Many methods can be written for the # union of such a view and a SparseVector so we define an alias for such a union as well -const SparseColumnView{T} = SubArray{T,1,<:AbstractSparseMatrixCSC,Tuple{Base.Slice{Base.OneTo{Int}},Int},false} -const SparseVectorUnion{T} = Union{SparseVector{T}, SparseColumnView{T}} -const AdjOrTransSparseVectorUnion{T} = LinearAlgebra.AdjOrTrans{T, <:SparseVectorUnion{T}} +const SparseColumnView{Tv,Ti} = SubArray{Tv,1,<:AbstractSparseMatrixCSC{Tv,Ti},Tuple{Base.Slice{Base.OneTo{Int}},Int},false} +const SparseVectorView{Tv,Ti} = SubArray{Tv,1,<:AbstractSparseVector{Tv,Ti},Tuple{Base.Slice{Base.OneTo{Int}}},false} +const SparseVectorUnion{Tv,Ti} = Union{SparseVector{Tv,Ti}, SparseColumnView{Tv,Ti}, SparseVectorView{Tv,Ti}} +const AdjOrTransSparseVectorUnion{Tv,Ti} = LinearAlgebra.AdjOrTrans{Tv, <:SparseVectorUnion{Tv,Ti}} ### Basic properties size(x::SparseVector) = (getfield(x, :n),) -nnz(x::SparseVector) = length(nonzeros(x)) count(f, x::SparseVector) = count(f, nonzeros(x)) + f(zero(eltype(x)))*(length(x) - nnz(x)) +# implement the nnz - nzrange - nonzeros - rowvals interface for sparse vectors + +nnz(x::SparseVector) = length(nonzeros(x)) +function nnz(x::SparseColumnView) + rowidx, colidx = parentindices(x) + return length(nzrange(parent(x), colidx)) +end +nnz(x::SparseVectorView) = nnz(x.parent) + +""" + nzrange(x::SparseVectorUnion, col) + +Give the range of indices to the structural nonzero values of a sparse vector. +The column index `col` is ignored (assumed to be `1`). +""" +function nzrange(x::SparseVectorUnion, j::Integer) + j == 1 ? (1:nnz(x)) : throw(BoundsError(x, (":", j))) +end + nonzeros(x::SparseVector) = getfield(x, :nzval) function nonzeros(x::SparseColumnView) rowidx, colidx = parentindices(x) @@ -49,6 +68,7 @@ function nonzeros(x::SparseColumnView) @inbounds y = view(nonzeros(A), nzrange(A, colidx)) return y end +nonzeros(x::SparseVectorView) = nonzeros(parent(x)) nonzeroinds(x::SparseVector) = getfield(x, :nzind) function nonzeroinds(x::SparseColumnView) @@ -57,12 +77,12 @@ function nonzeroinds(x::SparseColumnView) @inbounds y = view(rowvals(A), nzrange(A, colidx)) return y end +nonzeroinds(x::SparseVectorView) = nonzeroinds(parent(x)) + +rowvals(x::SparseVectorUnion) = nonzeroinds(x) indtype(x::SparseColumnView) = indtype(parent(x)) -function nnz(x::SparseColumnView) - rowidx, colidx = parentindices(x) - return length(nzrange(parent(x), colidx)) -end +indtype(x::SparseVectorView) = indtype(parent(x)) ## similar # diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index a5d71f0bcce7a..b6a747c74b4b5 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -2996,4 +2996,84 @@ end end end +@testset "Multiplying with triangular sparse matrices #35609 #35610" begin + n = 10 + A = sprand(n, n, 5/n) + U = UpperTriangular(A) + L = LowerTriangular(A) + AM = Matrix(A) + UM = Matrix(U) + LM = Matrix(L) + Y = A * U + @test Y ≈ AM * UM + @test typeof(Y) == typeof(A) + Y = A * L + @test Y ≈ AM * LM + @test typeof(Y) == typeof(A) + Y = U * A + @test Y ≈ UM * AM + @test typeof(Y) == typeof(A) + Y = L * A + @test Y ≈ LM * AM + @test typeof(Y) == typeof(A) + Y = U * U + @test Y ≈ UM * UM + @test typeof(Y) == typeof(U) + Y = L * L + @test Y ≈ LM * LM + @test typeof(Y) == typeof(L) + Y = L * U + @test Y ≈ LM * UM + @test typeof(Y) == typeof(A) + Y = U * L + @test Y ≈ UM * LM + @test typeof(Y) == typeof(A) +end + +#testing the sparse matrix/vector access functions nnz, nzrange, rowvals, nonzeros +@testset "generic sparse matrix access functions" begin + I = [1,3,4,5, 1,3,4,5, 1,3,4,5]; + J = [4,4,4,4, 5,5,5,5, 6,6,6,6]; + V = [14,34,44,54, 15,35,45,55, 16,36,46,56]; + A = sparse(I, J, V, 9, 9); + AU = UpperTriangular(A) + AL = LowerTriangular(A) + b = SparseVector(9, I[1:4], V[1:4]) + c = view(A, :, 5) + d = view(b, :) + + @testset "nnz $n" for (n, M, nz) in (("A", A, 12), ("AU", AU, 11), ("AL", AL, 3), + ("b", b, 4), ("c", c, 4), ("d", d, 4)) + @test nnz(M) == nz + @test_throws BoundsError nzrange(M, 0) + @test_throws BoundsError nzrange(M, size(M, 2) + 1) + end + @testset "nzrange(A, $i)" for (i, nzr) in ((1,1:0),(4,1:4),(5,5:8),(6,9:12),(9,13:12)) + @test nzrange(A, i) == nzr + end + @testset "nzrange(AU, $i)" for (i, nzr) in ((2,1:0),(4,1:3),(5,5:8),(6,9:12),(8,13:12)) + @test nzrange(AU, i) == nzr + end + @testset "nzrange(AL, $i)" for (i, nzr) in ((3,1:0),(4,3:4),(5,8:8),(6,13:12),(7,13:12)) + @test nzrange(AL, i) == nzr + end + @test nzrange(b, 1) == 1:4 + @test nzrange(c, 1) == 1:4 + @test nzrange(d, 1) == 1:4 + + @test rowvals(A) == I + @test rowvals(AL) == I + @test rowvals(AL) == I + @test rowvals(b) == I[1:4] + @test rowvals(c) == I[5:8] + @test rowvals(d) == I[1:4] + + @test nonzeros(A) == V + @test nonzeros(AU) == V + @test nonzeros(AL) == V + @test nonzeros(b) == V[1:4] + @test nonzeros(c) == V[5:8] + @test nonzeros(d) == V[1:4] +end + end # module diff --git a/stdlib/SparseArrays/test/sparsevector.jl b/stdlib/SparseArrays/test/sparsevector.jl index 975a9f2885476..b69db8fdb1e2c 100644 --- a/stdlib/SparseArrays/test/sparsevector.jl +++ b/stdlib/SparseArrays/test/sparsevector.jl @@ -1442,4 +1442,19 @@ end @test nonzeros(A) !== nonzeros(B) end +@testset "multiplication of Triangular sparse matrices with sparse vectors #35642" begin + n = 10 + A = sprand(n, n, 5/n) + U = UpperTriangular(A) + L = LowerTriangular(A) + x = sprand(n, 5/n) + y = view(A, :, 6) + z = view(x, :) + ty = typeof + @testset "matvec multiplication $(ty(X)) * $(ty(v))" for X in (U, L), v in (x, y, z) + @test X * v ≈ Matrix(X) * Vector(v) + @test typeof(X * v) == typeof(x) + end +end + end # module From 147d5b101cea8c845b81c80943a8089bd59f5e1a Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 1 Jun 2020 14:58:40 -0400 Subject: [PATCH 080/232] clarify `show` doc strings (#36076) fixes #36072 --- base/multimedia.jl | 18 ++++++++++-------- base/show.jl | 23 +++++++++++++++-------- doc/src/base/io-network.md | 4 ++-- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/base/multimedia.jl b/base/multimedia.jl index a50ea4690c994..4729849cfbc8d 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -77,7 +77,7 @@ showable(::MIME{mime}, @nospecialize x) where {mime} = hasmethod(show, Tuple{IO, showable(m::AbstractString, @nospecialize x) = showable(MIME(m), x) """ - show(io, mime, x) + show(io::IO, mime, x) The [`display`](@ref) functions ultimately call `show` in order to write an object `x` as a given `mime` type to a given I/O stream `io` (usually a memory buffer), if possible. In order @@ -94,18 +94,20 @@ your images to be displayed on any PNG-capable `AbstractDisplay` (such as IJulia to `import Base.show` in order to add new methods to the built-in Julia function `show`. -The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` -output that calls `show` with 2 arguments. Therefore, this case should be handled by -defining a 2-argument `show(io::IO, x::MyType)` method. - Technically, the `MIME"mime"` macro defines a singleton type for the given `mime` string, which allows us to exploit Julia's dispatch mechanisms in determining how to display objects of any given type. -The first argument to `show` can be an [`IOContext`](@ref) specifying output format properties. -See [`IOContext`](@ref) for details. +The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` +output that calls `show` with 2 arguments, so it is not always necessary to add a method +for that case. If a type benefits from custom human-readable output though, +`show(::IO, ::MIME"text/plain", ::T)` should be defined. For example, the `Day` type uses +`1 day` as the output for the `text/plain` MIME type, and `Day(1)` as the output of 2-argument `show`. + +Container types generally implement 3-argument `show` by calling `show(io, MIME"text/plain"(), x)` +for elements `x`, with `:compact => true` set in an [`IOContext`](@ref) passed as the first argument. """ -show(stream, mime, x) +show(stream::IO, mime, x) show(io::IO, m::AbstractString, x) = show(io, MIME(m), x) """ diff --git a/base/show.jl b/base/show.jl index 17aa231a2b5c3..c361fb4dd88b9 100644 --- a/base/show.jl +++ b/base/show.jl @@ -265,9 +265,9 @@ the properties of that stream (note that `io` can itself be an `IOContext`). The following properties are in common use: - - `:compact`: Boolean specifying that small values should be printed more compactly, e.g. + - `:compact`: Boolean specifying that values should be printed more compactly, e.g. that numbers should be printed with fewer digits. This is set when printing array - elements. + elements. `:compact` output should not contain line breaks. - `:limit`: Boolean specifying that containers should be truncated, e.g. showing `…` in place of most elements. - `:displaysize`: A `Tuple{Int,Int}` giving the size in rows and columns to use for text @@ -358,14 +358,21 @@ function show_circular(io::IOContext, @nospecialize(x)) end """ - show(x) + show([io::IO = stdout], x) -Write an informative text representation of a value to the current output stream. New types -should overload `show(io::IO, x)` where the first argument is a stream. The representation used -by `show` generally includes Julia-specific formatting and type information. +Write a text representation of a value `x` to the output stream `io`. New types `T` +should overload `show(io::IO, x::T)`. The representation used by `show` generally +includes Julia-specific formatting and type information, and should be parseable +Julia code when possible. [`repr`](@ref) returns the output of `show` as a string. +To customize human-readable text output for objects of type `T`, define +`show(io::IO, ::MIME"text/plain", ::T)` instead. Checking the `:compact` +[`IOContext`](@ref) property of `io` in such methods is recommended, +since some containers show their elements by calling this method with +`:compact => true`. + See also [`print`](@ref), which writes un-decorated representations. # Examples @@ -376,10 +383,10 @@ julia> print("Hello World!") Hello World! ``` """ -show(x) = show(stdout::IO, x) - show(io::IO, @nospecialize(x)) = show_default(io, x) +show(x) = show(stdout::IO, x) + # avoid inferring show_default on the type of `x` show_default(io::IO, @nospecialize(x)) = _show_default(io, inferencebarrier(x)) diff --git a/doc/src/base/io-network.md b/doc/src/base/io-network.md index adaa8e8b53856..101cdc890f9a9 100644 --- a/doc/src/base/io-network.md +++ b/doc/src/base/io-network.md @@ -55,7 +55,7 @@ Base.IOContext(::IO, ::IOContext) ## Text I/O ```@docs -Base.show(::Any) +Base.show(::IO, ::Any) Base.summary Base.print Base.println @@ -93,7 +93,7 @@ Base.AbstractDisplay Base.Multimedia.display Base.Multimedia.redisplay Base.Multimedia.displayable -Base.show(::Any, ::Any, ::Any) +Base.show(::IO, ::Any, ::Any) Base.Multimedia.showable Base.repr(::MIME, ::Any) Base.MIME From fc279ca80f36f5e1e45c888045419d35c78a731e Mon Sep 17 00:00:00 2001 From: Shan Sikdar Date: Mon, 1 Jun 2020 23:26:12 -0400 Subject: [PATCH 081/232] add documentation for findnz w/ SparseVector (#36089) * add documentation for findnz w/ SparseVector * change docstring to include paramtype * Update abstractsparse.jl Co-authored-by: Viral B. Shah --- stdlib/SparseArrays/src/abstractsparse.jl | 2 +- stdlib/SparseArrays/src/sparsevector.jl | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/stdlib/SparseArrays/src/abstractsparse.jl b/stdlib/SparseArrays/src/abstractsparse.jl index 9cc33cdbc6e29..9e0495814c24d 100644 --- a/stdlib/SparseArrays/src/abstractsparse.jl +++ b/stdlib/SparseArrays/src/abstractsparse.jl @@ -103,7 +103,7 @@ function findprev(f::Function, v::AbstractSparseArray, i) end """ - findnz(A) + findnz(A::SparseMatrixCSC) Return a tuple `(I, J, V)` where `I` and `J` are the row and column indices of the stored ("structurally non-zero") values in sparse matrix `A`, and `V` is a vector of the values. diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index 077bb11583330..6a10dfc8531c3 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -752,6 +752,25 @@ end findall(p::Base.Fix2{typeof(in)}, x::SparseVector{<:Any,Ti}) where {Ti} = invoke(findall, Tuple{Base.Fix2{typeof(in)}, AbstractArray}, p, x) +""" + findnz(x::SparseVector) + +Return a tuple `(I, V)` where `I` is the indices of the stored ("structurally non-zero") +values in sparse vector `x` and `V` is a vector of the values. + +# Examples +```jldoctest +julia> x = sparsevec([1 2 0; 0 0 3; 0 4 0]) +9-element SparseVector{Int64,Int64} with 4 stored entries: + [1] = 1 + [4] = 2 + [6] = 4 + [8] = 3 + +julia> findnz(x) +([1, 4, 6, 8], [1, 2, 4, 3]) +``` +""" function findnz(x::SparseVector{Tv,Ti}) where {Tv,Ti} numnz = nnz(x) From be2c6430bd5ce1b80f8324fd23f3b4c6644ea7b3 Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Tue, 2 Jun 2020 16:26:33 +0200 Subject: [PATCH 082/232] Revert "Use norm instead of abs in generic lu factorization (#34575)" (#36096) This reverts commit ecc0c434fae2438c05093b0fb1443b21c5463825. --- stdlib/LinearAlgebra/src/lu.jl | 4 ++-- stdlib/LinearAlgebra/test/generic.jl | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lu.jl b/stdlib/LinearAlgebra/src/lu.jl index 18ba0bbe11e6a..c770f478d4f4a 100644 --- a/stdlib/LinearAlgebra/src/lu.jl +++ b/stdlib/LinearAlgebra/src/lu.jl @@ -140,9 +140,9 @@ function generic_lufact!(A::StridedMatrix{T}, ::Val{Pivot} = Val(true); # find index max kp = k if Pivot - amax = norm(zero(T)) + amax = abs(zero(T)) for i = k:m - absi = norm(A[i,k]) + absi = abs(A[i,k]) if absi > amax kp = i amax = absi diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index de7eade6030b3..9ca508d9a9908 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -365,8 +365,6 @@ LinearAlgebra.Transpose(a::ModInt{n}) where {n} = transpose(a) # Needed for pivoting: Base.abs(a::ModInt{n}) where {n} = a - LinearAlgebra.norm(a::ModInt{n}) where {n} = a - Base.:<(a::ModInt{n}, b::ModInt{n}) where {n} = a.k < b.k @test A*(lu(A, Val(true))\b) == b From 016410192516c56392be3653b44f7c82960ef769 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 2 Jun 2020 16:28:07 +0200 Subject: [PATCH 083/232] rename pop!(vector, idx, [default]) to popat! (#36070) * rename pop!(vector, idx, [default]) to popat! * popat! : explain what `default` is --- base/array.jl | 35 +++++++++++++++++++++++++++++++++-- base/dict.jl | 3 --- base/exports.jl | 1 + doc/src/base/collections.md | 1 + test/arrayops.jl | 14 +++++++------- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/base/array.jl b/base/array.jl index d52997bf7af53..8954f3e5b9d23 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1153,13 +1153,44 @@ function pop!(a::Vector) return item end -function pop!(a::Vector, i::Integer) +""" + popat!(a::Vector, i::Integer, [default]) + +Remove the item at the given `i` and return it. Subsequent items +are shifted to fill the resulting gap. +When `i` is not a valid index for `a`, return `default`, or throw an error if +`default` is not specified. +See also [`deleteat!`](@ref) and [`splice!`](@ref). + +!!! compat "Julia 1.5" + This function is available as of Julia 1.5. + +# Examples +```jldoctest +julia> a = [4, 3, 2, 1]; popat!(a, 2) +3 + +julia> a +3-element Array{Int64,1}: + 4 + 2 + 1 + +julia> popat!(a, 4, missing) +missing + +julia> popat!(a, 4) +ERROR: BoundsError: attempt to access 3-element Array{Int64,1} at index [4] +[...] +``` +""" +function popat!(a::Vector, i::Integer) x = a[i] _deleteat!(a, i, 1) x end -function pop!(a::Vector, i::Integer, default) +function popat!(a::Vector, i::Integer, default) if 1 <= i <= length(a) x = @inbounds a[i] _deleteat!(a, i, 1) diff --git a/base/dict.jl b/base/dict.jl index 65eacd9b1962b..1760e62338cf8 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -576,9 +576,6 @@ end Delete and return the mapping for `key` if it exists in `collection`, otherwise return `default`, or throw an error if `default` is not specified. -!!! compat "Julia 1.5" - For `collection::Vector`, this method requires at least Julia 1.5. - # Examples ```jldoctest julia> d = Dict("a"=>1, "b"=>2, "c"=>3); diff --git a/base/exports.jl b/base/exports.jl index 3d11cc0481931..f47e1f719cbf2 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -473,6 +473,7 @@ export append!, insert!, pop!, + popat!, prepend!, push!, resize!, diff --git a/doc/src/base/collections.md b/doc/src/base/collections.md index fae621b302871..383dbcda4f93e 100644 --- a/doc/src/base/collections.md +++ b/doc/src/base/collections.md @@ -269,6 +269,7 @@ Partially implemented by: ```@docs Base.push! Base.pop! +Base.popat! Base.pushfirst! Base.popfirst! Base.insert! diff --git a/test/arrayops.jl b/test/arrayops.jl index 6e915f8c83e9c..9ecd9f4a0bab2 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -467,17 +467,17 @@ end @test_throws BoundsError insert!(v, 5, 5) end -@testset "pop!(::Vector, i, [default])" begin +@testset "popat!(::Vector, i, [default])" begin a = [1, 2, 3, 4] - @test_throws BoundsError pop!(a, 0) - @test pop!(a, 0, "default") == "default" + @test_throws BoundsError popat!(a, 0) + @test popat!(a, 0, "default") == "default" @test a == 1:4 - @test_throws BoundsError pop!(a, 5) - @test pop!(a, 1) == 1 + @test_throws BoundsError popat!(a, 5) + @test popat!(a, 1) == 1 @test a == [2, 3, 4] - @test pop!(a, 2) == 3 + @test popat!(a, 2) == 3 @test a == [2, 4] - badpop() = @inbounds pop!([1], 2) + badpop() = @inbounds popat!([1], 2) @test_throws BoundsError badpop() end From 8c8f7a66a23465fae2fbdb9c672ba824fa24833e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 22 May 2020 14:06:11 -0400 Subject: [PATCH 084/232] fix argtypes of kwsorter when on the heap (#35995) --- src/builtins.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index c89745c48ea93..42ad5a1dcde15 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1038,18 +1038,19 @@ JL_CALLABLE(jl_f_invoke_kwsorter) size_t i, nt = jl_nparams(argtypes) + 2; if (nt < jl_page_size/sizeof(jl_value_t*)) { jl_value_t **types = (jl_value_t**)alloca(nt*sizeof(jl_value_t*)); - types[0] = (jl_value_t*)jl_namedtuple_type; types[1] = jl_typeof(func); - for(i=2; i < nt; i++) - types[i] = jl_tparam(argtypes,i-2); + types[0] = (jl_value_t*)jl_namedtuple_type; + types[1] = jl_typeof(func); + for (i = 2; i < nt; i++) + types[i] = jl_tparam(argtypes, i - 2); argtypes = (jl_value_t*)jl_apply_tuple_type_v(types, nt); } else { jl_svec_t *types = jl_alloc_svec_uninit(nt); JL_GC_PUSH1(&types); - jl_svecset(types, 0, jl_array_any_type); + jl_svecset(types, 0, jl_namedtuple_type); jl_svecset(types, 1, jl_typeof(func)); - for(i=2; i < nt; i++) - jl_svecset(types, i, jl_tparam(argtypes,i-2)); + for (i = 2; i < nt; i++) + jl_svecset(types, i, jl_tparam(argtypes, i - 2)); argtypes = (jl_value_t*)jl_apply_tuple_type(types); JL_GC_POP(); } From be9ab4873d42f52bc776aa29d6e301d55b314033 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 2 Jun 2020 16:50:43 -0400 Subject: [PATCH 085/232] micro-optimization for generic tuple allocation (#35995) Not a huge time savings, but saves us from needing to copy the `typeof` the argument array into a second buffer. --- src/builtins.c | 17 +---- src/gf.c | 35 +---------- src/jltypes.c | 145 ++++++++++++++++++++++++++++++++++++++++++- src/julia_internal.h | 1 + 4 files changed, 147 insertions(+), 51 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 42ad5a1dcde15..9d33258dd97f6 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -714,21 +714,8 @@ JL_CALLABLE(jl_f_tuple) size_t i; if (nargs == 0) return (jl_value_t*)jl_emptytuple; - jl_datatype_t *tt; - if (nargs < jl_page_size / sizeof(jl_value_t*)) { - jl_value_t **types = (jl_value_t**)alloca(nargs * sizeof(jl_value_t*)); - for (i = 0; i < nargs; i++) - types[i] = jl_typeof(args[i]); - tt = jl_inst_concrete_tupletype_v(types, nargs); - } - else { - jl_svec_t *types = jl_alloc_svec_uninit(nargs); - JL_GC_PUSH1(&types); - for (i = 0; i < nargs; i++) - jl_svecset(types, i, jl_typeof(args[i])); - tt = jl_inst_concrete_tupletype(types); - JL_GC_POP(); - } + jl_datatype_t *tt = jl_inst_arg_tuple_type(args[0], &args[1], nargs, 0); + JL_GC_PROMISE_ROOTED(tt); // it is a concrete type if (tt->instance != NULL) return tt->instance; jl_ptls_t ptls = jl_get_ptls_states(); diff --git a/src/gf.c b/src/gf.c index 183a6c74d7187..ef4694de438b2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1819,40 +1819,7 @@ void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs) { - jl_tupletype_t *tt; - size_t i; - int onstack = (nargs * sizeof(jl_value_t*) < jl_page_size); - jl_value_t **roots; - jl_value_t **types; - JL_GC_PUSHARGS(roots, onstack ? nargs : 1); - if (onstack) { - types = roots; - } - else { - roots[0] = (jl_value_t*)jl_alloc_svec(nargs); - types = jl_svec_data(roots[0]); - } - for (i = 0; i < nargs; i++) { - jl_value_t *ai = (i == 0 ? arg1 : args[i - 1]); - if (jl_is_type(ai)) { - // if `ai` has free type vars this will not be a valid (concrete) type. - // TODO: it would be really nice to only dispatch and cache those as - // `jl_typeof(ai)`, but that will require some redesign of the caching - // logic. - types[i] = (jl_value_t*)jl_wrap_Type(ai); - if (!onstack) - jl_gc_wb(roots[0], types[i]); - } - else { - types[i] = jl_typeof(ai); - } - } - if (onstack) - tt = jl_apply_tuple_type_v(types, nargs); - else - tt = jl_apply_tuple_type((jl_svec_t*)roots[0]); - JL_GC_POP(); - return tt; + return jl_inst_arg_tuple_type(arg1, args, nargs, 1); } jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, int cache, size_t world) diff --git a/src/jltypes.c b/src/jltypes.c index 50278be9d44a4..a595d3175cc05 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -572,7 +572,45 @@ static int typekey_eq(jl_datatype_t *tt, jl_value_t **key, size_t n) return 1; } +// These `value` functions return the same values as the primary functions, +// but operate on the typeof/Typeof each object in an array +static int typekeyvalue_eq(jl_datatype_t *tt, jl_value_t *key1, jl_value_t **key, size_t n, int leaf) +{ + size_t j; + // TOOD: This shouldn't be necessary + JL_GC_PROMISE_ROOTED(tt); + size_t tnp = jl_nparams(tt); + if (n != tnp) + return 0; + if (leaf && tt->name == jl_type_typename) { + // for Type{T}, require `typeof(T)` to match also, to avoid incorrect + // dispatch from changing the type of something. + // this should work because `Type`s don't have uids, and aren't the + // direct tags of values so we don't rely on pointer equality. + jl_value_t *kj = key1; + jl_value_t *tj = jl_tparam0(tt); + return (kj == tj || (jl_typeof(tj) == jl_typeof(kj) && jl_types_equal(tj, kj))); + } + for (j = 0; j < n; j++) { + jl_value_t *kj = j == 0 ? key1 : key[j - 1]; + jl_value_t *tj = jl_svecref(tt->parameters, j); + if (leaf && jl_is_type_type(tj)) { + jl_value_t *tp0 = jl_tparam0(tj); + if (!(kj == tp0 || (jl_typeof(tp0) == jl_typeof(kj) && jl_types_equal(tp0, kj)))) + return 0; + } + else if (jl_typeof(kj) != tj) { + return 0; + } + else if (leaf && jl_is_kind(tj)) { + return 0; + } + } + return 1; +} + static unsigned typekey_hash(jl_typename_t *tn, jl_value_t **key, size_t n, int nofail) JL_NOTSAFEPOINT; +static unsigned typekeyvalue_hash(jl_typename_t *tn, jl_value_t *key1, jl_value_t **key, size_t n, int leaf) JL_NOTSAFEPOINT; /* returns val if key is in hash, otherwise NULL */ static jl_datatype_t *lookup_type_set(jl_svec_t *cache, jl_value_t **key, size_t n, uint_t hv) @@ -597,6 +635,28 @@ static jl_datatype_t *lookup_type_set(jl_svec_t *cache, jl_value_t **key, size_t return NULL; } +/* returns val if key is in hash, otherwise NULL */ +static jl_datatype_t *lookup_type_setvalue(jl_svec_t *cache, jl_value_t *key1, jl_value_t **key, size_t n, uint_t hv, int leaf) +{ + size_t sz = jl_svec_len(cache); + if (sz == 0) + return NULL; + size_t maxprobe = max_probe(sz); + jl_datatype_t **tab = (jl_datatype_t**)jl_svec_data(cache); + size_t index = h2index(hv, sz); + size_t orig = index; + size_t iter = 0; + do { + jl_datatype_t *val = jl_atomic_load_relaxed(&tab[index]); + if (val == NULL) + return NULL; + if (val->hash == hv && typekeyvalue_eq(val, key1, key, n, leaf)) + return val; + index = (index + 1) & (sz - 1); + iter++; + } while (iter <= maxprobe && index != orig); + return NULL; +} // look up a type in a cache by binary or linear search. // if found, returns the index of the found item. if not found, returns @@ -618,6 +678,23 @@ static ssize_t lookup_type_idx_linear(jl_svec_t *cache, jl_value_t **key, size_t return ~cl; } +static ssize_t lookup_type_idx_linearvalue(jl_svec_t *cache, jl_value_t *key1, jl_value_t **key, size_t n) +{ + if (n == 0) + return -1; + jl_datatype_t **data = (jl_datatype_t**)jl_svec_data(cache); + size_t cl = jl_svec_len(cache); + ssize_t i; + for (i = 0; i < cl; i++) { + jl_datatype_t *tt = jl_atomic_load_relaxed(&data[i - 1]); + if (tt == NULL) + return ~i; + if (typekeyvalue_eq(tt, key1, key, n, 1)) + return i; + } + return ~cl; +} + static jl_value_t *lookup_type(jl_typename_t *tn, jl_value_t **key, size_t n) { JL_TIMING(TYPE_CACHE_LOOKUP); @@ -633,6 +710,22 @@ static jl_value_t *lookup_type(jl_typename_t *tn, jl_value_t **key, size_t n) } } +static jl_value_t *lookup_typevalue(jl_typename_t *tn, jl_value_t *key1, jl_value_t **key, size_t n, int leaf) +{ + JL_TIMING(TYPE_CACHE_LOOKUP); + unsigned hv = typekeyvalue_hash(tn, key1, key, n, leaf); + if (hv) { + jl_svec_t *cache = jl_atomic_load_relaxed(&tn->cache); + return (jl_value_t*)lookup_type_setvalue(cache, key1, key, n, hv, leaf); + } + else { + assert(leaf); + jl_svec_t *linearcache = jl_atomic_load_relaxed(&tn->linearcache); + ssize_t idx = lookup_type_idx_linearvalue(linearcache, key1, key, n); + return (idx < 0) ? NULL : jl_svecref(linearcache, idx); + } +} + static int cache_insert_type_set_(jl_svec_t *a, jl_datatype_t *val, uint_t hv) { jl_datatype_t **tab = (jl_datatype_t**)jl_svec_data(a); @@ -756,8 +849,6 @@ void jl_cache_type_(jl_datatype_t *type) cache_insert_type_set(type, hv); } else { - jl_value_t **key = jl_svec_data(type->parameters); - int n = jl_svec_len(type->parameters); ssize_t idx = lookup_type_idx_linear(type->name->linearcache, key, n); assert(idx < 0); cache_insert_type_linear(type, ~idx); @@ -1019,6 +1110,27 @@ static unsigned typekey_hash(jl_typename_t *tn, jl_value_t **key, size_t n, int return hash ? hash : 1; } +static unsigned typekeyvalue_hash(jl_typename_t *tn, jl_value_t *key1, jl_value_t **key, size_t n, int leaf) JL_NOTSAFEPOINT +{ + size_t j; + unsigned hash = 3; + for (j = 0; j < n; j++) { + jl_value_t *kj = j == 0 ? key1 : key[j - 1]; + uint_t hj; + if (leaf && jl_is_kind(jl_typeof(kj))) { + hj = typekey_hash(jl_type_typename, &kj, 1, 0); + if (hj == 0) + return 0; + } + else { + hj = ((jl_datatype_t*)jl_typeof(kj))->hash; + } + hash = bitmix(hj, hash); + } + hash = bitmix(~tn->hash, hash); + return hash ? hash : 1; +} + void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) { int istuple = (dt->name == jl_tuple_typename); @@ -1406,6 +1518,35 @@ jl_datatype_t *jl_inst_concrete_tupletype_v(jl_value_t **p, size_t np) return (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, NULL, p, np, 1, NULL, NULL); } +jl_tupletype_t *jl_inst_arg_tuple_type(jl_value_t *arg1, jl_value_t **args, size_t nargs, int leaf) +{ + jl_tupletype_t *tt = (jl_datatype_t*)lookup_typevalue(jl_tuple_typename, arg1, args, nargs, leaf); + if (tt == NULL) { + int cacheable = 1; + size_t i; + jl_svec_t *params = jl_alloc_svec(nargs); + JL_GC_PUSH1(¶ms); + for (i = 0; i < nargs; i++) { + jl_value_t *ai = (i == 0 ? arg1 : args[i - 1]); + if (leaf && jl_is_type(ai)) { + // if `ai` has free type vars this will not be a valid (concrete) type. + // TODO: it would be really nice to only dispatch and cache those as + // `jl_typeof(ai)`, but that will require some redesign of the caching + // logic. + ai = (jl_value_t*)jl_wrap_Type(ai); + cacheable = 0; + } + else { + ai = jl_typeof(ai); + } + jl_svecset(params, i, ai); + } + tt = (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, jl_svec_data(params), nargs, cacheable, NULL, NULL); + JL_GC_POP(); + } + return tt; +} + static jl_svec_t *inst_ftypes(jl_svec_t *p, jl_typeenv_t *env, jl_typestack_t *stack) { size_t i; diff --git a/src/julia_internal.h b/src/julia_internal.h index 7486b1c630642..61123c4b4423d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -443,6 +443,7 @@ int jl_subtype_invariant(jl_value_t *a, jl_value_t *b, int ta); int jl_has_concrete_subtype(jl_value_t *typ); jl_datatype_t *jl_inst_concrete_tupletype_v(jl_value_t **p, size_t np) JL_ALWAYS_LEAFTYPE; jl_datatype_t *jl_inst_concrete_tupletype(jl_svec_t *p) JL_ALWAYS_LEAFTYPE; +jl_tupletype_t *jl_inst_arg_tuple_type(jl_value_t *arg1, jl_value_t **args, size_t nargs, int leaf); JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype); jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_args_t fptr) JL_GC_DISABLED; jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, int *issubty); From ec1c536aa38fa91cdde75385f5343f693c5e0d8c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 2 Jun 2020 17:07:07 -0400 Subject: [PATCH 086/232] separate .ji writing and IR encoding implementations [NFC] (#36053) --- src/Makefile | 2 +- src/dump.c | 862 +++----------------------------------------- src/ircode.c | 932 ++++++++++++++++++++++++++++++++++++++++++++++++ src/serialize.h | 117 ++++++ 4 files changed, 1108 insertions(+), 805 deletions(-) create mode 100644 src/ircode.c create mode 100644 src/serialize.h diff --git a/src/Makefile b/src/Makefile index 5b1cecf67cfca..3153c0178d0a9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,7 +45,7 @@ RUNTIME_SRCS := \ simplevector runtime_intrinsics precompile \ threading partr stackwalk gc gc-debug gc-pages gc-stacks method \ jlapi signal-handling safepoint timing subtype \ - crc32c APInt-C processor + crc32c APInt-C processor ircode SRCS := jloptions runtime_ccall rtutils LLVMLINK := diff --git a/src/dump.c b/src/dump.c index 0e96c025ffbde..9c3b19b5f1a8d 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1,7 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license /* - saving and restoring system images + saving and restoring precompiled modules (.ji files) */ #include #include @@ -9,6 +9,7 @@ #include "julia.h" #include "julia_internal.h" #include "builtin_proto.h" +#include "serialize.h" #ifndef _OS_WINDOWS_ #include @@ -38,10 +39,9 @@ static htable_t common_symbol_tag; static jl_value_t *deser_symbols[256]; // table of all objects that have been deserialized, indexed by pos -// (the order in the serializer stream) in MODE_MODULE, the low +// (the order in the serializer stream). the low // bit is reserved for flagging certain entries and pos is // left shift by 1 -// (not used in MODE_IR) static htable_t backref_table; static int backref_table_numel; static arraylist_t backref_list; @@ -49,103 +49,27 @@ static arraylist_t backref_list; // list of (jl_value_t **loc, size_t pos) entries // for anything that was flagged by the deserializer for later // type-rewriting of some sort -// (not used in MODE_IR) static arraylist_t flagref_list; static htable_t uniquing_table; // list of (size_t pos, (void *f)(jl_value_t*)) entries // for the serializer to mark values in need of rework by function f // during deserialization later -// (not used in MODE_IR) static arraylist_t reinit_list; // list of stuff that is being serialized -// (only used by the incremental serializer in MODE_MODULE) // This is not quite globally rooted, but we take care to only // ever assigned rooted values here. static jl_array_t *serializer_worklist JL_GLOBALLY_ROOTED; // inverse of backedges tree -// (only used by the incremental serializer in MODE_MODULE) htable_t edges_map; // list of requested ccallable signatures static arraylist_t ccallable_list; -#define TAG_SYMBOL 2 -#define TAG_SSAVALUE 3 -#define TAG_DATATYPE 4 -#define TAG_SLOTNUMBER 5 -#define TAG_SVEC 6 -#define TAG_ARRAY 7 -#define TAG_NULL 8 -#define TAG_EXPR 9 -#define TAG_PHINODE 10 -#define TAG_PHICNODE 11 -#define TAG_LONG_SYMBOL 12 -#define TAG_LONG_SVEC 13 -#define TAG_LONG_EXPR 14 -#define TAG_LONG_PHINODE 15 -#define TAG_LONG_PHICNODE 16 -#define TAG_METHODROOT 17 -#define TAG_STRING 18 -#define TAG_SHORT_INT64 19 -#define TAG_SHORT_GENERAL 20 -#define TAG_CNULL 21 -#define TAG_ARRAY1D 22 -#define TAG_SINGLETON 23 -#define TAG_MODULE 24 -#define TAG_TVAR 25 -#define TAG_METHOD_INSTANCE 26 -#define TAG_METHOD 27 -#define TAG_CODE_INSTANCE 28 -#define TAG_COMMONSYM 29 -#define TAG_NEARBYGLOBAL 30 -#define TAG_GLOBALREF 31 -#define TAG_CORE 32 -#define TAG_BASE 33 -#define TAG_BITYPENAME 34 -#define TAG_NEARBYMODULE 35 -#define TAG_INT32 36 -#define TAG_INT64 37 -#define TAG_UINT8 38 -#define TAG_VECTORTY 39 -#define TAG_PTRTY 40 -#define TAG_LONG_SSAVALUE 41 -#define TAG_LONG_METHODROOT 42 -#define TAG_SHORTER_INT64 43 -#define TAG_SHORT_INT32 44 -#define TAG_CALL1 45 -#define TAG_CALL2 46 -#define TAG_LINEINFO 47 -#define TAG_SHORT_BACKREF 48 -#define TAG_BACKREF 49 -#define TAG_UNIONALL 50 -#define TAG_GOTONODE 51 -#define TAG_QUOTENODE 52 -#define TAG_GENERAL 53 - -#define LAST_TAG 53 - -typedef enum _DUMP_MODES { - // not in the serializer at all, or - // something is seriously wrong - MODE_INVALID = 0, - - // compressing / decompressing a CodeInfo - MODE_IR, - - // jl_restore_new_module - // restoring a single module from disk for integration - // into the currently running system image / environment - MODE_MODULE -} DUMP_MODES; - typedef struct { ios_t *s; - DUMP_MODES mode; - // method we're compressing for in MODE_IR - jl_method_t *method; jl_ptls_t ptls; jl_array_t *loaded_modules_array; } jl_serializer_state; @@ -156,72 +80,40 @@ static jl_value_t *jl_bigint_type = NULL; static int gmp_limb_size = 0; static arraylist_t builtin_typenames; -#define write_uint8(s, n) ios_putc((n), (s)) -#define read_uint8(s) ((uint8_t)ios_getc(s)) -#define write_int8(s, n) write_uint8(s, n) -#define read_int8(s) read_uint8(s) - -/* read and write in host byte order */ - -static void write_int32(ios_t *s, int32_t i) JL_NOTSAFEPOINT -{ - ios_write(s, (char*)&i, 4); -} - -static int32_t read_int32(ios_t *s) JL_NOTSAFEPOINT -{ - int32_t x = 0; - ios_read(s, (char*)&x, 4); - return x; -} - static void write_uint64(ios_t *s, uint64_t i) JL_NOTSAFEPOINT { ios_write(s, (char*)&i, 8); } -static uint64_t read_uint64(ios_t *s) JL_NOTSAFEPOINT +static void write_float64(ios_t *s, double x) JL_NOTSAFEPOINT { - uint64_t x = 0; - ios_read(s, (char*)&x, 8); - return x; + write_uint64(s, *((uint64_t*)&x)); } -static void write_int64(ios_t *s, int64_t i) JL_NOTSAFEPOINT +void *jl_lookup_ser_tag(jl_value_t *v) { - ios_write(s, (char*)&i, 8); + return ptrhash_get(&ser_tag, v); } -static void write_uint16(ios_t *s, uint16_t i) JL_NOTSAFEPOINT +void *jl_lookup_common_symbol(jl_value_t *v) { - ios_write(s, (char*)&i, 2); + return ptrhash_get(&common_symbol_tag, v); } -static uint16_t read_uint16(ios_t *s) JL_NOTSAFEPOINT +jl_value_t *jl_deser_tag(uint8_t tag) { - int16_t x = 0; - ios_read(s, (char*)&x, 2); - return x; + return deser_tag[tag]; } -static void write_float64(ios_t *s, double x) JL_NOTSAFEPOINT +jl_value_t *jl_deser_symbol(uint8_t tag) { - write_uint64(s, *((uint64_t*)&x)); + return deser_symbols[tag]; } -//#ifdef _P64 -//#define write_ulong(s, x) (write_uint64((s), (x))) -//#define read_ulong(s, x) (read_uint64((s), (x))) -//#else -//#define write_ulong(s, x) (write_uint32((s), (x))) -//#define read_ulong(s, x) (read_uint32((s), (x))) -//#endif - // --- serialize --- #define jl_serialize_value(s, v) jl_serialize_value_((s), (jl_value_t*)(v), 0) static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED; -static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc) JL_GC_DISABLED; static void jl_serialize_cnull(jl_serializer_state *s, jl_value_t *t) { @@ -485,38 +377,6 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m) write_int32(s->s, m->optlevel); } -static int is_ir_node(jl_value_t *v) -{ - // TODO: this accidentally copies QuoteNode(Expr(...)) and QuoteNode(svec(...)) - return jl_is_slot(v) || jl_is_ssavalue(v) || - jl_is_uniontype(v) || jl_is_expr(v) || jl_is_newvarnode(v) || - jl_is_svec(v) || jl_is_tuple(v) || ((jl_datatype_t*)jl_typeof(v))->instance || - jl_is_int32(v) || jl_is_int64(v) || jl_is_bool(v) || jl_is_uint8(v) || - jl_is_quotenode(v) || jl_is_gotonode(v) || jl_is_linenode(v) || jl_is_globalref(v) || - jl_is_phinode(v) || jl_is_phicnode(v) || jl_is_upsilonnode(v) || jl_is_pinode(v) || - jl_typeis(v, jl_lineinfonode_type); -} - -static int literal_val_id(jl_serializer_state *s, jl_value_t *v) JL_GC_DISABLED -{ - jl_array_t *rs = s->method->roots; - int i, l = jl_array_len(rs); - if (jl_is_symbol(v) || jl_is_concrete_type(v)) { - for (i = 0; i < l; i++) { - if (jl_array_ptr_ref(rs, i) == v) - return i; - } - } - else { - for (i = 0; i < l; i++) { - if (jl_egal(jl_array_ptr_ref(rs, i), v)) - return i; - } - } - jl_array_ptr_1d_push(rs, v); - return jl_array_len(rs) - 1; -} - static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED { if (v == NULL) { @@ -549,40 +409,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li return; } - if (s->mode == MODE_IR) { - if (v == (jl_value_t*)s->method->module) { - write_uint8(s->s, TAG_NEARBYMODULE); - return; - } - else if (jl_is_datatype(v) && ((jl_datatype_t*)v)->name == jl_array_typename && - jl_is_long(jl_tparam1(v)) && jl_unbox_long(jl_tparam1(v)) == 1 && - !((jl_datatype_t*)v)->hasfreetypevars) { - write_uint8(s->s, TAG_VECTORTY); - jl_serialize_value(s, jl_tparam0(v)); - return; - } - else if (jl_is_datatype(v) && ((jl_datatype_t*)v)->name == jl_pointer_typename && - !((jl_datatype_t*)v)->hasfreetypevars) { - write_uint8(s->s, TAG_PTRTY); - jl_serialize_value(s, jl_tparam0(v)); - return; - } - else if (!as_literal && !is_ir_node(v)) { - int id = literal_val_id(s, v); - assert(id >= 0); - if (id < 256) { - write_uint8(s->s, TAG_METHODROOT); - write_uint8(s->s, id); - } - else { - assert(id <= UINT16_MAX); - write_uint8(s->s, TAG_LONG_METHODROOT); - write_uint16(s->s, id); - } - return; - } - } - else if (jl_typeis(v, jl_string_type) && jl_string_len(v) == 0) { + if (jl_typeis(v, jl_string_type) && jl_string_len(v) == 0) { jl_serialize_value(s, jl_an_empty_string); return; } @@ -650,29 +477,6 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li } ios_write(s->s, jl_symbol_name((jl_sym_t*)v), l); } - else if (jl_is_globalref(v)) { - if (s->mode == MODE_IR && jl_globalref_mod(v) == s->method->module) { - write_uint8(s->s, TAG_NEARBYGLOBAL); - jl_serialize_value(s, jl_globalref_name(v)); - } - else { - write_uint8(s->s, TAG_GLOBALREF); - jl_serialize_value(s, jl_globalref_mod(v)); - jl_serialize_value(s, jl_globalref_name(v)); - } - } - else if (jl_is_ssavalue(v) && ((jl_ssavalue_t*)v)->id < 256 && ((jl_ssavalue_t*)v)->id >= 0) { - write_uint8(s->s, TAG_SSAVALUE); - write_uint8(s->s, ((jl_ssavalue_t*)v)->id); - } - else if (jl_is_ssavalue(v) && ((jl_ssavalue_t*)v)->id <= UINT16_MAX && ((jl_ssavalue_t*)v)->id >= 0) { - write_uint8(s->s, TAG_LONG_SSAVALUE); - write_uint16(s->s, ((jl_ssavalue_t*)v)->id); - } - else if (jl_typeis(v, jl_slotnumber_type) && jl_slot_number(v) <= UINT16_MAX && jl_slot_number(v) >= 0) { - write_uint8(s->s, TAG_SLOTNUMBER); - write_uint16(s->s, jl_slot_number(v)); - } else if (jl_is_array(v)) { jl_array_t *ar = (jl_array_t*)v; jl_value_t *et = jl_tparam0(jl_typeof(ar)); @@ -736,81 +540,6 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li ios_write(s->s, jl_array_typetagdata(ar), l); } } - else if (jl_is_expr(v)) { - jl_expr_t *e = (jl_expr_t*)v; - size_t l = jl_array_len(e->args); - if (e->head == call_sym) { - if (l == 2) { - write_uint8(s->s, TAG_CALL1); - jl_serialize_value(s, jl_exprarg(e, 0)); - jl_serialize_value(s, jl_exprarg(e, 1)); - return; - } - else if (l == 3) { - write_uint8(s->s, TAG_CALL2); - jl_serialize_value(s, jl_exprarg(e, 0)); - jl_serialize_value(s, jl_exprarg(e, 1)); - jl_serialize_value(s, jl_exprarg(e, 2)); - return; - } - } - if (l <= 255) { - write_uint8(s->s, TAG_EXPR); - write_uint8(s->s, (uint8_t)l); - } - else { - write_uint8(s->s, TAG_LONG_EXPR); - write_int32(s->s, l); - } - jl_serialize_value(s, e->head); - for (i = 0; i < l; i++) { - jl_serialize_value(s, jl_exprarg(e, i)); - } - } - else if (jl_is_phinode(v)) { - jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(v, 0); - jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(v, 1); - size_t l = jl_array_len(edges); - if (l <= 255 && jl_array_len(values) == l) { - write_uint8(s->s, TAG_PHINODE); - write_uint8(s->s, (uint8_t)l); - } - else { - write_uint8(s->s, TAG_LONG_PHINODE); - write_int32(s->s, l); - write_int32(s->s, jl_array_len(values)); - } - for (i = 0; i < l; i++) { - jl_serialize_value(s, jl_array_ptr_ref(edges, i)); - } - l = jl_array_len(values); - for (i = 0; i < l; i++) { - jl_serialize_value(s, jl_array_ptr_ref(values, i)); - } - } - else if (jl_is_phicnode(v)) { - jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(v, 0); - size_t l = jl_array_len(values); - if (l <= 255) { - write_uint8(s->s, TAG_PHICNODE); - write_uint8(s->s, (uint8_t)l); - } - else { - write_uint8(s->s, TAG_LONG_PHICNODE); - write_int32(s->s, l); - } - for (i = 0; i < l; i++) { - jl_serialize_value(s, jl_array_ptr_ref(values, i)); - } - } - else if (jl_is_gotonode(v)) { - write_uint8(s->s, TAG_GOTONODE); - jl_serialize_value(s, jl_get_nth_field(v, 0)); - } - else if (jl_is_quotenode(v)) { - write_uint8(s->s, TAG_QUOTENODE); - jl_serialize_value(s, jl_get_nth_field(v, 0)); - } else if (jl_is_datatype(v)) { jl_serialize_datatype(s, (jl_datatype_t*)v); } @@ -905,7 +634,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li return; jl_serialize_value(s, (jl_value_t*)mi->sparam_vals); jl_array_t *backedges = mi->backedges; - if (s->mode == MODE_MODULE && backedges) { + if (backedges) { // filter backedges to only contain pointers // to items that we will actually store (internal == 2) size_t ins, i, l = jl_array_len(backedges); @@ -994,11 +723,6 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li jl_serialize_value(s, jl_typeof(v)); return; } - else if (jl_typeis(v, jl_lineinfonode_type)) { - write_uint8(s->s, TAG_LINEINFO); - for (i = 0; i < jl_datatype_nfields(jl_lineinfonode_type); i++) - jl_serialize_value(s, jl_get_nth_field(v, i)); - } else if (jl_bigint_type && jl_typeis(v, jl_bigint_type)) { write_uint8(s->s, TAG_SHORT_GENERAL); write_uint8(s->s, jl_datatype_size(jl_bigint_type)); @@ -1013,7 +737,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li else { jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); if (v == t->instance) { - if (s->mode == MODE_MODULE && !type_in_worklist(t)) { + if (!type_in_worklist(t)) { // also flag this in the backref table as special // if it might not be unique (is external) uintptr_t *bp = (uintptr_t*)ptrhash_bp(&backref_table, v); @@ -1432,6 +1156,8 @@ static int64_t write_dependency_list(ios_t *s, jl_array_t **udepsp, jl_array_t * // --- deserialize --- +static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc) JL_GC_DISABLED; + static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_value_t **loc) JL_GC_DISABLED { int tag = read_uint8(s->s); @@ -1459,7 +1185,6 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v assert(0 && "corrupt deserialization state"); abort(); } - assert(s->method == NULL && s->mode != MODE_IR && "no new data-types expected during MODE_IR"); assert(pos == backref_list.len - 1 && "nothing should have been deserialized since assigning pos"); backref_list.items[pos] = dt; dt->size = size; @@ -1545,15 +1270,13 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v static jl_value_t *jl_deserialize_value_svec(jl_serializer_state *s, uint8_t tag) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); size_t i, len; if (tag == TAG_SVEC) len = read_uint8(s->s); else len = read_int32(s->s); jl_svec_t *sv = jl_alloc_svec_uninit(len); - if (usetable) - arraylist_push(&backref_list, (jl_value_t*)sv); + arraylist_push(&backref_list, (jl_value_t*)sv); jl_value_t **data = jl_svec_data(sv); for (i = 0; i < len; i++) { data[i] = jl_deserialize_value(s, &data[i]); @@ -1563,7 +1286,6 @@ static jl_value_t *jl_deserialize_value_svec(jl_serializer_state *s, uint8_t tag static jl_value_t *jl_deserialize_value_symbol(jl_serializer_state *s, uint8_t tag) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); size_t len; if (tag == TAG_SYMBOL) len = read_uint8(s->s); @@ -1575,14 +1297,12 @@ static jl_value_t *jl_deserialize_value_symbol(jl_serializer_state *s, uint8_t t jl_value_t *sym = (jl_value_t*)jl_symbol(name); if (len >= 256) free(name); - if (usetable) - arraylist_push(&backref_list, sym); + arraylist_push(&backref_list, sym); return sym; } static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t tag) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); int16_t i, ndims; int isptr, isunion, hasptr, elsize; if (tag == TAG_ARRAY1D) { @@ -1602,16 +1322,14 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t ta elsize = elsize & 0x3fff; } uintptr_t pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, NULL); + arraylist_push(&backref_list, NULL); size_t *dims = (size_t*)alloca(ndims * sizeof(size_t)); for (i = 0; i < ndims; i++) { dims[i] = jl_unbox_long(jl_deserialize_value(s, NULL)); } jl_array_t *a = jl_new_array_for_deserialization( (jl_value_t*)NULL, ndims, dims, !isptr, hasptr, isunion, elsize); - if (usetable) - backref_list.items[pos] = a; + backref_list.items[pos] = a; jl_value_t *aty = jl_deserialize_value(s, &jl_astaggedvalue(a)->type); jl_set_typeof(a, aty); if (a->flags.ptrarray) { @@ -1656,96 +1374,14 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t ta return (jl_value_t*)a; } -static jl_value_t *jl_deserialize_value_expr(jl_serializer_state *s, uint8_t tag) JL_GC_DISABLED -{ - int usetable = (s->mode != MODE_IR); - size_t i, len; - jl_sym_t *head = NULL; - if (tag == TAG_EXPR) { - len = read_uint8(s->s); - } - else if (tag == TAG_CALL1) { - len = 2; - head = call_sym; - } - else if (tag == TAG_CALL2) { - len = 3; - head = call_sym; - } - else { - len = read_int32(s->s); - } - int pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, NULL); - if (head == NULL) - head = (jl_sym_t*)jl_deserialize_value(s, NULL); - jl_expr_t *e = jl_exprn(head, len); - if (usetable) - backref_list.items[pos] = e; - jl_value_t **data = (jl_value_t**)(e->args->data); - for (i = 0; i < len; i++) { - data[i] = jl_deserialize_value(s, &data[i]); - } - return (jl_value_t*)e; -} - -static jl_value_t *jl_deserialize_value_phi(jl_serializer_state *s, uint8_t tag) JL_GC_DISABLED -{ - int usetable = (s->mode != MODE_IR); - size_t i, len_e, len_v; - if (tag == TAG_PHINODE) { - len_e = len_v = read_uint8(s->s); - } - else { - len_e = read_int32(s->s); - len_v = read_int32(s->s); - } - jl_array_t *e = jl_alloc_vec_any(len_e); - jl_array_t *v = jl_alloc_vec_any(len_v); - jl_value_t *phi = jl_new_struct(jl_phinode_type, e, v); - if (usetable) - arraylist_push(&backref_list, phi); - jl_value_t **data_e = (jl_value_t**)(e->data); - for (i = 0; i < len_e; i++) { - data_e[i] = jl_deserialize_value(s, &data_e[i]); - } - jl_value_t **data_v = (jl_value_t**)(v->data); - for (i = 0; i < len_v; i++) { - data_v[i] = jl_deserialize_value(s, &data_v[i]); - } - return phi; -} - -static jl_value_t *jl_deserialize_value_phic(jl_serializer_state *s, uint8_t tag) JL_GC_DISABLED -{ - int usetable = (s->mode != MODE_IR); - size_t i, len; - if (tag == TAG_PHICNODE) - len = read_uint8(s->s); - else - len = read_int32(s->s); - jl_array_t *v = jl_alloc_vec_any(len); - jl_value_t *phic = jl_new_struct(jl_phicnode_type, v); - if (usetable) - arraylist_push(&backref_list, phic); - jl_value_t **data = (jl_value_t**)(v->data); - for (i = 0; i < len; i++) { - data[i] = jl_deserialize_value(s, &data[i]); - } - return phic; -} - static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_t **loc) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); jl_method_t *m = (jl_method_t*)jl_gc_alloc(s->ptls, sizeof(jl_method_t), jl_method_type); memset(m, 0, sizeof(jl_method_t)); uintptr_t pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, m); + arraylist_push(&backref_list, m); m->sig = (jl_value_t*)jl_deserialize_value(s, (jl_value_t**)&m->sig); jl_gc_wb(m, m->sig); m->module = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&m->module); @@ -1804,14 +1440,12 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ static jl_value_t *jl_deserialize_value_method_instance(jl_serializer_state *s, jl_value_t **loc) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); jl_method_instance_t *mi = (jl_method_instance_t*)jl_gc_alloc(s->ptls, sizeof(jl_method_instance_t), jl_method_instance_type); memset(mi, 0, sizeof(jl_method_instance_t)); uintptr_t pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, mi); + arraylist_push(&backref_list, mi); int internal = read_uint8(s->s); mi->specTypes = (jl_value_t*)jl_deserialize_value(s, (jl_value_t**)&mi->specTypes); jl_gc_wb(mi, mi->specTypes); @@ -1842,12 +1476,10 @@ static jl_value_t *jl_deserialize_value_method_instance(jl_serializer_state *s, static jl_value_t *jl_deserialize_value_code_instance(jl_serializer_state *s, jl_value_t **loc) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); jl_code_instance_t *codeinst = (jl_code_instance_t*)jl_gc_alloc(s->ptls, sizeof(jl_code_instance_t), jl_code_instance_type); memset(codeinst, 0, sizeof(jl_code_instance_t)); - if (usetable) - arraylist_push(&backref_list, codeinst); + arraylist_push(&backref_list, codeinst); int flags = read_uint8(s->s); int validate = (flags >> 0) & 3; int constret = (flags >> 2) & 1; @@ -1871,10 +1503,8 @@ static jl_value_t *jl_deserialize_value_code_instance(jl_serializer_state *s, jl static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); uintptr_t pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, NULL); + arraylist_push(&backref_list, NULL); jl_sym_t *mname = (jl_sym_t*)jl_deserialize_value(s, NULL); int ref_only = read_uint8(s->s); if (ref_only) { @@ -1883,13 +1513,11 @@ static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) JL_GC_DIS m_ref = jl_get_global((jl_module_t*)jl_deserialize_value(s, NULL), mname); else m_ref = jl_array_ptr_ref(s->loaded_modules_array, read_int32(s->s)); - if (usetable) - backref_list.items[pos] = m_ref; + backref_list.items[pos] = m_ref; return m_ref; } jl_module_t *m = jl_new_module(mname); - if (usetable) - backref_list.items[pos] = m; + backref_list.items[pos] = m; m->parent = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&m->parent); jl_gc_wb(m, m->parent); @@ -1930,44 +1558,20 @@ static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) JL_GC_DIS return (jl_value_t*)m; } -static jl_value_t *jl_deserialize_value_globalref(jl_serializer_state *s) JL_GC_DISABLED -{ - int usetable = (s->mode != MODE_IR); - if (usetable) { - jl_value_t *v = jl_new_struct_uninit(jl_globalref_type); - arraylist_push(&backref_list, v); - jl_value_t **data = jl_data_ptr(v); - data[0] = jl_deserialize_value(s, &data[0]); - data[1] = jl_deserialize_value(s, &data[1]); - return v; - } - else { - jl_value_t *mod = jl_deserialize_value(s, NULL); - jl_value_t *var = jl_deserialize_value(s, NULL); - return jl_module_globalref((jl_module_t*)mod, (jl_sym_t*)var); - } -} - static jl_value_t *jl_deserialize_value_singleton(jl_serializer_state *s, jl_value_t **loc) JL_GC_DISABLED { - if (s->mode == MODE_IR) { - jl_datatype_t *dt = (jl_datatype_t*)jl_deserialize_value(s, NULL); - return dt->instance; - } jl_value_t *v = (jl_value_t*)jl_gc_alloc(s->ptls, 0, NULL); uintptr_t pos = backref_list.len; arraylist_push(&backref_list, (void*)v); - if (s->mode == MODE_MODULE) { - // TODO: optimize the case where the value can easily be obtained - // from an external module (tag == 6) as dt->instance - assert(loc != HT_NOTFOUND); - // if loc == NULL, then the caller can't provide the address where the instance will be - // stored. this happens if a field might store a 0-size value, but the field itself is - // not 0 size, e.g. `::Union{Int,Nothing}` - if (loc != NULL) { - arraylist_push(&flagref_list, loc); - arraylist_push(&flagref_list, (void*)pos); - } + // TODO: optimize the case where the value can easily be obtained + // from an external module (tag == 6) as dt->instance + assert(loc != HT_NOTFOUND); + // if loc == NULL, then the caller can't provide the address where the instance will be + // stored. this happens if a field might store a 0-size value, but the field itself is + // not 0 size, e.g. `::Union{Int,Nothing}` + if (loc != NULL) { + arraylist_push(&flagref_list, loc); + arraylist_push(&flagref_list, (void*)pos); } jl_datatype_t *dt = (jl_datatype_t*)jl_deserialize_value(s, (jl_value_t**)HT_NOTFOUND); // no loc, since if dt is replaced, then dt->instance would be also jl_set_typeof(v, dt); @@ -2013,16 +1617,14 @@ static void jl_deserialize_struct(jl_serializer_state *s, jl_value_t *v) JL_GC_D static jl_value_t *jl_deserialize_value_any(jl_serializer_state *s, uint8_t tag, jl_value_t **loc) JL_GC_DISABLED { - int usetable = (s->mode != MODE_IR); int32_t sz = (tag == TAG_SHORT_GENERAL ? read_uint8(s->s) : read_int32(s->s)); jl_value_t *v = jl_gc_alloc(s->ptls, sz, NULL); jl_set_typeof(v, (void*)(intptr_t)0x50); uintptr_t pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); jl_datatype_t *dt = (jl_datatype_t*)jl_deserialize_value(s, &jl_astaggedvalue(v)->type); - assert(s->mode == MODE_IR || sz != 0 || loc); - if (s->mode == MODE_MODULE && dt == jl_typename_type) { + assert(sz != 0 || loc); + if (dt == jl_typename_type) { int internal = read_uint8(s->s); jl_typename_t *tn; if (internal) { @@ -2032,8 +1634,7 @@ static jl_value_t *jl_deserialize_value_any(jl_serializer_state *s, uint8_t tag, tn->cache = jl_emptysvec; // the cache is refilled later (tag 5) tn->linearcache = jl_emptysvec; // the cache is refilled later (tag 5) tn->partial = NULL; - if (usetable) - backref_list.items[pos] = tn; + backref_list.items[pos] = tn; } jl_module_t *m = (jl_module_t*)jl_deserialize_value(s, NULL); jl_sym_t *sym = (jl_sym_t*)jl_deserialize_value(s, NULL); @@ -2052,8 +1653,7 @@ static jl_value_t *jl_deserialize_value_any(jl_serializer_state *s, uint8_t tag, jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(jl_get_global(m, sym)); assert(jl_is_datatype(dt)); tn = dt->name; - if (usetable) - backref_list.items[pos] = tn; + backref_list.items[pos] = tn; } return (jl_value_t*)tn; } @@ -2086,14 +1686,12 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc uint8_t tag = read_uint8(s->s); if (tag > LAST_TAG) return deser_tag[tag]; - int usetable = (s->mode != MODE_IR); switch (tag) { case TAG_NULL: return NULL; case 0: tag = read_uint8(s->s); return deser_tag[tag]; - case TAG_BACKREF: JL_FALLTHROUGH; case TAG_SHORT_BACKREF: - assert(s->method == NULL && s->mode != MODE_IR); + case TAG_BACKREF: JL_FALLTHROUGH; case TAG_SHORT_BACKREF: ; uintptr_t offs = (tag == TAG_BACKREF) ? read_int32(s->s) : read_uint16(s->s); int isflagref = 0; isflagref = !!(offs & 1); @@ -2111,64 +1709,27 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc } } return (jl_value_t*)bp; - case TAG_METHODROOT: - return jl_array_ptr_ref(s->method->roots, read_uint8(s->s)); - case TAG_LONG_METHODROOT: - return jl_array_ptr_ref(s->method->roots, read_uint16(s->s)); case TAG_SVEC: JL_FALLTHROUGH; case TAG_LONG_SVEC: return jl_deserialize_value_svec(s, tag); case TAG_COMMONSYM: return deser_symbols[read_uint8(s->s)]; case TAG_SYMBOL: JL_FALLTHROUGH; case TAG_LONG_SYMBOL: return jl_deserialize_value_symbol(s, tag); - case TAG_SSAVALUE: - v = jl_box_ssavalue(read_uint8(s->s)); - if (usetable) - arraylist_push(&backref_list, v); - return v; - case TAG_LONG_SSAVALUE: - v = jl_box_ssavalue(read_uint16(s->s)); - if (usetable) - arraylist_push(&backref_list, v); - return v; - case TAG_SLOTNUMBER: - v = jl_box_slotnumber(read_uint16(s->s)); - if (usetable) - arraylist_push(&backref_list, v); - return v; case TAG_ARRAY: JL_FALLTHROUGH; case TAG_ARRAY1D: return jl_deserialize_value_array(s, tag); - case TAG_EXPR: JL_FALLTHROUGH; - case TAG_LONG_EXPR: JL_FALLTHROUGH; - case TAG_CALL1: JL_FALLTHROUGH; - case TAG_CALL2: - return jl_deserialize_value_expr(s, tag); - case TAG_PHINODE: JL_FALLTHROUGH; case TAG_LONG_PHINODE: - return jl_deserialize_value_phi(s, tag); - case TAG_PHICNODE: JL_FALLTHROUGH; case TAG_LONG_PHICNODE: - return jl_deserialize_value_phic(s, tag); - case TAG_GOTONODE: JL_FALLTHROUGH; case TAG_QUOTENODE: - v = jl_new_struct_uninit(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type); - if (usetable) - arraylist_push(&backref_list, v); - set_nth_field(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type, (void*)v, 0, jl_deserialize_value(s, NULL)); - return v; case TAG_UNIONALL: pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, NULL); + arraylist_push(&backref_list, NULL); if (read_uint8(s->s)) { jl_module_t *m = (jl_module_t*)jl_deserialize_value(s, NULL); jl_sym_t *sym = (jl_sym_t*)jl_deserialize_value(s, NULL); jl_value_t *v = jl_get_global(m, sym); assert(jl_is_unionall(v)); - if (usetable) - backref_list.items[pos] = v; + backref_list.items[pos] = v; return v; } v = jl_gc_alloc(s->ptls, sizeof(jl_unionall_t), jl_unionall_type); - if (usetable) - backref_list.items[pos] = v; + backref_list.items[pos] = v; ((jl_unionall_t*)v)->var = (jl_tvar_t*)jl_deserialize_value(s, (jl_value_t**)&((jl_unionall_t*)v)->var); jl_gc_wb(v, ((jl_unionall_t*)v)->var); ((jl_unionall_t*)v)->body = jl_deserialize_value(s, &((jl_unionall_t*)v)->body); @@ -2177,8 +1738,7 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc case TAG_TVAR: v = jl_gc_alloc(s->ptls, sizeof(jl_tvar_t), jl_tvar_type); jl_tvar_t *tv = (jl_tvar_t*)v; - if (usetable) - arraylist_push(&backref_list, tv); + arraylist_push(&backref_list, tv); tv->name = (jl_sym_t*)jl_deserialize_value(s, NULL); jl_gc_wb(tv, tv->name); tv->lb = jl_deserialize_value(s, &tv->lb); @@ -2196,59 +1756,38 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc return jl_deserialize_value_module(s); case TAG_SHORTER_INT64: v = jl_box_int64((int16_t)read_uint16(s->s)); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); return v; case TAG_SHORT_INT64: v = jl_box_int64(read_int32(s->s)); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); return v; case TAG_INT64: v = jl_box_int64((int64_t)read_uint64(s->s)); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); return v; case TAG_SHORT_INT32: v = jl_box_int32((int16_t)read_uint16(s->s)); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); return v; case TAG_INT32: v = jl_box_int32(read_int32(s->s)); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); return v; case TAG_UINT8: return jl_box_uint8(read_uint8(s->s)); - case TAG_NEARBYGLOBAL: - assert(s->method != NULL); - v = jl_deserialize_value(s, NULL); - return jl_module_globalref(s->method->module, (jl_sym_t*)v); - case TAG_NEARBYMODULE: - assert(s->method != NULL); - return (jl_value_t*)s->method->module; - case TAG_GLOBALREF: - return jl_deserialize_value_globalref(s); case TAG_SINGLETON: return jl_deserialize_value_singleton(s, loc); case TAG_CORE: return (jl_value_t*)jl_core_module; case TAG_BASE: return (jl_value_t*)jl_base_module; - case TAG_VECTORTY: - v = jl_deserialize_value(s, NULL); - return jl_apply_type2((jl_value_t*)jl_array_type, v, jl_box_long(1)); - case TAG_PTRTY: - v = jl_deserialize_value(s, NULL); - return jl_apply_type1((jl_value_t*)jl_pointer_type, v); case TAG_CNULL: v = jl_gc_alloc(s->ptls, sizeof(void*), NULL); jl_set_typeof(v, (void*)(intptr_t)0x50); *(void**)v = NULL; uintptr_t pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); jl_set_typeof(v, jl_deserialize_value(s, &jl_astaggedvalue(v)->type)); return v; case TAG_BITYPENAME: @@ -2257,14 +1796,12 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc case TAG_STRING: n = read_int32(s->s); v = jl_alloc_string(n); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); ios_readall(s->s, jl_string_data(v), n); return v; case TAG_LINEINFO: v = jl_new_struct_uninit(jl_lineinfonode_type); - if (usetable) - arraylist_push(&backref_list, v); + arraylist_push(&backref_list, v); for (i = 0; i < jl_datatype_nfields(jl_lineinfonode_type); i++) { size_t offs = jl_field_offset(jl_lineinfonode_type, i); set_nth_field(jl_lineinfonode_type, (void*)v, i, jl_deserialize_value(s, (jl_value_t**)((char*)v + offs))); @@ -2272,8 +1809,7 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc return v; case TAG_DATATYPE: pos = backref_list.len; - if (usetable) - arraylist_push(&backref_list, NULL); + arraylist_push(&backref_list, NULL); return jl_deserialize_datatype(s, pos, loc); default: assert(tag == TAG_GENERAL || tag == TAG_SHORT_GENERAL); @@ -2555,286 +2091,6 @@ JL_DLLEXPORT void jl_init_restored_modules(jl_array_t *init_order) // --- entry points --- -JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) -{ - JL_TIMING(AST_COMPRESS); - JL_LOCK(&m->writelock); // protect the roots array (Might GC) - assert(jl_is_method(m)); - assert(jl_is_code_info(code)); - ios_t dest; - ios_mem(&dest, 0); - int en = jl_gc_enable(0); // Might GC - size_t i; - - if (m->roots == NULL) { - m->roots = jl_alloc_vec_any(0); - jl_gc_wb(m, m->roots); - } - jl_serializer_state s = { - &dest, MODE_IR, - m, - jl_get_ptls_states(), - NULL - }; - - uint8_t flags = (code->inferred << 3) - | (code->inlineable << 2) - | (code->propagate_inbounds << 1) - | (code->pure << 0); - write_uint8(s.s, flags); - - size_t nslots = jl_array_len(code->slotflags); - assert(nslots >= m->nargs && nslots < INT32_MAX); // required by generated functions - write_int32(s.s, nslots); - ios_write(s.s, (char*)jl_array_data(code->slotflags), nslots); - - // N.B.: The layout of everything before this point is explicitly referenced - // by the various jl_ir_ accessors. Make sure to adjust those if you change - // the data layout. - - for (i = 0; i < 6; i++) { - int copy = 1; - if (i == 1) { // skip codelocs - assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, codelocs)); - continue; - } - if (i == 4) { // don't copy contents of method_for_inference_limit_heuristics field - assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, method_for_inference_limit_heuristics)); - copy = 0; - } - jl_serialize_value_(&s, jl_get_nth_field((jl_value_t*)code, i), copy); - } - - if (m->generator) - // can't optimize generated functions - jl_serialize_value_(&s, (jl_value_t*)jl_compress_argnames(code->slotnames), 1); - else - jl_serialize_value(&s, jl_nothing); - - size_t nstmt = jl_array_len(code->code); - assert(nstmt == jl_array_len(code->codelocs)); - if (jl_array_len(code->linetable) < 256) { - for (i = 0; i < nstmt; i++) { - write_uint8(s.s, ((int32_t*)jl_array_data(code->codelocs))[i]); - } - } - else if (jl_array_len(code->linetable) < 65536) { - for (i = 0; i < nstmt; i++) { - write_uint16(s.s, ((int32_t*)jl_array_data(code->codelocs))[i]); - } - } - else { - ios_write(s.s, (char*)jl_array_data(code->codelocs), nstmt * sizeof(int32_t)); - } - - ios_flush(s.s); - jl_array_t *v = jl_take_buffer(&dest); - ios_close(s.s); - if (jl_array_len(m->roots) == 0) { - m->roots = NULL; - } - JL_GC_PUSH1(&v); - jl_gc_enable(en); - JL_UNLOCK(&m->writelock); // Might GC - JL_GC_POP(); - return v; -} - -JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data) -{ - if (jl_is_code_info(data)) - return (jl_code_info_t*)data; - JL_TIMING(AST_UNCOMPRESS); - JL_LOCK(&m->writelock); // protect the roots array (Might GC) - assert(jl_is_method(m)); - assert(jl_typeis(data, jl_array_uint8_type)); - size_t i; - ios_t src; - ios_mem(&src, 0); - ios_setbuf(&src, (char*)data->data, jl_array_len(data), 0); - src.size = jl_array_len(data); - int en = jl_gc_enable(0); // Might GC - jl_serializer_state s = { - &src, MODE_IR, - m, - jl_get_ptls_states(), - NULL - }; - - jl_code_info_t *code = jl_new_code_info_uninit(); - uint8_t flags = read_uint8(s.s); - code->inferred = !!(flags & (1 << 3)); - code->inlineable = !!(flags & (1 << 2)); - code->propagate_inbounds = !!(flags & (1 << 1)); - code->pure = !!(flags & (1 << 0)); - - size_t nslots = read_int32(&src); - code->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots); - ios_readall(s.s, (char*)jl_array_data(code->slotflags), nslots); - - for (i = 0; i < 6; i++) { - if (i == 1) // skip codelocs - continue; - assert(jl_field_isptr(jl_code_info_type, i)); - jl_value_t **fld = (jl_value_t**)((char*)jl_data_ptr(code) + jl_field_offset(jl_code_info_type, i)); - *fld = jl_deserialize_value(&s, fld); - } - - jl_value_t *slotnames = jl_deserialize_value(&s, NULL); - if (!jl_is_string(slotnames)) - slotnames = m->slot_syms; - code->slotnames = jl_uncompress_argnames(slotnames); - - size_t nstmt = jl_array_len(code->code); - code->codelocs = (jl_value_t*)jl_alloc_array_1d(jl_array_int32_type, nstmt); - if (jl_array_len(code->linetable) < 256) { - for (i = 0; i < nstmt; i++) { - ((int32_t*)jl_array_data(code->codelocs))[i] = read_uint8(s.s); - } - } - else if (jl_array_len(code->linetable) < 65536) { - for (i = 0; i < nstmt; i++) { - ((int32_t*)jl_array_data(code->codelocs))[i] = read_uint16(s.s); - } - } - else { - ios_readall(s.s, (char*)jl_array_data(code->codelocs), nstmt * sizeof(int32_t)); - } - - assert(ios_getc(s.s) == -1); - ios_close(s.s); - JL_GC_PUSH1(&code); - jl_gc_enable(en); - JL_UNLOCK(&m->writelock); // Might GC - JL_GC_POP(); - if (metadata) { - code->min_world = metadata->min_world; - code->max_world = metadata->max_world; - code->rettype = metadata->rettype; - code->parent = metadata->def; - } - return code; -} - -JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) -{ - if (jl_is_code_info(data)) - return ((jl_code_info_t*)data)->inferred; - assert(jl_typeis(data, jl_array_uint8_type)); - uint8_t flags = ((uint8_t*)data->data)[0]; - return !!(flags & (1 << 3)); -} - -JL_DLLEXPORT uint8_t jl_ir_flag_inlineable(jl_array_t *data) -{ - if (jl_is_code_info(data)) - return ((jl_code_info_t*)data)->inlineable; - assert(jl_typeis(data, jl_array_uint8_type)); - uint8_t flags = ((uint8_t*)data->data)[0]; - return !!(flags & (1 << 2)); -} - -JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data) -{ - if (jl_is_code_info(data)) - return ((jl_code_info_t*)data)->pure; - assert(jl_typeis(data, jl_array_uint8_type)); - uint8_t flags = ((uint8_t*)data->data)[0]; - return !!(flags & (1 << 0)); -} - -JL_DLLEXPORT jl_value_t *jl_compress_argnames(jl_array_t *syms) -{ - size_t nsyms = jl_array_len(syms); - size_t i, len = 0; - for (i = 0; i < nsyms; i++) { - jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(syms, i); - assert(jl_is_symbol(name)); - char *namestr = jl_symbol_name(name); - size_t namelen = strlen(namestr) + 1; - len += namelen; - } - jl_value_t *str = jl_alloc_string(len); - len = 0; - for (i = 0; i < nsyms; i++) { - jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(syms, i); - assert(jl_is_symbol(name)); - char *namestr = jl_symbol_name(name); - size_t namelen = strlen(namestr) + 1; // include nul-byte - assert(len + namelen <= jl_string_len(str)); - memcpy(jl_string_data(str) + len, namestr, namelen); - len += namelen; - } - assert(len == jl_string_len(str)); - return str; -} - -JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) -{ - if (jl_is_code_info(data)) { - jl_code_info_t *func = (jl_code_info_t*)data; - return jl_array_len(func->slotnames); - } - else { - assert(jl_typeis(data, jl_array_uint8_type)); - int nslots = jl_load_unaligned_i32((char*)data->data + 1); - return nslots; - } -} - -JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_array_t *data, size_t i) -{ - assert(i < jl_ir_nslots(data)); - if (jl_is_code_info(data)) - return ((uint8_t*)((jl_code_info_t*)data)->slotflags->data)[i]; - assert(jl_typeis(data, jl_array_uint8_type)); - return ((uint8_t*)data->data)[1 + sizeof(int32_t) + i]; -} - -JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms) -{ - assert(jl_is_string(syms)); - char *namestr; - namestr = jl_string_data(syms); - size_t remaining = jl_string_len(syms); - size_t i, len = 0; - while (remaining) { - size_t namelen = strlen(namestr); - len += 1; - namestr += namelen + 1; - remaining -= namelen + 1; - } - namestr = jl_string_data(syms); - jl_array_t *names = jl_alloc_array_1d(jl_array_symbol_type, len); - JL_GC_PUSH1(&names); - for (i = 0; i < len; i++) { - size_t namelen = strlen(namestr); - jl_sym_t *name = jl_symbol_n(namestr, namelen); - jl_array_ptr_set(names, i, name); - namestr += namelen + 1; - } - JL_GC_POP(); - return names; -} - -JL_DLLEXPORT jl_value_t *jl_uncompress_argname_n(jl_value_t *syms, size_t i) -{ - assert(jl_is_string(syms)); - char *namestr = jl_string_data(syms); - size_t remaining = jl_string_len(syms); - while (remaining) { - size_t namelen = strlen(namestr); - if (i-- == 0) { - jl_sym_t *name = jl_symbol_n(namestr, namelen); - return (jl_value_t*)name; - } - namestr += namelen + 1; - remaining -= namelen + 1; - } - return jl_nothing; -} - - JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist) { JL_TIMING(SAVE_MODULE); @@ -2893,8 +2149,7 @@ JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist) jl_collect_backedges(edges, targets); jl_serializer_state s = { - &f, MODE_MODULE, - NULL, + &f, jl_get_ptls_states(), mod_array }; @@ -3230,8 +2485,7 @@ static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array) htable_new(&uniquing_table, 0); jl_serializer_state s = { - f, MODE_MODULE, - NULL, + f, ptls, mod_array }; diff --git a/src/ircode.c b/src/ircode.c new file mode 100644 index 0000000000000..4f6f8f8bd50dd --- /dev/null +++ b/src/ircode.c @@ -0,0 +1,932 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +/* + encoding IR to/from compact representation +*/ +#include +#include + +#include "julia.h" +#include "julia_internal.h" +#include "serialize.h" + +#ifndef _OS_WINDOWS_ +#include +#endif + +#ifndef _COMPILER_MICROSOFT_ +#include "valgrind.h" +#else +#define RUNNING_ON_VALGRIND 0 +#endif +#include "julia_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + ios_t *s; + // method we're compressing for + jl_method_t *method; + jl_ptls_t ptls; +} jl_ircode_state; + +// --- encoding --- + +#define jl_encode_value(s, v) jl_encode_value_((s), (jl_value_t*)(v), 0) + +static int literal_val_id(jl_ircode_state *s, jl_value_t *v) JL_GC_DISABLED +{ + jl_array_t *rs = s->method->roots; + int i, l = jl_array_len(rs); + if (jl_is_symbol(v) || jl_is_concrete_type(v)) { + for (i = 0; i < l; i++) { + if (jl_array_ptr_ref(rs, i) == v) + return i; + } + } + else { + for (i = 0; i < l; i++) { + if (jl_egal(jl_array_ptr_ref(rs, i), v)) + return i; + } + } + jl_array_ptr_1d_push(rs, v); + return jl_array_len(rs) - 1; +} + +static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED +{ + size_t i; + + if (v == NULL) { + write_uint8(s->s, TAG_NULL); + return; + } + + void *tag = jl_lookup_ser_tag(v); + if (tag != HT_NOTFOUND) { + uint8_t t8 = (intptr_t)tag; + if (t8 <= LAST_TAG) + write_uint8(s->s, 0); + write_uint8(s->s, t8); + } + else if (jl_is_symbol(v) && (tag = jl_lookup_common_symbol(v)) != HT_NOTFOUND) { + write_uint8(s->s, TAG_COMMONSYM); + write_uint8(s->s, (uint8_t)(size_t)tag); + } + else if (v == (jl_value_t*)jl_core_module) { + write_uint8(s->s, TAG_CORE); + } + else if (v == (jl_value_t*)jl_base_module) { + write_uint8(s->s, TAG_BASE); + } + else if (jl_typeis(v, jl_string_type) && jl_string_len(v) == 0) { + jl_encode_value(s, jl_an_empty_string); + } + else if (v == (jl_value_t*)s->method->module) { + write_uint8(s->s, TAG_NEARBYMODULE); + } + else if (jl_is_datatype(v) && ((jl_datatype_t*)v)->name == jl_array_typename && + jl_is_long(jl_tparam1(v)) && jl_unbox_long(jl_tparam1(v)) == 1 && + !((jl_datatype_t*)v)->hasfreetypevars) { + write_uint8(s->s, TAG_VECTORTY); + jl_encode_value(s, jl_tparam0(v)); + } + else if (jl_is_datatype(v) && ((jl_datatype_t*)v)->name == jl_pointer_typename && + !((jl_datatype_t*)v)->hasfreetypevars) { + write_uint8(s->s, TAG_PTRTY); + jl_encode_value(s, jl_tparam0(v)); + } + else if (jl_is_svec(v)) { + size_t l = jl_svec_len(v); + if (l <= 255) { + write_uint8(s->s, TAG_SVEC); + write_uint8(s->s, (uint8_t)l); + } + else { + write_uint8(s->s, TAG_LONG_SVEC); + write_int32(s->s, l); + } + for (i = 0; i < l; i++) { + jl_encode_value(s, jl_svecref(v, i)); + } + } + else if (jl_is_globalref(v)) { + if (jl_globalref_mod(v) == s->method->module) { + write_uint8(s->s, TAG_NEARBYGLOBAL); + jl_encode_value(s, jl_globalref_name(v)); + } + else { + write_uint8(s->s, TAG_GLOBALREF); + jl_encode_value(s, jl_globalref_mod(v)); + jl_encode_value(s, jl_globalref_name(v)); + } + } + else if (jl_is_ssavalue(v) && ((jl_ssavalue_t*)v)->id < 256 && ((jl_ssavalue_t*)v)->id >= 0) { + write_uint8(s->s, TAG_SSAVALUE); + write_uint8(s->s, ((jl_ssavalue_t*)v)->id); + } + else if (jl_is_ssavalue(v) && ((jl_ssavalue_t*)v)->id <= UINT16_MAX && ((jl_ssavalue_t*)v)->id >= 0) { + write_uint8(s->s, TAG_LONG_SSAVALUE); + write_uint16(s->s, ((jl_ssavalue_t*)v)->id); + } + else if (jl_typeis(v, jl_slotnumber_type) && jl_slot_number(v) <= UINT16_MAX && jl_slot_number(v) >= 0) { + write_uint8(s->s, TAG_SLOTNUMBER); + write_uint16(s->s, jl_slot_number(v)); + } + else if (jl_is_expr(v)) { + jl_expr_t *e = (jl_expr_t*)v; + size_t l = jl_array_len(e->args); + if (e->head == call_sym) { + if (l == 2) { + write_uint8(s->s, TAG_CALL1); + jl_encode_value(s, jl_exprarg(e, 0)); + jl_encode_value(s, jl_exprarg(e, 1)); + return; + } + else if (l == 3) { + write_uint8(s->s, TAG_CALL2); + jl_encode_value(s, jl_exprarg(e, 0)); + jl_encode_value(s, jl_exprarg(e, 1)); + jl_encode_value(s, jl_exprarg(e, 2)); + return; + } + } + if (l <= 255) { + write_uint8(s->s, TAG_EXPR); + write_uint8(s->s, (uint8_t)l); + } + else { + write_uint8(s->s, TAG_LONG_EXPR); + write_int32(s->s, l); + } + jl_encode_value(s, e->head); + for (i = 0; i < l; i++) { + jl_encode_value(s, jl_exprarg(e, i)); + } + } + else if (jl_is_phinode(v)) { + jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(v, 0); + jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(v, 1); + size_t l = jl_array_len(edges); + if (l <= 255 && jl_array_len(values) == l) { + write_uint8(s->s, TAG_PHINODE); + write_uint8(s->s, (uint8_t)l); + } + else { + write_uint8(s->s, TAG_LONG_PHINODE); + write_int32(s->s, l); + write_int32(s->s, jl_array_len(values)); + } + for (i = 0; i < l; i++) { + jl_encode_value(s, jl_array_ptr_ref(edges, i)); + } + l = jl_array_len(values); + for (i = 0; i < l; i++) { + jl_encode_value(s, jl_array_ptr_ref(values, i)); + } + } + else if (jl_is_phicnode(v)) { + jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(v, 0); + size_t l = jl_array_len(values); + if (l <= 255) { + write_uint8(s->s, TAG_PHICNODE); + write_uint8(s->s, (uint8_t)l); + } + else { + write_uint8(s->s, TAG_LONG_PHICNODE); + write_int32(s->s, l); + } + for (i = 0; i < l; i++) { + jl_encode_value(s, jl_array_ptr_ref(values, i)); + } + } + else if (jl_is_gotonode(v)) { + write_uint8(s->s, TAG_GOTONODE); + jl_encode_value(s, jl_get_nth_field(v, 0)); + } + else if (jl_is_quotenode(v)) { + write_uint8(s->s, TAG_QUOTENODE); + jl_encode_value(s, jl_get_nth_field(v, 0)); + } + else if (jl_typeis(v, jl_int64_type)) { + void *data = jl_data_ptr(v); + if (*(int64_t*)data >= INT16_MIN && *(int64_t*)data <= INT16_MAX) { + write_uint8(s->s, TAG_SHORTER_INT64); + write_uint16(s->s, (uint16_t)*(int64_t*)data); + } + else if (*(int64_t*)data >= S32_MIN && *(int64_t*)data <= S32_MAX) { + write_uint8(s->s, TAG_SHORT_INT64); + write_int32(s->s, (int32_t)*(int64_t*)data); + } + else { + write_uint8(s->s, TAG_INT64); + write_int64(s->s, *(int64_t*)data); + } + } + else if (jl_typeis(v, jl_int32_type)) { + void *data = jl_data_ptr(v); + if (*(int32_t*)data >= INT16_MIN && *(int32_t*)data <= INT16_MAX) { + write_uint8(s->s, TAG_SHORT_INT32); + write_uint16(s->s, (uint16_t)*(int32_t*)data); + } + else { + write_uint8(s->s, TAG_INT32); + write_int32(s->s, *(int32_t*)data); + } + } + else if (jl_typeis(v, jl_uint8_type)) { + write_uint8(s->s, TAG_UINT8); + write_int8(s->s, *(int8_t*)jl_data_ptr(v)); + } + else if (jl_typeis(v, jl_lineinfonode_type)) { + write_uint8(s->s, TAG_LINEINFO); + for (i = 0; i < jl_datatype_nfields(jl_lineinfonode_type); i++) + jl_encode_value(s, jl_get_nth_field(v, i)); + } + else if (((jl_datatype_t*)jl_typeof(v))->instance == v) { + write_uint8(s->s, TAG_SINGLETON); + jl_encode_value(s, jl_typeof(v)); + } + else if (as_literal && jl_typeis(v, jl_string_type)) { + write_uint8(s->s, TAG_STRING); + write_int32(s->s, jl_string_len(v)); + ios_write(s->s, jl_string_data(v), jl_string_len(v)); + } + else if (as_literal && jl_is_array(v)) { + jl_array_t *ar = (jl_array_t*)v; + jl_value_t *et = jl_tparam0(jl_typeof(ar)); + int isunion = jl_is_uniontype(et); + if (ar->flags.ndims == 1 && ar->elsize <= 0x1f) { + write_uint8(s->s, TAG_ARRAY1D); + write_uint8(s->s, (ar->flags.ptrarray << 7) | (ar->flags.hasptr << 6) | (isunion << 5) | (ar->elsize & 0x1f)); + } + else { + write_uint8(s->s, TAG_ARRAY); + write_uint16(s->s, ar->flags.ndims); + write_uint16(s->s, (ar->flags.ptrarray << 15) | (ar->flags.hasptr << 14) | (isunion << 13) | (ar->elsize & 0x1fff)); + } + for (i = 0; i < ar->flags.ndims; i++) + jl_encode_value(s, jl_box_long(jl_array_dim(ar,i))); + jl_encode_value(s, jl_typeof(ar)); + size_t l = jl_array_len(ar); + if (ar->flags.ptrarray) { + for (i = 0; i < l; i++) { + jl_value_t *e = jl_array_ptr_ref(v, i); + jl_encode_value(s, e); + } + } + else if (ar->flags.hasptr) { + const char *data = (const char*)jl_array_data(ar); + uint16_t elsz = ar->elsize; + size_t j, np = ((jl_datatype_t*)et)->layout->npointers; + for (i = 0; i < l; i++) { + const char *start = data; + for (j = 0; j < np; j++) { + uint32_t ptr = jl_ptr_offset((jl_datatype_t*)et, j); + const jl_value_t *const *fld = &((const jl_value_t *const *)data)[ptr]; + if ((const char*)fld != start) + ios_write(s->s, start, (const char*)fld - start); + JL_GC_PROMISE_ROOTED(*fld); + jl_encode_value(s, *fld); + start = (const char*)&fld[1]; + } + data += elsz; + if (data != start) + ios_write(s->s, start, data - start); + } + } + else { + ios_write(s->s, (char*)jl_array_data(ar), l * ar->elsize); + if (jl_array_isbitsunion(ar)) + ios_write(s->s, jl_array_typetagdata(ar), l); + } + } + else { + if (!as_literal && !(jl_is_uniontype(v) || jl_is_newvarnode(v) || jl_is_tuple(v) || + jl_is_linenode(v) || jl_is_upsilonnode(v) || jl_is_pinode(v))) { + int id = literal_val_id(s, v); + assert(id >= 0); + if (id < 256) { + write_uint8(s->s, TAG_METHODROOT); + write_uint8(s->s, id); + } + else { + assert(id <= UINT16_MAX); + write_uint8(s->s, TAG_LONG_METHODROOT); + write_uint16(s->s, id); + } + return; + } + jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); + if (t->size <= 255) { + write_uint8(s->s, TAG_SHORT_GENERAL); + write_uint8(s->s, t->size); + } + else { + write_uint8(s->s, TAG_GENERAL); + write_int32(s->s, t->size); + } + jl_encode_value(s, t); + + char *data = (char*)jl_data_ptr(v); + size_t i, j, np = t->layout->npointers; + uint32_t nf = t->layout->nfields; + char *last = data; + for (i = 0, j = 0; i < nf+1; i++) { + char *ptr = data + (i < nf ? jl_field_offset(t, i) : jl_datatype_size(t)); + if (j < np) { + char *prevptr = (char*)&((jl_value_t**)data)[jl_ptr_offset(t, j)]; + while (ptr > prevptr) { + // previous field contained pointers; write them and their interleaved data + if (prevptr > last) + ios_write(s->s, last, prevptr - last); + jl_value_t *e = *(jl_value_t**)prevptr; + JL_GC_PROMISE_ROOTED(e); + jl_encode_value(s, e); + last = prevptr + sizeof(jl_value_t*); + j++; + if (j < np) + prevptr = (char*)&((jl_value_t**)data)[jl_ptr_offset(t, j)]; + else + break; + } + } + if (i == nf) + break; + } + char *ptr = data + jl_datatype_size(t); + if (ptr > last) + ios_write(s->s, last, ptr - last); + } +} + +// --- decoding --- + +static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED; + +static jl_value_t *jl_decode_value_svec(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +{ + size_t i, len; + if (tag == TAG_SVEC) + len = read_uint8(s->s); + else + len = read_int32(s->s); + jl_svec_t *sv = jl_alloc_svec_uninit(len); + jl_value_t **data = jl_svec_data(sv); + for (i = 0; i < len; i++) { + data[i] = jl_decode_value(s); + } + return (jl_value_t*)sv; +} + +static jl_value_t *jl_decode_value_array(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +{ + int16_t i, ndims; + int isptr, isunion, hasptr, elsize; + if (tag == TAG_ARRAY1D) { + ndims = 1; + elsize = read_uint8(s->s); + isptr = (elsize >> 7) & 1; + hasptr = (elsize >> 6) & 1; + isunion = (elsize >> 5) & 1; + elsize = elsize & 0x1f; + } + else { + ndims = read_uint16(s->s); + elsize = read_uint16(s->s); + isptr = (elsize >> 15) & 1; + hasptr = (elsize >> 14) & 1; + isunion = (elsize >> 13) & 1; + elsize = elsize & 0x3fff; + } + size_t *dims = (size_t*)alloca(ndims * sizeof(size_t)); + for (i = 0; i < ndims; i++) { + dims[i] = jl_unbox_long(jl_decode_value(s)); + } + jl_array_t *a = jl_new_array_for_deserialization( + (jl_value_t*)NULL, ndims, dims, !isptr, hasptr, isunion, elsize); + jl_value_t *aty = jl_decode_value(s); + jl_set_typeof(a, aty); + if (a->flags.ptrarray) { + jl_value_t **data = (jl_value_t**)jl_array_data(a); + size_t i, numel = jl_array_len(a); + for (i = 0; i < numel; i++) { + data[i] = jl_decode_value(s); + } + assert(jl_astaggedvalue(a)->bits.gc == GC_CLEAN); // gc is disabled + } + else if (a->flags.hasptr) { + size_t i, numel = jl_array_len(a); + char *data = (char*)jl_array_data(a); + uint16_t elsz = a->elsize; + jl_datatype_t *et = (jl_datatype_t*)jl_tparam0(jl_typeof(a)); + size_t j, np = et->layout->npointers; + for (i = 0; i < numel; i++) { + char *start = data; + for (j = 0; j < np; j++) { + uint32_t ptr = jl_ptr_offset(et, j); + jl_value_t **fld = &((jl_value_t**)data)[ptr]; + if ((char*)fld != start) + ios_readall(s->s, start, (const char*)fld - start); + *fld = jl_decode_value(s); + start = (char*)&fld[1]; + } + data += elsz; + if (data != start) + ios_readall(s->s, start, data - start); + } + assert(jl_astaggedvalue(a)->bits.gc == GC_CLEAN); // gc is disabled + } + else { + size_t extra = jl_array_isbitsunion(a) ? jl_array_len(a) : 0; + size_t tot = jl_array_len(a) * a->elsize + extra; + ios_readall(s->s, (char*)jl_array_data(a), tot); + } + return (jl_value_t*)a; +} + +static jl_value_t *jl_decode_value_expr(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +{ + size_t i, len; + jl_sym_t *head = NULL; + if (tag == TAG_EXPR) { + len = read_uint8(s->s); + } + else if (tag == TAG_CALL1) { + len = 2; + head = call_sym; + } + else if (tag == TAG_CALL2) { + len = 3; + head = call_sym; + } + else { + len = read_int32(s->s); + } + if (head == NULL) + head = (jl_sym_t*)jl_decode_value(s); + jl_expr_t *e = jl_exprn(head, len); + jl_value_t **data = (jl_value_t**)(e->args->data); + for (i = 0; i < len; i++) { + data[i] = jl_decode_value(s); + } + return (jl_value_t*)e; +} + +static jl_value_t *jl_decode_value_phi(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +{ + size_t i, len_e, len_v; + if (tag == TAG_PHINODE) { + len_e = len_v = read_uint8(s->s); + } + else { + len_e = read_int32(s->s); + len_v = read_int32(s->s); + } + jl_array_t *e = jl_alloc_vec_any(len_e); + jl_array_t *v = jl_alloc_vec_any(len_v); + jl_value_t *phi = jl_new_struct(jl_phinode_type, e, v); + jl_value_t **data_e = (jl_value_t**)(e->data); + for (i = 0; i < len_e; i++) { + data_e[i] = jl_decode_value(s); + } + jl_value_t **data_v = (jl_value_t**)(v->data); + for (i = 0; i < len_v; i++) { + data_v[i] = jl_decode_value(s); + } + return phi; +} + +static jl_value_t *jl_decode_value_phic(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +{ + size_t i, len; + if (tag == TAG_PHICNODE) + len = read_uint8(s->s); + else + len = read_int32(s->s); + jl_array_t *v = jl_alloc_vec_any(len); + jl_value_t *phic = jl_new_struct(jl_phicnode_type, v); + jl_value_t **data = (jl_value_t**)(v->data); + for (i = 0; i < len; i++) { + data[i] = jl_decode_value(s); + } + return phic; +} + +static jl_value_t *jl_decode_value_globalref(jl_ircode_state *s) JL_GC_DISABLED +{ + jl_value_t *mod = jl_decode_value(s); + jl_value_t *var = jl_decode_value(s); + return jl_module_globalref((jl_module_t*)mod, (jl_sym_t*)var); +} + +static jl_value_t *jl_decode_value_any(jl_ircode_state *s, uint8_t tag) JL_GC_DISABLED +{ + int32_t sz = (tag == TAG_SHORT_GENERAL ? read_uint8(s->s) : read_int32(s->s)); + jl_value_t *v = jl_gc_alloc(s->ptls, sz, NULL); + jl_set_typeof(v, (void*)(intptr_t)0x50); + jl_datatype_t *dt = (jl_datatype_t*)jl_decode_value(s); + jl_set_typeof(v, dt); + char *data = (char*)jl_data_ptr(v); + size_t i, np = dt->layout->npointers; + char *start = data; + for (i = 0; i < np; i++) { + uint32_t ptr = jl_ptr_offset(dt, i); + jl_value_t **fld = &((jl_value_t**)data)[ptr]; + if ((char*)fld != start) + ios_readall(s->s, start, (const char*)fld - start); + *fld = jl_decode_value(s); + start = (char*)&fld[1]; + } + data += jl_datatype_size(dt); + if (data != start) + ios_readall(s->s, start, data - start); + return v; +} + +static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED +{ + assert(!ios_eof(s->s)); + jl_value_t *v; + size_t i, n; + uint8_t tag = read_uint8(s->s); + if (tag > LAST_TAG) + return jl_deser_tag(tag); + switch (tag) { + case TAG_NULL: return NULL; + case 0: + tag = read_uint8(s->s); + return jl_deser_tag(tag); + case TAG_METHODROOT: + return jl_array_ptr_ref(s->method->roots, read_uint8(s->s)); + case TAG_LONG_METHODROOT: + return jl_array_ptr_ref(s->method->roots, read_uint16(s->s)); + case TAG_SVEC: JL_FALLTHROUGH; case TAG_LONG_SVEC: + return jl_decode_value_svec(s, tag); + case TAG_COMMONSYM: + return jl_deser_symbol(read_uint8(s->s)); + case TAG_SSAVALUE: + v = jl_box_ssavalue(read_uint8(s->s)); + return v; + case TAG_LONG_SSAVALUE: + v = jl_box_ssavalue(read_uint16(s->s)); + return v; + case TAG_SLOTNUMBER: + v = jl_box_slotnumber(read_uint16(s->s)); + return v; + case TAG_ARRAY: JL_FALLTHROUGH; case TAG_ARRAY1D: + return jl_decode_value_array(s, tag); + case TAG_EXPR: JL_FALLTHROUGH; + case TAG_LONG_EXPR: JL_FALLTHROUGH; + case TAG_CALL1: JL_FALLTHROUGH; + case TAG_CALL2: + return jl_decode_value_expr(s, tag); + case TAG_PHINODE: JL_FALLTHROUGH; case TAG_LONG_PHINODE: + return jl_decode_value_phi(s, tag); + case TAG_PHICNODE: JL_FALLTHROUGH; case TAG_LONG_PHICNODE: + return jl_decode_value_phic(s, tag); + case TAG_GOTONODE: JL_FALLTHROUGH; case TAG_QUOTENODE: + v = jl_new_struct_uninit(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type); + set_nth_field(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type, (void*)v, 0, jl_decode_value(s)); + return v; + case TAG_SHORTER_INT64: + v = jl_box_int64((int16_t)read_uint16(s->s)); + return v; + case TAG_SHORT_INT64: + v = jl_box_int64(read_int32(s->s)); + return v; + case TAG_INT64: + v = jl_box_int64((int64_t)read_uint64(s->s)); + return v; + case TAG_SHORT_INT32: + v = jl_box_int32((int16_t)read_uint16(s->s)); + return v; + case TAG_INT32: + v = jl_box_int32(read_int32(s->s)); + return v; + case TAG_UINT8: + return jl_box_uint8(read_uint8(s->s)); + case TAG_NEARBYGLOBAL: + assert(s->method != NULL); + v = jl_decode_value(s); + return jl_module_globalref(s->method->module, (jl_sym_t*)v); + case TAG_NEARBYMODULE: + assert(s->method != NULL); + return (jl_value_t*)s->method->module; + case TAG_GLOBALREF: + return jl_decode_value_globalref(s); + case TAG_SINGLETON: + return ((jl_datatype_t*)jl_decode_value(s))->instance; + case TAG_CORE: + return (jl_value_t*)jl_core_module; + case TAG_BASE: + return (jl_value_t*)jl_base_module; + case TAG_VECTORTY: + v = jl_decode_value(s); + return jl_apply_type2((jl_value_t*)jl_array_type, v, jl_box_long(1)); + case TAG_PTRTY: + v = jl_decode_value(s); + return jl_apply_type1((jl_value_t*)jl_pointer_type, v); + case TAG_STRING: + n = read_int32(s->s); + v = jl_alloc_string(n); + ios_readall(s->s, jl_string_data(v), n); + return v; + case TAG_LINEINFO: + v = jl_new_struct_uninit(jl_lineinfonode_type); + for (i = 0; i < jl_datatype_nfields(jl_lineinfonode_type); i++) { + //size_t offs = jl_field_offset(jl_lineinfonode_type, i); + set_nth_field(jl_lineinfonode_type, (void*)v, i, jl_decode_value(s)); + } + return v; + default: + assert(tag == TAG_GENERAL || tag == TAG_SHORT_GENERAL); + return jl_decode_value_any(s, tag); + } +} + +// --- entry points --- + +JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) +{ + JL_TIMING(AST_COMPRESS); + JL_LOCK(&m->writelock); // protect the roots array (Might GC) + assert(jl_is_method(m)); + assert(jl_is_code_info(code)); + ios_t dest; + ios_mem(&dest, 0); + int en = jl_gc_enable(0); // Might GC + size_t i; + + if (m->roots == NULL) { + m->roots = jl_alloc_vec_any(0); + jl_gc_wb(m, m->roots); + } + jl_ircode_state s = { + &dest, + m, + jl_get_ptls_states() + }; + + uint8_t flags = (code->inferred << 3) + | (code->inlineable << 2) + | (code->propagate_inbounds << 1) + | (code->pure << 0); + write_uint8(s.s, flags); + + size_t nslots = jl_array_len(code->slotflags); + assert(nslots >= m->nargs && nslots < INT32_MAX); // required by generated functions + write_int32(s.s, nslots); + ios_write(s.s, (char*)jl_array_data(code->slotflags), nslots); + + // N.B.: The layout of everything before this point is explicitly referenced + // by the various jl_ir_ accessors. Make sure to adjust those if you change + // the data layout. + + for (i = 0; i < 6; i++) { + int copy = 1; + if (i == 1) { // skip codelocs + assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, codelocs)); + continue; + } + if (i == 4) { // don't copy contents of method_for_inference_limit_heuristics field + assert(jl_field_offset(jl_code_info_type, i) == offsetof(jl_code_info_t, method_for_inference_limit_heuristics)); + copy = 0; + } + jl_encode_value_(&s, jl_get_nth_field((jl_value_t*)code, i), copy); + } + + if (m->generator) + // can't optimize generated functions + jl_encode_value_(&s, (jl_value_t*)jl_compress_argnames(code->slotnames), 1); + else + jl_encode_value(&s, jl_nothing); + + size_t nstmt = jl_array_len(code->code); + assert(nstmt == jl_array_len(code->codelocs)); + if (jl_array_len(code->linetable) < 256) { + for (i = 0; i < nstmt; i++) { + write_uint8(s.s, ((int32_t*)jl_array_data(code->codelocs))[i]); + } + } + else if (jl_array_len(code->linetable) < 65536) { + for (i = 0; i < nstmt; i++) { + write_uint16(s.s, ((int32_t*)jl_array_data(code->codelocs))[i]); + } + } + else { + ios_write(s.s, (char*)jl_array_data(code->codelocs), nstmt * sizeof(int32_t)); + } + + ios_flush(s.s); + jl_array_t *v = jl_take_buffer(&dest); + ios_close(s.s); + if (jl_array_len(m->roots) == 0) { + m->roots = NULL; + } + JL_GC_PUSH1(&v); + jl_gc_enable(en); + JL_UNLOCK(&m->writelock); // Might GC + JL_GC_POP(); + return v; +} + +JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data) +{ + if (jl_is_code_info(data)) + return (jl_code_info_t*)data; + JL_TIMING(AST_UNCOMPRESS); + JL_LOCK(&m->writelock); // protect the roots array (Might GC) + assert(jl_is_method(m)); + assert(jl_typeis(data, jl_array_uint8_type)); + size_t i; + ios_t src; + ios_mem(&src, 0); + ios_setbuf(&src, (char*)data->data, jl_array_len(data), 0); + src.size = jl_array_len(data); + int en = jl_gc_enable(0); // Might GC + jl_ircode_state s = { + &src, + m, + jl_get_ptls_states() + }; + + jl_code_info_t *code = jl_new_code_info_uninit(); + uint8_t flags = read_uint8(s.s); + code->inferred = !!(flags & (1 << 3)); + code->inlineable = !!(flags & (1 << 2)); + code->propagate_inbounds = !!(flags & (1 << 1)); + code->pure = !!(flags & (1 << 0)); + + size_t nslots = read_int32(&src); + code->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots); + ios_readall(s.s, (char*)jl_array_data(code->slotflags), nslots); + + for (i = 0; i < 6; i++) { + if (i == 1) // skip codelocs + continue; + assert(jl_field_isptr(jl_code_info_type, i)); + jl_value_t **fld = (jl_value_t**)((char*)jl_data_ptr(code) + jl_field_offset(jl_code_info_type, i)); + *fld = jl_decode_value(&s); + } + + jl_value_t *slotnames = jl_decode_value(&s); + if (!jl_is_string(slotnames)) + slotnames = m->slot_syms; + code->slotnames = jl_uncompress_argnames(slotnames); + + size_t nstmt = jl_array_len(code->code); + code->codelocs = (jl_value_t*)jl_alloc_array_1d(jl_array_int32_type, nstmt); + if (jl_array_len(code->linetable) < 256) { + for (i = 0; i < nstmt; i++) { + ((int32_t*)jl_array_data(code->codelocs))[i] = read_uint8(s.s); + } + } + else if (jl_array_len(code->linetable) < 65536) { + for (i = 0; i < nstmt; i++) { + ((int32_t*)jl_array_data(code->codelocs))[i] = read_uint16(s.s); + } + } + else { + ios_readall(s.s, (char*)jl_array_data(code->codelocs), nstmt * sizeof(int32_t)); + } + + assert(ios_getc(s.s) == -1); + ios_close(s.s); + JL_GC_PUSH1(&code); + jl_gc_enable(en); + JL_UNLOCK(&m->writelock); // Might GC + JL_GC_POP(); + if (metadata) { + code->min_world = metadata->min_world; + code->max_world = metadata->max_world; + code->rettype = metadata->rettype; + code->parent = metadata->def; + } + return code; +} + +JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) +{ + if (jl_is_code_info(data)) + return ((jl_code_info_t*)data)->inferred; + assert(jl_typeis(data, jl_array_uint8_type)); + uint8_t flags = ((uint8_t*)data->data)[0]; + return !!(flags & (1 << 3)); +} + +JL_DLLEXPORT uint8_t jl_ir_flag_inlineable(jl_array_t *data) +{ + if (jl_is_code_info(data)) + return ((jl_code_info_t*)data)->inlineable; + assert(jl_typeis(data, jl_array_uint8_type)); + uint8_t flags = ((uint8_t*)data->data)[0]; + return !!(flags & (1 << 2)); +} + +JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data) +{ + if (jl_is_code_info(data)) + return ((jl_code_info_t*)data)->pure; + assert(jl_typeis(data, jl_array_uint8_type)); + uint8_t flags = ((uint8_t*)data->data)[0]; + return !!(flags & (1 << 0)); +} + +JL_DLLEXPORT jl_value_t *jl_compress_argnames(jl_array_t *syms) +{ + size_t nsyms = jl_array_len(syms); + size_t i, len = 0; + for (i = 0; i < nsyms; i++) { + jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(syms, i); + assert(jl_is_symbol(name)); + char *namestr = jl_symbol_name(name); + size_t namelen = strlen(namestr) + 1; + len += namelen; + } + jl_value_t *str = jl_alloc_string(len); + len = 0; + for (i = 0; i < nsyms; i++) { + jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(syms, i); + assert(jl_is_symbol(name)); + char *namestr = jl_symbol_name(name); + size_t namelen = strlen(namestr) + 1; // include nul-byte + assert(len + namelen <= jl_string_len(str)); + memcpy(jl_string_data(str) + len, namestr, namelen); + len += namelen; + } + assert(len == jl_string_len(str)); + return str; +} + +JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) +{ + if (jl_is_code_info(data)) { + jl_code_info_t *func = (jl_code_info_t*)data; + return jl_array_len(func->slotnames); + } + else { + assert(jl_typeis(data, jl_array_uint8_type)); + int nslots = jl_load_unaligned_i32((char*)data->data + 1); + return nslots; + } +} + +JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_array_t *data, size_t i) +{ + assert(i < jl_ir_nslots(data)); + if (jl_is_code_info(data)) + return ((uint8_t*)((jl_code_info_t*)data)->slotflags->data)[i]; + assert(jl_typeis(data, jl_array_uint8_type)); + return ((uint8_t*)data->data)[1 + sizeof(int32_t) + i]; +} + +JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms) +{ + assert(jl_is_string(syms)); + char *namestr; + namestr = jl_string_data(syms); + size_t remaining = jl_string_len(syms); + size_t i, len = 0; + while (remaining) { + size_t namelen = strlen(namestr); + len += 1; + namestr += namelen + 1; + remaining -= namelen + 1; + } + namestr = jl_string_data(syms); + jl_array_t *names = jl_alloc_array_1d(jl_array_symbol_type, len); + JL_GC_PUSH1(&names); + for (i = 0; i < len; i++) { + size_t namelen = strlen(namestr); + jl_sym_t *name = jl_symbol_n(namestr, namelen); + jl_array_ptr_set(names, i, name); + namestr += namelen + 1; + } + JL_GC_POP(); + return names; +} + +JL_DLLEXPORT jl_value_t *jl_uncompress_argname_n(jl_value_t *syms, size_t i) +{ + assert(jl_is_string(syms)); + char *namestr = jl_string_data(syms); + size_t remaining = jl_string_len(syms); + while (remaining) { + size_t namelen = strlen(namestr); + if (i-- == 0) { + jl_sym_t *name = jl_symbol_n(namestr, namelen); + return (jl_value_t*)name; + } + namestr += namelen + 1; + remaining -= namelen + 1; + } + return jl_nothing; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/serialize.h b/src/serialize.h new file mode 100644 index 0000000000000..d7e6216a55ad3 --- /dev/null +++ b/src/serialize.h @@ -0,0 +1,117 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#ifndef JL_SERIALIZE_H +#define JL_SERIALIZE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define TAG_SYMBOL 2 +#define TAG_SSAVALUE 3 +#define TAG_DATATYPE 4 +#define TAG_SLOTNUMBER 5 +#define TAG_SVEC 6 +#define TAG_ARRAY 7 +#define TAG_NULL 8 +#define TAG_EXPR 9 +#define TAG_PHINODE 10 +#define TAG_PHICNODE 11 +#define TAG_LONG_SYMBOL 12 +#define TAG_LONG_SVEC 13 +#define TAG_LONG_EXPR 14 +#define TAG_LONG_PHINODE 15 +#define TAG_LONG_PHICNODE 16 +#define TAG_METHODROOT 17 +#define TAG_STRING 18 +#define TAG_SHORT_INT64 19 +#define TAG_SHORT_GENERAL 20 +#define TAG_CNULL 21 +#define TAG_ARRAY1D 22 +#define TAG_SINGLETON 23 +#define TAG_MODULE 24 +#define TAG_TVAR 25 +#define TAG_METHOD_INSTANCE 26 +#define TAG_METHOD 27 +#define TAG_CODE_INSTANCE 28 +#define TAG_COMMONSYM 29 +#define TAG_NEARBYGLOBAL 30 +#define TAG_GLOBALREF 31 +#define TAG_CORE 32 +#define TAG_BASE 33 +#define TAG_BITYPENAME 34 +#define TAG_NEARBYMODULE 35 +#define TAG_INT32 36 +#define TAG_INT64 37 +#define TAG_UINT8 38 +#define TAG_VECTORTY 39 +#define TAG_PTRTY 40 +#define TAG_LONG_SSAVALUE 41 +#define TAG_LONG_METHODROOT 42 +#define TAG_SHORTER_INT64 43 +#define TAG_SHORT_INT32 44 +#define TAG_CALL1 45 +#define TAG_CALL2 46 +#define TAG_LINEINFO 47 +#define TAG_SHORT_BACKREF 48 +#define TAG_BACKREF 49 +#define TAG_UNIONALL 50 +#define TAG_GOTONODE 51 +#define TAG_QUOTENODE 52 +#define TAG_GENERAL 53 + +#define LAST_TAG 53 + +#define write_uint8(s, n) ios_putc((n), (s)) +#define read_uint8(s) ((uint8_t)ios_getc(s)) +#define write_int8(s, n) write_uint8(s, n) +#define read_int8(s) read_uint8(s) + +/* read and write in host byte order */ + +static void write_int32(ios_t *s, int32_t i) JL_NOTSAFEPOINT +{ + ios_write(s, (char*)&i, 4); +} + +static int32_t read_int32(ios_t *s) JL_NOTSAFEPOINT +{ + int32_t x = 0; + ios_read(s, (char*)&x, 4); + return x; +} + +static uint64_t read_uint64(ios_t *s) JL_NOTSAFEPOINT +{ + uint64_t x = 0; + ios_read(s, (char*)&x, 8); + return x; +} + +static void write_int64(ios_t *s, int64_t i) JL_NOTSAFEPOINT +{ + ios_write(s, (char*)&i, 8); +} + +static void write_uint16(ios_t *s, uint16_t i) JL_NOTSAFEPOINT +{ + ios_write(s, (char*)&i, 2); +} + +static uint16_t read_uint16(ios_t *s) JL_NOTSAFEPOINT +{ + int16_t x = 0; + ios_read(s, (char*)&x, 2); + return x; +} + +void *jl_lookup_ser_tag(jl_value_t *v); +void *jl_lookup_common_symbol(jl_value_t *v); +jl_value_t *jl_deser_tag(uint8_t tag); +jl_value_t *jl_deser_symbol(uint8_t tag); + +#ifdef __cplusplus +} +#endif + +#endif /* JL_SERIALIZE_H */ From d58896aa0841e5eb3537cb89bc5d37b671065c16 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 2 Jun 2020 19:38:35 -0400 Subject: [PATCH 087/232] Update build dependencies (#36123) C/C++ compiler based on LLVM recommendation: https://llvm.org/docs/GettingStarted.html#host-c-toolchain-both-compiler-and-standard-library Powershell based on: https://julialang.org/downloads/platform/#windows Remove version numbers for the libraries in deps since they are quite out of date, and change frequently. Instead point to Versions.make. [ci skip] --- doc/build/build.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/doc/build/build.md b/doc/build/build.md index 03bc157c1b976..7d3825a70f371 100644 --- a/doc/build/build.md +++ b/doc/build/build.md @@ -144,7 +144,7 @@ Notes for various architectures: Building Julia requires that the following software be installed: - **[GNU make]** — building dependencies. -- **[gcc & g++][gcc]** (>= 4.7) or **[Clang][clang]** (>= 3.1, Xcode 4.3.3 on macOS) — compiling and linking C, C++. +- **[gcc & g++][gcc]** (>= 5.1) or **[Clang][clang]** (>= 3.5, >= 6.0 for Apple Clang) — compiling and linking C, C++. - **[libatomic][gcc]** — provided by **[gcc]** and needed to support atomic operations. - **[python]** (>=2.7) — needed to build LLVM. - **[gfortran]** — compiling and linking Fortran libraries. @@ -155,6 +155,7 @@ Building Julia requires that the following software be installed: - **[patch]** — for modifying source code. - **[cmake]** (>= 3.4.3) — needed to build `libgit2`. - **[pkg-config]** — needed to build `libgit2` correctly, especially for proxy support. +- **[powershell]** (>= 3.0) — necessary only on Windows. On Debian-based distributions (e.g. Ubuntu), you can easily install them with `apt-get`: ``` @@ -164,25 +165,26 @@ sudo apt-get install build-essential libatomic1 python gfortran perl wget m4 cma Julia uses the following external libraries, which are automatically downloaded (or in a few cases, included in the Julia source repository) and then compiled from source the first time you run -`make`: +`make`. The specific version numbers of these libraries that Julia +uses are listed in [`deps/Versions.make`](https://github.com/JuliaLang/julia/blob/master/deps/Versions.make): - **[LLVM]** (9.0 + [patches](https://github.com/JuliaLang/julia/tree/master/deps/patches)) — compiler infrastructure (see [note below](#llvm)). - **[FemtoLisp]** — packaged with Julia source, and used to implement the compiler front-end. - **[libuv]** (custom fork) — portable, high-performance event-based I/O library. - **[OpenLibm]** — portable libm library containing elementary math functions. - **[DSFMT]** — fast Mersenne Twister pseudorandom number generator library. -- **[OpenBLAS]** — fast, open, and maintained [basic linear algebra subprograms (BLAS)](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) library, based on [Kazushige Goto's](https://en.wikipedia.org/wiki/Kazushige_Goto) famous [GotoBLAS](https://www.tacc.utexas.edu/research-development/tacc-software/gotoblas2) (see [note below](#blas-and-lapack)). -- **[LAPACK]** (>= 3.5) — library of linear algebra routines for solving systems of simultaneous linear equations, least-squares solutions of linear systems of equations, eigenvalue problems, and singular value problems. +- **[OpenBLAS]** — fast, open, and maintained [basic linear algebra subprograms (BLAS)] +- **[LAPACK]** — library of linear algebra routines for solving systems of simultaneous linear equations, least-squares solutions of linear systems of equations, eigenvalue problems, and singular value problems. - **[MKL]** (optional) – OpenBLAS and LAPACK may be replaced by Intel's MKL library. -- **[SuiteSparse]** (>= 4.1) — library of linear algebra routines for sparse matrices. -- **[PCRE]** (>= 10.00) — Perl-compatible regular expressions library. -- **[GMP]** (>= 5.0) — GNU multiple precision arithmetic library, needed for `BigInt` support. -- **[MPFR]** (>= 4.0) — GNU multiple precision floating point library, needed for arbitrary precision floating point (`BigFloat`) support. -- **[libgit2]** (>= 0.23) — Git linkable library, used by Julia's package manager. -- **[curl]** (>= 7.50) — libcurl provides download and proxy support for Julia's package manager. -- **[libssh2]** (>= 1.7) — library for SSH transport, used by libgit2 for packages with SSH remotes. -- **[mbedtls]** (>= 2.2) — library used for cryptography and transport layer security, used by libssh2 -- **[utf8proc]** (>= 2.1) — a library for processing UTF-8 encoded Unicode strings. +- **[SuiteSparse]** — library of linear algebra routines for sparse matrices. +- **[PCRE]** — Perl-compatible regular expressions library. +- **[GMP]** — GNU multiple precision arithmetic library, needed for `BigInt` support. +- **[MPFR]** — GNU multiple precision floating point library, needed for arbitrary precision floating point (`BigFloat`) support. +- **[libgit2]** — Git linkable library, used by Julia's package manager. +- **[curl]** — libcurl provides download and proxy support for Julia's package manager. +- **[libssh2]** — library for SSH transport, used by libgit2 for packages with SSH remotes. +- **[mbedtls]** — library used for cryptography and transport layer security, used by libssh2 +- **[utf8proc]** — a library for processing UTF-8 encoded Unicode strings. - **[libosxunwind]** — fork of [libunwind], a library that determines the call-chain of a program. [GNU make]: https://www.gnu.org/software/make @@ -217,6 +219,7 @@ repository) and then compiled from source the first time you run [libssh2]: https://www.libssh2.org [mbedtls]: https://tls.mbed.org/ [pkg-config]: https://www.freedesktop.org/wiki/Software/pkg-config/ +[powershell]: https://docs.microsoft.com/en-us/powershell/scripting/wmf/overview ## Build dependencies From b9986c78a6d49043b365c2d0c46e7ce60292524e Mon Sep 17 00:00:00 2001 From: natema Date: Wed, 3 Jun 2020 01:39:43 +0200 Subject: [PATCH 088/232] Fixing typo (#36119) Adding missing space --- doc/src/manual/distributed-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/distributed-computing.md b/doc/src/manual/distributed-computing.md index 550dd6ed5f57d..da266f48a6dfe 100644 --- a/doc/src/manual/distributed-computing.md +++ b/doc/src/manual/distributed-computing.md @@ -652,7 +652,7 @@ remotecalls and when data is stored to a[`RemoteChannel`](@ref) / [`Future`](@re a different node. As expected, this results in a copy of the serialized objects on the remote node. However, when the destination node is the local node, i.e. the calling process id is the same as the remote node id, it is executed -as a local call. It is usually(not always) executed in a different task - but there is no +as a local call. It is usually (not always) executed in a different task - but there is no serialization/deserialization of data. Consequently, the call refers to the same object instances as passed - no copies are created. This behavior is highlighted below: From a1b6d7df500e205c8a1c1cca4b234d8f2a203d1f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 2 Jun 2020 19:44:55 -0400 Subject: [PATCH 089/232] Fix whitespace --- doc/build/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build/build.md b/doc/build/build.md index 7d3825a70f371..fd662459cc489 100644 --- a/doc/build/build.md +++ b/doc/build/build.md @@ -165,7 +165,7 @@ sudo apt-get install build-essential libatomic1 python gfortran perl wget m4 cma Julia uses the following external libraries, which are automatically downloaded (or in a few cases, included in the Julia source repository) and then compiled from source the first time you run -`make`. The specific version numbers of these libraries that Julia +`make`. The specific version numbers of these libraries that Julia uses are listed in [`deps/Versions.make`](https://github.com/JuliaLang/julia/blob/master/deps/Versions.make): - **[LLVM]** (9.0 + [patches](https://github.com/JuliaLang/julia/tree/master/deps/patches)) — compiler infrastructure (see [note below](#llvm)). From 5d6a910260c6114565c1102cbc4a4ca34362189c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 2 Jun 2020 22:40:19 -0400 Subject: [PATCH 090/232] inference: ignore badly behaving generated functions (#36115) fix #36088 --- base/compiler/abstractinterpretation.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index f82380cecaa57..faa3c757d8c92 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -263,6 +263,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp if inf_result === nothing inf_result = InferenceResult(mi, argtypes) frame = InferenceState(inf_result, #=cache=#false, interp) + frame === nothing && return Any # this is probably a bad generated function (unsound), but just ignore it frame.limited = true frame.parent = sv push!(inf_cache, inf_result) From 65c2a03268e7bf894f7d84af8630e34359fe7f34 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 3 Jun 2020 07:27:25 -0500 Subject: [PATCH 091/232] jl_debug_method_invalidation: return a list of instances, signatures, and tags (#35768) Rather than print debug info, this stores the relevant entities to an array. This facilitates easier analysis of the causes, for example making it possible to automatically walk the hierarchy of related code objects (methods, functions, signature types), perform type-intersection, etc. Analysis code will live outside Julia proper (SnoopCompile and MethodAnalysis are two early "consumers"). --- src/dump.c | 13 +++++--- src/gf.c | 96 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/dump.c b/src/dump.c index 9c3b19b5f1a8d..55bb6915fee07 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1830,7 +1830,7 @@ static void jl_insert_methods(jl_array_t *list) } } -extern int jl_debug_method_invalidation; +extern jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED; // verify that these edges intersect with the same methods as before static void jl_verify_edges(jl_array_t *targets, jl_array_t **pvalids) @@ -1884,7 +1884,8 @@ static void jl_insert_backedges(jl_array_t *list, jl_array_t *targets) // map(enable, ((list[i] => targets[list[i + 1] .* 2]) for i in 1:2:length(list) if all(valids[list[i + 1]]))) size_t i, l = jl_array_len(list); jl_array_t *valids = NULL; - JL_GC_PUSH1(&valids); + jl_value_t *loctag = NULL; + JL_GC_PUSH2(&valids, &loctag); jl_verify_edges(targets, &valids); for (i = 0; i < l; i += 2) { jl_method_instance_t *caller = (jl_method_instance_t*)jl_array_ptr_ref(list, i); @@ -1922,9 +1923,11 @@ static void jl_insert_backedges(jl_array_t *list, jl_array_t *targets) } } else { - if (jl_debug_method_invalidation) { - jl_static_show(JL_STDOUT, (jl_value_t*)caller); - jl_uv_puts(JL_STDOUT, "<<<\n", 4); + if (_jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)caller); + loctag = jl_cstr_to_string("insert_backedges"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } } } diff --git a/src/gf.c b/src/gf.c index ef4694de438b2..51fa2f00ba454 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1485,17 +1485,33 @@ static void update_max_args(jl_methtable_t *mt, jl_value_t *type) mt->max_args = na; } -JL_DLLEXPORT int jl_debug_method_invalidation = 0; +jl_array_t *_jl_debug_method_invalidation JL_GLOBALLY_ROOTED = NULL; +JL_DLLEXPORT jl_value_t *jl_debug_method_invalidation(int state) +{ + /* After calling with `state = 1`, caller is responsible for + holding a reference to the returned array until this is called + again with `state = 0`. */ + if (state) { + if (_jl_debug_method_invalidation) + return (jl_value_t*) _jl_debug_method_invalidation; + _jl_debug_method_invalidation = jl_alloc_array_1d(jl_array_any_type, 0); + return (jl_value_t*) _jl_debug_method_invalidation; + } + _jl_debug_method_invalidation = NULL; + return jl_nothing; +} // recursively invalidate cached methods that had an edge to a replaced method static void invalidate_method_instance(jl_method_instance_t *replaced, size_t max_world, int depth) { - if (jl_debug_method_invalidation) { - int d0 = depth; - while (d0-- > 0) - jl_uv_puts(JL_STDOUT, " ", 1); - jl_static_show(JL_STDOUT, (jl_value_t*)replaced); - jl_uv_puts(JL_STDOUT, "\n", 1); + if (_jl_debug_method_invalidation) { + jl_value_t *boxeddepth = NULL; + JL_GC_PUSH1(&boxeddepth); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)replaced); + boxeddepth = jl_box_int32(depth); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, boxeddepth); + jl_gc_wb(_jl_debug_method_invalidation, boxeddepth); + JL_GC_POP(); } if (!jl_is_method(replaced->def.method)) return; // shouldn't happen, but better to be safe @@ -1622,10 +1638,14 @@ static int invalidate_mt_cache(jl_typemap_entry_t *oldentry, void *closure0) } } if (intersects) { - if (jl_debug_method_invalidation) { - jl_uv_puts(JL_STDOUT, "-- ", 3); - jl_static_show(JL_STDOUT, (jl_value_t*)mi); - jl_uv_puts(JL_STDOUT, "\n", 1); + if (_jl_debug_method_invalidation) { + jl_value_t *loctag = NULL; + JL_GC_PUSH1(&loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + loctag = jl_cstr_to_string("invalidate_mt_cache"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + JL_GC_POP(); } oldentry->max_world = env->max_world; } @@ -1673,12 +1693,33 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); // Invalidate the backedges jl_svec_t *specializations = methodentry->func.method->specializations; + int invalidated = 0; + jl_value_t *loctag = NULL; + JL_GC_PUSH1(&loctag); size_t i, l = jl_svec_len(specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); - if (mi) - invalidate_backedges(mi, methodentry->max_world); + if (mi) { + invalidated = 1; + if (invalidate_backedges(mi, methodentry->max_world)) + if (_jl_debug_method_invalidation) { + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_disable"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + } + } + } + if (invalidated && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_disable"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } + JL_GC_POP(); JL_UNLOCK(&mt->writelock); } @@ -1693,7 +1734,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method method->primary_world = ++jl_world_counter; size_t max_world = method->primary_world - 1; int invalidated = 0; - JL_GC_PUSH1(&oldvalue); + jl_value_t *loctag = NULL; // debug info for invalidation + JL_GC_PUSH2(&oldvalue, &loctag); JL_LOCK(&mt->writelock); // first delete the existing entry (we'll disable it later) struct jl_typemap_assoc search = {(jl_value_t*)type, method->primary_world, NULL, 0, ~(size_t)0}; @@ -1722,6 +1764,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_method_instance_t *backedge = (jl_method_instance_t*)backedges[i]; invalidate_method_instance(backedge, max_world, 0); invalidated = 1; + if (_jl_debug_method_invalidation) + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)backedgetyp); } else { backedges[ins++] = backedges[i - 1]; @@ -1767,17 +1811,27 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); if (mi != NULL && !jl_has_empty_intersection(type, (jl_value_t*)mi->specTypes)) - if (invalidate_backedges(mi, max_world)) + if (invalidate_backedges(mi, max_world)) { invalidated = 1; + if (_jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_insert"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); + } + } } } } - if (invalidated && jl_debug_method_invalidation) { - jl_uv_puts(JL_STDOUT, ">> ", 3); - jl_static_show(JL_STDOUT, (jl_value_t*)method); - jl_uv_puts(JL_STDOUT, " ", 1); - jl_static_show(JL_STDOUT, (jl_value_t*)type); - jl_uv_puts(JL_STDOUT, "\n", 1); + if (invalidated && _jl_debug_method_invalidation) { + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); + if (!loctag) { + loctag = jl_cstr_to_string("jl_method_table_insert"); + jl_gc_wb(_jl_debug_method_invalidation, loctag); + } + jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } update_max_args(mt, type); JL_UNLOCK(&mt->writelock); From b49a0d5910635ff1297f9df5adf299804a908a4e Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Wed, 3 Jun 2020 08:08:09 -0500 Subject: [PATCH 092/232] fix #36116, diff(::AbstractRange) returns an Array (#36117) * fix #36116, diff(::AbstractRange) returns an Array --- base/multidimensional.jl | 4 ++++ test/ranges.jl | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 9c9006ff1076c..03b1d151f23b4 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -848,6 +848,10 @@ function diff(a::AbstractArray{T,N}; dims::Integer) where {T,N} return view(a, r1...) .- view(a, r0...) end +function diff(r::AbstractRange{T}; dims::Integer=1) where {T} + dims == 1 || throw(ArgumentError("dimension $dims out of range (1:1)")) + return T[@inbounds r[i+1] - r[i] for i in firstindex(r):lastindex(r)-1] +end ### from abstractarray.jl diff --git a/test/ranges.jl b/test/ranges.jl index 77f925b5af337..0777323b9ed66 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1607,6 +1607,16 @@ end @test collect(r) == ['a','c','e','g'] end +@testset "diff of ranges, #36116" begin + for r in (0:2, 0:1:2, 0.0:1.0:2.0, LinRange(0,2,3)) + @test diff(r) == diff(collect(r)) == fill(1, 2) + @test_throws ArgumentError diff(r, dims=2) + end + for r in (0:2:5, 0.1:0.1:2.0, LinRange(0,2,33)) + @test diff(r) == diff(collect(r)) == [r[i+1] - r[i] for i in 1:length(r)-1] + end +end + @testset "Return type of indexing with ranges" begin for T = (Base.OneTo{Int}, UnitRange{Int}, StepRange{Int,Int}, StepRangeLen{Int}, LinRange{Int}) @test eltype(T(1:5)) === eltype(T(1:5)[1:2]) From 890f34cfdb9103cd5b64a3f009e9a0a2370c0e38 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 3 Jun 2020 12:00:03 -0400 Subject: [PATCH 093/232] fix #36108, printing invalid numeric juxtapositions (#36122) --- base/show.jl | 3 ++- test/show.jl | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/show.jl b/base/show.jl index c361fb4dd88b9..6d3df3076d710 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1342,7 +1342,8 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In # scalar multiplication (i.e. "100x") elseif (func === :* && - length(func_args)==2 && isa(func_args[1], Real) && isa(func_args[2], Symbol)) + length(func_args) == 2 && isa(func_args[1], Union{Int, Int64, Float32, Float64}) && + isa(func_args[2], Symbol) && !in(string(func_args[2])[1], ('e', 'E', 'f'))) if func_prec <= prec show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec, quote_level) else diff --git a/test/show.jl b/test/show.jl index 5127be81ba28a..4acf2e1d47458 100644 --- a/test/show.jl +++ b/test/show.jl @@ -138,6 +138,10 @@ end # basic expressions @test_repr "x + y" @test_repr "2e" +@test_repr "2*e1" +@test_repr "2*E1" +@test_repr "2*f1" +@test_repr "0x00*a" @test_repr "!x" @test_repr "f(1, 2, 3)" @test_repr "x = ~y" From 3222aba1d32ab9af41c82a1dd0bce0a7f72d8cd5 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 3 Jun 2020 17:05:34 -0400 Subject: [PATCH 094/232] Fix a few AbstractInterpreter nits (#36127) * Fix a few AbstractInterpreter nits * Update base/compiler/types.jl Co-authored-by: Jonas Schulze Co-authored-by: Jonas Schulze --- base/compiler/abstractinterpretation.jl | 2 -- base/compiler/types.jl | 2 +- base/show.jl | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index faa3c757d8c92..d15c56fb2dedb 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -4,8 +4,6 @@ # constants # ############# -const DEFAULT_INTERPRETER = NativeInterpreter(UInt(0)) - const CoreNumType = Union{Int32, Int64, Float32, Float64} const _REF_NAME = Ref.body.name diff --git a/base/compiler/types.jl b/base/compiler/types.jl index ae73c523d22ed..be92d0173f29f 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -5,7 +5,7 @@ An abstract base class that allows multiple dispatch to determine the method of executing Julia code. The native Julia LLVM pipeline is enabled by using the -`TypeInference` concrete instantiatoin of this abstract class, others can be +`NativeInterpreter` concrete instantiation of this abstract class, others can be swapped in as long as they follow the AbstractInterpreter API. All AbstractInterpreters are expected to provide at least the following methods: diff --git a/base/show.jl b/base/show.jl index 6d3df3076d710..f8b87b2c5b248 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1935,7 +1935,7 @@ function show(io::IO, inferred::Core.Compiler.InferenceResult) end function show(io::IO, ::Core.Compiler.NativeInterpreter) - print(io, "Core.Compiler.NativeInterpreter") + print(io, "Core.Compiler.NativeInterpreter(...)") end From cf410dc9e81cfa14a18ca3aef50346000b4615c7 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 3 Jun 2020 16:16:14 -0500 Subject: [PATCH 095/232] Add more trunc BigFloat tests (#36027) * Add more trunc BigFloat tests Taken from #21970 * Use testset instead of eval --- test/mpfr.jl | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/mpfr.jl b/test/mpfr.jl index 181eba6d5d72a..86c7d345f49fd 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -857,6 +857,45 @@ end # Issue #33676 @test trunc(UInt8, parse(BigFloat,"255.1")) == UInt8(255) @test_throws InexactError trunc(UInt8, parse(BigFloat,"256.1")) + + @testset "inexact limits ($T)" for T in Base.BitInteger_types + typemin_and_half = BigFloat(typemin(T)) - 0.5 + typemax_and_half = BigFloat(typemax(T)) + 0.5 + typemin_and_one = BigFloat(typemin(T)) - 1 + typemax_and_one = BigFloat(typemax(T)) + 1 + + @test trunc(T, typemin_and_half) == typemin(T) + @test trunc(T, typemax_and_half) == typemax(T) + @test_throws InexactError trunc(T, typemin_and_one) + @test_throws InexactError trunc(T, typemax_and_one) + + @test_throws InexactError floor(T, typemin_and_half) + @test floor(T, typemax_and_half) == typemax(T) + @test_throws InexactError floor(T, typemin_and_one) + @test_throws InexactError floor(T, typemax_and_one) + + @test ceil(T, typemin_and_half) == typemin(T) + @test_throws InexactError ceil(T, typemax_and_half) + @test_throws InexactError ceil(T, typemin_and_one) + @test_throws InexactError ceil(T, typemax_and_one) + + if iseven(typemin(T)) + @test round(T, typemin_and_half) == typemin(T) + else + @test_throws InexactError round(T, typemin_and_half) + end + + if iseven(typemax(T)) + @test round(T, typemax_and_half) == typemax(T) + else + @test_throws InexactError round(T, typemax_and_half) + end + + @test round(T, BigFloat(typemin(T)) - 0.4) == typemin(T) + @test round(T, BigFloat(typemax(T)) + 0.4) == typemax(T) + @test_throws InexactError round(T, typemin_and_one) + @test_throws InexactError round(T, typemax_and_one) + end end @testset "div" begin @test div(big"1.0",big"0.1") == 9 From c69fab596cd9879148e14e22d82a99dd06fbf0a9 Mon Sep 17 00:00:00 2001 From: Andrew Bylard Date: Wed, 3 Jun 2020 15:53:19 -0700 Subject: [PATCH 096/232] Add to documentation of two-argument atan (#36128) Clarify behavior of branch cuts. --- base/math.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/math.jl b/base/math.jl index 06458f0ca2766..b32b67d4c1c67 100644 --- a/base/math.jl +++ b/base/math.jl @@ -344,7 +344,8 @@ For one argument, this is the angle in radians between the positive *x*-axis and For two arguments, this is the angle in radians between the positive *x*-axis and the point (*x*, *y*), returning a value in the interval ``[-\\pi, \\pi]``. This corresponds to a -standard [`atan2`](https://en.wikipedia.org/wiki/Atan2) function. +standard [`atan2`](https://en.wikipedia.org/wiki/Atan2) function. Note that by convention +`atan(0.0,x)` is defined as ``\\pi`` and `atan(-0.0,x)` is defined as ``-\\pi`` when `x < 0`. """ atan(x::Number) From 912a8ed365f707e6104ce52c35a3dffa496a0868 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 3 Jun 2020 15:53:47 -0700 Subject: [PATCH 097/232] Fix mkpath error handling (#36126) The error thrown by `mkdir` when the directory already exists was changed in #33422. --- base/file.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/file.jl b/base/file.jl index e0030522ffa43..1e860f30dc12f 100644 --- a/base/file.jl +++ b/base/file.jl @@ -228,7 +228,7 @@ function mkpath(path::AbstractString; mode::Integer = 0o777) catch err # If there is a problem with making the directory, but the directory # does in fact exist, then ignore the error. Else re-throw it. - if !isa(err, SystemError) || !isdir(path) + if !isa(err, IOError) || !isdir(path) rethrow() end end From 855a08b35197e21a736d1f30e26e042c091712c7 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 3 Jun 2020 19:28:19 -0500 Subject: [PATCH 098/232] Remove unnecessary jl_gc_wb calls (#36137) --- src/dump.c | 1 - src/gf.c | 18 ++++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/dump.c b/src/dump.c index 55bb6915fee07..7cb4ffc47d092 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1926,7 +1926,6 @@ static void jl_insert_backedges(jl_array_t *list, jl_array_t *targets) if (_jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)caller); loctag = jl_cstr_to_string("insert_backedges"); - jl_gc_wb(_jl_debug_method_invalidation, loctag); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } } diff --git a/src/gf.c b/src/gf.c index 51fa2f00ba454..ca51b5f24917b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1510,7 +1510,6 @@ static void invalidate_method_instance(jl_method_instance_t *replaced, size_t ma jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)replaced); boxeddepth = jl_box_int32(depth); jl_array_ptr_1d_push(_jl_debug_method_invalidation, boxeddepth); - jl_gc_wb(_jl_debug_method_invalidation, boxeddepth); JL_GC_POP(); } if (!jl_is_method(replaced->def.method)) @@ -1643,7 +1642,6 @@ static int invalidate_mt_cache(jl_typemap_entry_t *oldentry, void *closure0) JL_GC_PUSH1(&loctag); jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); loctag = jl_cstr_to_string("invalidate_mt_cache"); - jl_gc_wb(_jl_debug_method_invalidation, loctag); jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); JL_GC_POP(); } @@ -1703,20 +1701,16 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho invalidated = 1; if (invalidate_backedges(mi, methodentry->max_world)) if (_jl_debug_method_invalidation) { - if (!loctag) { + if (!loctag) loctag = jl_cstr_to_string("jl_method_table_disable"); - jl_gc_wb(_jl_debug_method_invalidation, loctag); - } jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } } } if (invalidated && _jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); - if (!loctag) { + if (!loctag) loctag = jl_cstr_to_string("jl_method_table_disable"); - jl_gc_wb(_jl_debug_method_invalidation, loctag); - } jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } JL_GC_POP(); @@ -1815,10 +1809,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method invalidated = 1; if (_jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)mi); - if (!loctag) { + if (!loctag) loctag = jl_cstr_to_string("jl_method_table_insert"); - jl_gc_wb(_jl_debug_method_invalidation, loctag); - } jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } } @@ -1827,10 +1819,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } if (invalidated && _jl_debug_method_invalidation) { jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method); - if (!loctag) { + if (!loctag) loctag = jl_cstr_to_string("jl_method_table_insert"); - jl_gc_wb(_jl_debug_method_invalidation, loctag); - } jl_array_ptr_1d_push(_jl_debug_method_invalidation, loctag); } update_max_args(mt, type); From f65561f88386a691da11b3c53d25f8cf7a6a248c Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 4 Jun 2020 07:18:17 -0500 Subject: [PATCH 099/232] Add some broken tests for invalidation (#35855) --- base/expr.jl | 1 + test/worlds.jl | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/base/expr.jl b/base/expr.jl index 71eacaaacf4a0..db2d6333d01c1 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -77,6 +77,7 @@ end ==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args) ==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value) +==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = stmt1.edges == stmt2.edges && stmt1.values == stmt2.values """ macroexpand(m::Module, x; recursive=true) diff --git a/test/worlds.jl b/test/worlds.jl index 2c3ed4bea7832..92ee5866e1517 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -199,3 +199,115 @@ end notify(c26506_1) wait(c26506_2) @test result26506[1] == 3 + + +## Invalidation tests + +function instance(f, types) + m = which(f, types) + inst = nothing + tt = Tuple{typeof(f), types...} + specs = m.specializations + if isa(specs, Nothing) + elseif isa(specs, Core.SimpleVector) + for i = 1:length(specs) + if isassigned(specs, i) + mi = specs[i]::Core.MethodInstance + if mi.specTypes === tt + inst = mi + break + end + end + end + else + Base.visit(specs) do mi + if mi.specTypes === tt + inst = mi + end + end + end + return inst +end + +function worlds(mi::Core.MethodInstance) + w = Tuple{UInt,UInt}[] + if isdefined(mi, :cache) + ci = mi.cache + push!(w, (ci.min_world, ci.max_world)) + while isdefined(ci, :next) + ci = ci.next + push!(w, (ci.min_world, ci.max_world)) + end + end + return w +end + +# avoid adding this to Base +function equal(ci1::Core.CodeInfo, ci2::Core.CodeInfo) + return ci1.code == ci2.code && + ci1.codelocs == ci2.codelocs && + ci1.ssavaluetypes == ci2.ssavaluetypes && + ci1.ssaflags == ci2.ssaflags && + ci1.method_for_inference_limit_heuristics == ci2.method_for_inference_limit_heuristics && + ci1.linetable == ci2.linetable && + ci1.slotnames == ci2.slotnames && + ci1.slotflags == ci2.slotflags && + ci1.slottypes == ci2.slottypes && + ci1.rettype == ci2.rettype +end +equal(p1::Pair, p2::Pair) = p1.second == p2.second && equal(p1.first, p2.first) + +## Union-splitting based on state-of-the-world: check that each invalidation corresponds to new code +applyf35855(c) = f35855(c[1]) +f35855(::Int) = 1 +f35855(::Float64) = 2 +applyf35855([1]) +applyf35855([1.0]) +applyf35855(Any[1]) +wint = worlds(instance(applyf35855, (Vector{Int},))) +wfloat = worlds(instance(applyf35855, (Vector{Float64},))) +wany2 = worlds(instance(applyf35855, (Vector{Any},))) +src2 = code_typed(applyf35855, (Vector{Any},))[1] +f35855(::String) = 3 +applyf35855(Any[1]) +@test worlds(instance(applyf35855, (Vector{Int},))) == wint +@test worlds(instance(applyf35855, (Vector{Float64},))) == wfloat +wany3 = worlds(instance(applyf35855, (Vector{Any},))) +src3 = code_typed(applyf35855, (Vector{Any},))[1] +@test (wany3 == wany2) == equal(src3, src2) # don't invalidate unless you also change the code +f35855(::AbstractVector) = 4 # next test would pass if this were ::Vector{Int} +applyf35855(Any[1]) +wany4 = worlds(instance(applyf35855, (Vector{Any},))) +src4 = code_typed(applyf35855, (Vector{Any},))[1] +@test_broken (wany4 == wany3) == equal(src4, src3) +f35855(::Dict) = 5 +applyf35855(Any[1]) +wany5 = worlds(instance(applyf35855, (Vector{Any},))) +src5 = code_typed(applyf35855, (Vector{Any},))[1] +@test (wany5 == wany4) == equal(src5, src4) +f35855(::Set) = 6 # with current settings, this shouldn't invalidate +applyf35855(Any[1]) +wany6 = worlds(instance(applyf35855, (Vector{Any},))) +src6 = code_typed(applyf35855, (Vector{Any},))[1] +@test (wany6 == wany5) == equal(src6, src5) + +## ambiguities do not trigger invalidation +using Printf +Printf.gen("%f") +mi = instance(+, (AbstractChar, UInt8)) +w = worlds(mi) + +abstract type FixedPoint35855{T <: Integer} <: Real end +struct Normed35855 <: FixedPoint35855{UInt8} + i::UInt8 + Normed35855(i::Integer, _) = new(i % UInt8) +end +(::Type{X})(x::Real) where X<:FixedPoint35855{T} where T = X(round(T, typemax(T)*x), 0) + +@test_broken worlds(mi) == w + +mi = instance(convert, (Type{Nothing}, String)) +w = worlds(mi) +abstract type Colorant35855 end +Base.convert(::Type{C}, c) where C<:Colorant35855 = false +@test_broken worlds(mi) == w From 865c6367e1ecbbaf1c972a0f7611857f43977aca Mon Sep 17 00:00:00 2001 From: Goran Nakerst Date: Thu, 4 Jun 2020 15:32:59 +0100 Subject: [PATCH 100/232] Update sysimg.md - typo (#36144) typo in the last paragraph --- doc/src/devdocs/sysimg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index aa0da7651c7da..734c8a5ca4659 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -109,5 +109,5 @@ See code comments for each components for more implementation details. parsing the metadata saved during system image generation. Host feature detection and selection decision are done in `src/processor_*.cpp` depending on the ISA. The target selection will prefer exact CPU name match, - larger vector register size, and larget number of features. + larger vector register size, and larger number of features. An overview of this process is in `src/processor.cpp`. From 186c3bbe6139352f463a6e8849580d1035d13f7b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 4 Jun 2020 22:04:29 +0200 Subject: [PATCH 101/232] remove unnecessary newline when printing BoundsError (#36138) --- base/errorshow.jl | 1 - base/strings/basic.jl | 12 ++++-------- doc/src/manual/strings.md | 6 ++---- test/errorshow.jl | 4 ++-- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 25e2b54b5b171..05c6c6d787ecd 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -42,7 +42,6 @@ function showerror(io::IO, ex::BoundsError) print(io, ": attempt to access ") summary(io, ex.a) if isdefined(ex, :i) - !isa(ex.a, AbstractArray) && print(io, "\n ") print(io, " at index [") if ex.i isa AbstractRange print(io, ex.i) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index de977d0b7c084..e4c88752a33d1 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -412,13 +412,11 @@ julia> thisind("α", 3) 3 julia> thisind("α", 4) -ERROR: BoundsError: attempt to access String - at index [4] +ERROR: BoundsError: attempt to access String at index [4] [...] julia> thisind("α", -1) -ERROR: BoundsError: attempt to access String - at index [-1] +ERROR: BoundsError: attempt to access String at index [-1] [...] ``` """ @@ -468,8 +466,7 @@ julia> prevind("α", 1) 0 julia> prevind("α", 0) -ERROR: BoundsError: attempt to access String - at index [0] +ERROR: BoundsError: attempt to access String at index [0] [...] julia> prevind("α", 2, 2) @@ -528,8 +525,7 @@ julia> nextind("α", 1) 3 julia> nextind("α", 3) -ERROR: BoundsError: attempt to access String - at index [3] +ERROR: BoundsError: attempt to access String at index [3] [...] julia> nextind("α", 0, 2) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 09f4506fe7398..02769108d7426 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -205,13 +205,11 @@ Using an index less than `begin` (`1`) or greater than `end` raises an error: ```jldoctest helloworldstring julia> str[begin-1] -ERROR: BoundsError: attempt to access String - at index [0] +ERROR: BoundsError: attempt to access String at index [0] [...] julia> str[end+1] -ERROR: BoundsError: attempt to access String - at index [15] +ERROR: BoundsError: attempt to access String at index [15] [...] ``` diff --git a/test/errorshow.jl b/test/errorshow.jl index 04a1176c45ea9..1cc7c1d603a10 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -291,7 +291,7 @@ let undefvar @test occursin("DomainError with [0.0 -1.0 …", err_str) err_str = @except_str (1, 2, 3)[4] BoundsError - @test err_str == "BoundsError: attempt to access (1, 2, 3)\n at index [4]" + @test err_str == "BoundsError: attempt to access (1, 2, 3) at index [4]" err_str = @except_str [5, 4, 3][-2, 1] BoundsError @test err_str == "BoundsError: attempt to access 3-element Array{$Int,1} at index [-2, 1]" @@ -299,7 +299,7 @@ let undefvar @test err_str == "BoundsError: attempt to access 3-element Array{$Int,1} at index [1:5]" err_str = @except_str Bounded(2)[3] BoundsError - @test err_str == "BoundsError: attempt to access 2-size Bounded\n at index [3]" + @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]" err_str = @except_str 0::Bool TypeError @test err_str == "TypeError: non-boolean ($Int) used in boolean context" From 095e92d4e9ad2b90d525fec99f5474cd8d017b80 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 4 Jun 2020 16:27:28 -0400 Subject: [PATCH 102/232] fix #36104, assign global name during type definitions (#36121) also fixes #21816 --- base/compiler/tfuncs.jl | 2 +- src/builtins.c | 57 ++++++++++++++++++++++++----------------- src/jltypes.c | 3 +++ src/julia-syntax.scm | 33 +++++++++++++++++++----- test/core.jl | 17 ++++++++++++ 5 files changed, 81 insertions(+), 31 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 3e87db3265df5..d67f6f7ccc435 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -568,7 +568,7 @@ is_dt_const_field(fld::Int) = ( function const_datatype_getfield_tfunc(@nospecialize(sv), fld::Int) if fld == DATATYPE_INSTANCE_FIELDINDEX return isdefined(sv, fld) ? Const(getfield(sv, fld)) : Union{} - elseif is_dt_const_field(fld) + elseif is_dt_const_field(fld) && isdefined(sv, fld) return Const(getfield(sv, fld)) end return nothing diff --git a/src/builtins.c b/src/builtins.c index 049bd5c0e7f43..9c9fec857f9da 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1250,6 +1250,27 @@ JL_CALLABLE(jl_f__setsuper) void jl_reinstantiate_inner_types(jl_datatype_t *t); +static int equiv_field_types(jl_value_t *old, jl_value_t *ft) +{ + size_t nf = jl_svec_len(ft); + if (jl_svec_len(old) != nf) + return 0; + size_t i; + for (i = 0; i < nf; i++) { + jl_value_t *ta = jl_svecref(old, i); + jl_value_t *tb = jl_svecref(ft, i); + if (jl_has_free_typevars(ta)) { + if (!jl_has_free_typevars(tb) || !jl_egal(ta, tb)) + return 0; + } + else if (jl_has_free_typevars(tb) || jl_typeof(ta) != jl_typeof(tb) || + !jl_types_equal(ta, tb)) { + return 0; + } + } + return 1; +} + JL_CALLABLE(jl_f__typebody) { JL_NARGS(_typebody!, 1, 2); @@ -1258,16 +1279,23 @@ JL_CALLABLE(jl_f__typebody) if (nargs == 2) { jl_value_t *ft = args[1]; JL_TYPECHK(_typebody!, simplevector, ft); - dt->types = (jl_svec_t*)ft; - jl_gc_wb(dt, ft); - for (size_t i = 0; i < jl_svec_len(dt->types); i++) { - jl_value_t *elt = jl_svecref(dt->types, i); + size_t nf = jl_svec_len(ft); + for (size_t i = 0; i < nf; i++) { + jl_value_t *elt = jl_svecref(ft, i); if ((!jl_is_type(elt) && !jl_is_typevar(elt)) || jl_is_vararg_type(elt)) { jl_type_error_rt(jl_symbol_name(dt->name->name), "type definition", (jl_value_t*)jl_type_type, elt); } } + if (dt->types != NULL) { + if (!equiv_field_types((jl_value_t*)dt->types, ft)) + jl_errorf("invalid redefinition of type %s", jl_symbol_name(dt->name->name)); + } + else { + dt->types = (jl_svec_t*)ft; + jl_gc_wb(dt, ft); + } } JL_TRY { @@ -1294,15 +1322,13 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb) dta->name->name == dtb->name->name && dta->abstract == dtb->abstract && dta->mutabl == dtb->mutabl && - dta->size == dtb->size && + (jl_svec_len(jl_field_names(dta)) != 0 || dta->size == dtb->size) && dta->ninitialized == dtb->ninitialized && jl_egal((jl_value_t*)jl_field_names(dta), (jl_value_t*)jl_field_names(dtb)) && - jl_nparams(dta) == jl_nparams(dtb) && - jl_svec_len(dta->types) == jl_svec_len(dtb->types))) + jl_nparams(dta) == jl_nparams(dtb))) return 0; jl_value_t *a=NULL, *b=NULL; int ok = 1; - size_t i, nf = jl_svec_len(dta->types); JL_GC_PUSH2(&a, &b); a = jl_rewrap_unionall((jl_value_t*)dta->super, dta->name->wrapper); b = jl_rewrap_unionall((jl_value_t*)dtb->super, dtb->name->wrapper); @@ -1328,21 +1354,6 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb) a = jl_instantiate_unionall(ua, (jl_value_t*)ub->var); b = ub->body; } - assert(jl_is_datatype(a) && jl_is_datatype(b)); - a = (jl_value_t*)jl_get_fieldtypes((jl_datatype_t*)a); - b = (jl_value_t*)jl_get_fieldtypes((jl_datatype_t*)b); - for (i = 0; i < nf; i++) { - jl_value_t *ta = jl_svecref(a, i); - jl_value_t *tb = jl_svecref(b, i); - if (jl_has_free_typevars(ta)) { - if (!jl_has_free_typevars(tb) || !jl_egal(ta, tb)) - goto no; - } - else if (jl_has_free_typevars(tb) || jl_typeof(ta) != jl_typeof(tb) || - !jl_types_equal(ta, tb)) { - goto no; - } - } JL_GC_POP(); return 1; no: diff --git a/src/jltypes.c b/src/jltypes.c index 845936ed10410..b6e94a624ec20 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1773,6 +1773,9 @@ JL_DLLEXPORT jl_svec_t *jl_compute_fieldtypes(jl_datatype_t *st JL_PROPAGATES_RO assert(n > 0 && "expected empty case to be handled during construction"); //if (n == 0) // return ((st->types = jl_emptysvec)); + if (wt->types == NULL) + jl_errorf("cannot determine field types of incomplete type %s", + jl_symbol_name(st->name->name)); jl_typeenv_t *env = (jl_typeenv_t*)alloca(n * sizeof(jl_typeenv_t)); for (i = 0; i < n; i++) { env[i].var = (jl_tvar_t*)jl_svecref(wt->parameters, i); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 27ac5d1e812aa..a4489ad313409 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -884,7 +884,8 @@ (defs2 (if (null? defs) (default-inner-ctors name field-names field-types params bounds locs) defs)) - (min-initialized (min (ctors-min-initialized defs) (length fields)))) + (min-initialized (min (ctors-min-initialized defs) (length fields))) + (prev (make-ssavalue))) (let ((dups (has-dups field-names))) (if dups (error (string "duplicate field name: \"" (car dups) "\" is not unique")))) (for-each (lambda (v) @@ -898,16 +899,29 @@ (local-def ,name) ,@(map (lambda (v) `(local ,v)) params) ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v #t))) params bounds) - (toplevel-only struct) + (toplevel-only struct (outerref ,name)) (= ,name (call (core _structtype) (thismodule) (inert ,name) (call (core svec) ,@params) (call (core svec) ,@(map quotify field-names)) ,mut ,min-initialized)) (call (core _setsuper!) ,name ,super) - (call (core _typebody!) ,name (call (core svec) ,@field-types)) - (if (&& (isdefined (outerref ,name)) - (call (core _equiv_typedef) (outerref ,name) ,name)) - (null) + (if (isdefined (outerref ,name)) + (block + (= ,prev (outerref ,name)) + (if (call (core _equiv_typedef) ,prev ,name) + ;; if this is compatible with an old definition, use the existing type object + ;; and its parameters + (block (= ,name ,prev) + ,@(if (pair? params) + `((= (tuple ,@params) (|.| + ,(foldl (lambda (_ x) `(|.| ,x (quote body))) + prev + params) + (quote parameters)))) + '())) + ;; otherwise do an assignment to trigger an error + (= (outerref ,name) ,name))) (= (outerref ,name) ,name)) + (call (core _typebody!) ,name (call (core svec) ,@field-types)) (null))) ;; "inner" constructors (scope-block @@ -3351,7 +3365,12 @@ f(x) = yt(x) ((atom? e) e) (else (case (car e) - ((quote top core globalref outerref thismodule toplevel-only line break inert module toplevel null true false meta) e) + ((quote top core globalref outerref thismodule line break inert module toplevel null true false meta) e) + ((toplevel-only) + ;; hack to avoid generating a (method x) expr for struct types + (if (eq? (cadr e) 'struct) + (put! defined (caddr e) #t)) + e) ((=) (let ((var (cadr e)) (rhs (cl-convert (caddr e) fname lam namemap defined toplevel interp))) diff --git a/test/core.jl b/test/core.jl index f183db7ac31f4..cb982d4d902d3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7221,3 +7221,20 @@ struct AVL35416{K,V} avl:: Union{Nothing,Node35416{AVL35416{K,V},<:K,<:V}} end @test AVL35416(Node35416{AVL35416{Integer,AbstractString},Int,String}()) isa AVL35416{Integer,AbstractString} + +# issue #36104 +module M36104 +struct T36104 + v::Vector{M36104.T36104} +end +struct T36104 # check that redefining it works, issue #21816 + v::Vector{T36104} +end +end +@test fieldtypes(M36104.T36104) == (Vector{M36104.T36104},) +@test_throws ErrorException("expected") @eval(struct X36104; x::error("expected"); end) +@test @isdefined(X36104) +struct X36104; x::Int; end +@test fieldtypes(X36104) == (Int,) +primitive type P36104 8 end +@test_throws ErrorException("invalid redefinition of constant P36104") @eval(primitive type P36104 16 end) From 38238c8de3858c000aff3d3b5bf404afbeccad6c Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 4 Jun 2020 22:42:48 +0200 Subject: [PATCH 103/232] Fix zero(::Type{<:TwicePrecision}) for dimensionful quantities (#36113) --- base/twiceprecision.jl | 5 ++++- test/ranges.jl | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 5ba47c003f6f0..837e77f05a285 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -257,7 +257,10 @@ big(x::TwicePrecision) = big(x.hi) + big(x.lo) -(x::TwicePrecision) = TwicePrecision(-x.hi, -x.lo) -zero(::Type{TwicePrecision{T}}) where {T} = TwicePrecision{T}(0, 0) +function zero(::Type{TwicePrecision{T}}) where {T} + z = zero(T) + TwicePrecision{T}(z, z) +end # Arithmetic diff --git a/test/ranges.jl b/test/ranges.jl index 0777323b9ed66..66e1891e7fcab 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -193,6 +193,7 @@ end @test_throws ErrorException("Int is incommensurate with PhysQuantity") x*2 # not a MethodError for convert @test x.hi/2 === PhysQuantity{1}(2.0) @test_throws ErrorException("Int is incommensurate with PhysQuantity") x/2 + @test zero(typeof(x)) === Base.TwicePrecision(PhysQuantity{1}(0.0)) end @testset "ranges" begin @test size(10:1:0) == (0,) From 162cde194c2c303a69d3689f5992ff7df6358363 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 4 Jun 2020 22:49:43 +0200 Subject: [PATCH 104/232] fix ImmutableDict(pairs...) constructor (#36143) It could only handle a couple of pairs, e.g. ImmutableDict(1=>1, 2=>2, 3=>3) would throw. The fix is implemented by adding the `ImmutableDict(t::ImmutableDict, pairs...)` constructor, which generalizes `ImmutableDict(t::ImmutableDict, pair)` (with some similarity to how `push!` accepts multiple items to be pushed). --- base/dict.jl | 2 ++ test/dict.jl | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/base/dict.jl b/base/dict.jl index 1760e62338cf8..1f42ab8c82f85 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -739,6 +739,8 @@ Create a new entry in the `ImmutableDict` for a `key => value` pair ImmutableDict ImmutableDict(KV::Pair{K,V}) where {K,V} = ImmutableDict{K,V}(KV[1], KV[2]) ImmutableDict(t::ImmutableDict{K,V}, KV::Pair) where {K,V} = ImmutableDict{K,V}(t, KV[1], KV[2]) +ImmutableDict(t::ImmutableDict{K,V}, KV::Pair, rest::Pair...) where {K,V} = + ImmutableDict(ImmutableDict(t, KV), rest...) ImmutableDict(KV::Pair, rest::Pair...) = ImmutableDict(ImmutableDict(KV), rest...) function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) diff --git a/test/dict.jl b/test/dict.jl index 73e8e5bad6e2e..c281c0cda9424 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -723,7 +723,9 @@ import Base.ImmutableDict d5 = ImmutableDict(v...) @test d5 == d2 @test reverse(collect(d5)) == v - @test ImmutableDict(:a => 1, :a => 2)[:a] == 2 + d6 = ImmutableDict(:a => 1, :b => 3, :a => 2) + @test d6[:a] == 2 + @test d6[:b] == 3 @test !haskey(ImmutableDict(-0.0=>1), 0.0) end From b5868b93637ed89f7d0a3fd1a7f9d4ae247a3f11 Mon Sep 17 00:00:00 2001 From: jmert <2965436+jmert@users.noreply.github.com> Date: Thu, 4 Jun 2020 15:53:32 -0500 Subject: [PATCH 105/232] Satisfy Complex muladd TODO for mulsub with negated muladd (#36140) Modern LLVM has sufficient optimizations to translate muladds with multiple negations into the appropriate fused-multiple-subtract instructions on x86_64. --- base/complex.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/complex.jl b/base/complex.jl index dc828eb6ca2e0..f0402a713a14b 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -278,8 +278,8 @@ inv(z::Complex{<:Integer}) = inv(float(z)) real(z) * imag(w) + imag(z) * real(w)) muladd(z::Complex, w::Complex, x::Complex) = - Complex(muladd(real(z), real(w), real(x)) - imag(z)*imag(w), # TODO: use mulsub given #15985 - muladd(real(z), imag(w), muladd(imag(z), real(w), imag(x)))) + Complex(muladd(real(z), real(w), -muladd(imag(z), imag(w), -real(x))), + muladd(real(z), imag(w), muladd(imag(z), real(w), imag(x)))) # handle Bool and Complex{Bool} # avoid type signature ambiguity warnings @@ -326,7 +326,7 @@ muladd(z::Complex, x::Real, w::Complex) = Complex(muladd(real(z),x,real(w)), muladd(imag(z),x,imag(w))) muladd(x::Real, y::Real, z::Complex) = Complex(muladd(x,y,real(z)), imag(z)) muladd(z::Complex, w::Complex, x::Real) = - Complex(muladd(real(z), real(w), x) - imag(z)*imag(w), # TODO: use mulsub given #15985 + Complex(muladd(real(z), real(w), -muladd(imag(z), imag(w), -x)), muladd(real(z), imag(w), imag(z) * real(w))) /(a::R, z::S) where {R<:Real,S<:Complex} = (T = promote_type(R,S); a*inv(T(z))) From 287215fc1816594d66624c0d79214eb986dd8cff Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 5 Jun 2020 01:04:34 +0200 Subject: [PATCH 106/232] Mark Tuples with Bottom among their parameters as cacheable (#36152) Fixes #36100. --- src/jltypes.c | 2 +- test/subtype.jl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/jltypes.c b/src/jltypes.c index b6e94a624ec20..bba9362e9a64f 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1492,7 +1492,7 @@ static jl_tupletype_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec int cacheable = 1; for (size_t i = 0; i < np; i++) { assert(p[i]); - if (!jl_is_concrete_type(p[i])) + if (!jl_is_concrete_type(p[i]) && p[i] != jl_bottom_type) cacheable = 0; } return (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, p, np, cacheable, NULL, NULL); diff --git a/test/subtype.jl b/test/subtype.jl index 39bc4da86b08d..d528a2230fca9 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1753,3 +1753,7 @@ s26065 = Ref{Tuple{T,Ref{Union{Ref{Tuple{Ref{Union{Ref{Ref{Tuple{Ref{Tuple{Union @test !issub(Tuple{Type{T}, T} where T<:Tuple{String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}, String, Union{Base.Regex, AbstractChar, AbstractString}}, Tuple{Type{Tuple{Vararg{V, N} where N}}, Tuple{Vararg{V, N} where N}} where V) + +# issue 36100 +@test NamedTuple{(:a, :b), Tuple{Missing, Union{}}} == NamedTuple{(:a, :b), Tuple{Missing, Union{}}} +@test Val{Tuple{Missing, Union{}}} === Val{Tuple{Missing, Union{}}} From fa102c1b3c5d27b8f0e8542dbca61eb6e07c4963 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 4 Jun 2020 20:02:09 -0400 Subject: [PATCH 107/232] support contiguous subarrays in isvalid(String, ...) (#36047) --- base/strings/string.jl | 4 ++-- base/strings/unicode.jl | 6 +++++- test/strings/basic.jl | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/base/strings/string.jl b/base/strings/string.jl index 85695459b3438..89ec13fddef93 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -163,13 +163,13 @@ end ## checking UTF-8 & ACSII validity ## -byte_string_classify(s::Union{String,Vector{UInt8}}) = +byte_string_classify(s::Union{String,Vector{UInt8},FastContiguousSubArray{UInt8,1,Vector{UInt8}}}) = ccall(:u8_isvalid, Int32, (Ptr{UInt8}, Int), s, sizeof(s)) # 0: neither valid ASCII nor UTF-8 # 1: valid ASCII # 2: valid UTF-8 -isvalid(::Type{String}, s::Union{Vector{UInt8},String}) = byte_string_classify(s) ≠ 0 +isvalid(::Type{String}, s::Union{Vector{UInt8},FastContiguousSubArray{UInt8,1,Vector{UInt8}},String}) = byte_string_classify(s) ≠ 0 isvalid(s::String) = isvalid(String, s) is_valid_continuation(c) = c & 0xc0 == 0x80 diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 5d5bc93ef592b..55a7725c034d6 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -33,7 +33,8 @@ isvalid(value) Returns `true` if the given value is valid for that type. Types currently can be either `AbstractChar` or `String`. Values for `AbstractChar` can be of type `AbstractChar` or [`UInt32`](@ref). -Values for `String` can be of that type, or `Vector{UInt8}` or `SubString{String}`. +Values for `String` can be of that type, `SubString{String}`, `Vector{UInt8}`, +or a contiguous subarray thereof. # Examples ```jldoctest @@ -46,6 +47,9 @@ true julia> isvalid(Char, 0xd799) true ``` + +!!! compat "Julia 1.6" + Support for subarray values was added in Julia 1.6. """ isvalid(T,value) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index aa6974e53a157..a34093fa4530e 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -575,7 +575,9 @@ end for (rng, flg) in ((0x00:0x9f, false), (0xa0:0xbf, true), (0xc0:0xff, false)) for cont in rng @test isvalid(String, UInt8[0xe0, cont]) == false - @test isvalid(String, UInt8[0xe0, cont, 0x80]) == flg + bytes = UInt8[0xe0, cont, 0x80] + @test isvalid(String, bytes) == flg + @test isvalid(String, @view(bytes[1:end])) == flg # contiguous subarray support end end # Check three-byte sequences From 8c65f8d987669678d2e6b5d19c84c82b40fdd684 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 5 Jun 2020 02:08:32 +0200 Subject: [PATCH 108/232] give more information in StringIndexError (#36054) --- base/strings/basic.jl | 2 +- base/strings/string.jl | 13 +++++++++++++ doc/src/manual/strings.md | 9 +++++---- test/strings/basic.jl | 9 +++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index e4c88752a33d1..874101c361e7a 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -133,7 +133,7 @@ julia> isvalid(str, 2) false julia> str[2] -ERROR: StringIndexError("αβγdef", 2) +ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'α', [3]=>'β' Stacktrace: [...] ``` diff --git a/base/strings/string.jl b/base/strings/string.jl index 89ec13fddef93..c267b263c66c9 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -11,6 +11,19 @@ struct StringIndexError <: Exception end @noinline string_index_err(s::AbstractString, i::Integer) = throw(StringIndexError(s, Int(i))) +function Base.showerror(io::IO, exc::StringIndexError) + s = exc.string + print(io, "StringIndexError: ", "invalid index [$(exc.index)]") + if firstindex(s) <= exc.index <= ncodeunits(s) + iprev = thisind(s, exc.index) + inext = nextind(s, iprev) + if inext <= ncodeunits(s) + print(io, ", valid nearby indices [$iprev]=>'$(s[iprev])', [$inext]=>'$(s[inext])'") + else + print(io, ", valid nearby index [$iprev]=>'$(s[iprev])'") + end + end +end const ByteArray = Union{Vector{UInt8},Vector{Int8}} diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 02769108d7426..6c34b842a7c6a 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -279,11 +279,12 @@ julia> s[1] '∀': Unicode U+2200 (category Sm: Symbol, math) julia> s[2] -ERROR: StringIndexError("∀ x ∃ y", 2) +ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'∀', [4]=>' ' +Stacktrace: [...] julia> s[3] -ERROR: StringIndexError("∀ x ∃ y", 3) +ERROR: StringIndexError: invalid index [3], valid nearby indices [1]=>'∀', [4]=>' ' Stacktrace: [...] @@ -303,7 +304,7 @@ julia> s[end-1] ' ': ASCII/Unicode U+0020 (category Zs: Separator, space) julia> s[end-2] -ERROR: StringIndexError("∀ x ∃ y", 9) +ERROR: StringIndexError: invalid index [9], valid nearby indices [7]=>'∃', [10]=>' ' Stacktrace: [...] @@ -323,7 +324,7 @@ julia> s[1:1] "∀" julia> s[1:2] -ERROR: StringIndexError("∀ x ∃ y", 2) +ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'∀', [4]=>' ' Stacktrace: [...] diff --git a/test/strings/basic.jl b/test/strings/basic.jl index a34093fa4530e..9d553220e4b69 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1076,3 +1076,12 @@ let x = SubString("ab", 1, 1) @test y === x chop("ab") === chop.(["ab"])[1] end + +@testset "show StringIndexError" begin + str = "abcdefghκijklmno" + e = StringIndexError(str, 10) + @test sprint(showerror, e) == "StringIndexError: invalid index [10], valid nearby indices [9]=>'κ', [11]=>'i'" + str = "κ" + e = StringIndexError(str, 2) + @test sprint(showerror, e) == "StringIndexError: invalid index [2], valid nearby index [1]=>'κ'" +end From f90afa68717173c3ff0816b09d0db59efab6f013 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 5 Jun 2020 09:00:06 +0200 Subject: [PATCH 109/232] Update base/uuid.jl Co-authored-by: Jeff Bezanson --- base/uuid.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/uuid.jl b/base/uuid.jl index eb997fc5d2cac..1e64e2e4ae00c 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -37,7 +37,7 @@ let d = __convert_digit(_c, UInt32(16)) d >= 16 && throw_malformed_uuid(s) u <<= 4 - u |= d + u | d end global UUID From 26f0f069dd1dd61dc4f77bd620a285f36f153fc4 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 5 Jun 2020 14:10:38 +0200 Subject: [PATCH 110/232] add a summary for AbstractString (#36139) * add a summary for AbstractString --- base/strings/basic.jl | 15 +++++++++++---- doc/src/manual/strings.md | 4 ++-- test/strings/basic.jl | 6 ++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 874101c361e7a..6d683e8740583 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -229,6 +229,13 @@ Symbol(x...) = Symbol(string(x...)) convert(::Type{T}, s::T) where {T<:AbstractString} = s convert(::Type{T}, s::AbstractString) where {T<:AbstractString} = T(s) +## summary ## + +function summary(io::IO, s::AbstractString) + prefix = isempty(s) ? "empty" : string(ncodeunits(s), "-codeunit") + print(io, prefix, " ", typeof(s)) +end + ## string & character concatenation ## """ @@ -412,11 +419,11 @@ julia> thisind("α", 3) 3 julia> thisind("α", 4) -ERROR: BoundsError: attempt to access String at index [4] +ERROR: BoundsError: attempt to access 2-codeunit String at index [4] [...] julia> thisind("α", -1) -ERROR: BoundsError: attempt to access String at index [-1] +ERROR: BoundsError: attempt to access 2-codeunit String at index [-1] [...] ``` """ @@ -466,7 +473,7 @@ julia> prevind("α", 1) 0 julia> prevind("α", 0) -ERROR: BoundsError: attempt to access String at index [0] +ERROR: BoundsError: attempt to access 2-codeunit String at index [0] [...] julia> prevind("α", 2, 2) @@ -525,7 +532,7 @@ julia> nextind("α", 1) 3 julia> nextind("α", 3) -ERROR: BoundsError: attempt to access String at index [3] +ERROR: BoundsError: attempt to access 2-codeunit String at index [3] [...] julia> nextind("α", 0, 2) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 6c34b842a7c6a..56e7336c3d926 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -205,11 +205,11 @@ Using an index less than `begin` (`1`) or greater than `end` raises an error: ```jldoctest helloworldstring julia> str[begin-1] -ERROR: BoundsError: attempt to access String at index [0] +ERROR: BoundsError: attempt to access 14-codeunit String at index [0] [...] julia> str[end+1] -ERROR: BoundsError: attempt to access String at index [15] +ERROR: BoundsError: attempt to access 14-codeunit String at index [15] [...] ``` diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 9d553220e4b69..465075cb88a3d 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -1085,3 +1085,9 @@ end e = StringIndexError(str, 2) @test sprint(showerror, e) == "StringIndexError: invalid index [2], valid nearby index [1]=>'κ'" end + +@testset "summary" begin + @test sprint(summary, "foα") == "4-codeunit String" + @test sprint(summary, SubString("foα", 2)) == "3-codeunit SubString{String}" + @test sprint(summary, "") == "empty String" +end From 8552817ce32c739d2e7403c1d74fbd3e04b80b79 Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Fri, 5 Jun 2020 14:30:15 +0100 Subject: [PATCH 111/232] Update mathematical-operations.md (#36065) * Update mathematical-operations.md Apply some remaining review suggestions from #35996 * Use @ref links --- doc/src/manual/mathematical-operations.md | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 5227c8821c1be..54f0b36413c58 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -22,12 +22,6 @@ are supported on all primitive numeric types: | `x ^ y` | power | raises `x` to the `y`th power | | `x % y` | remainder | equivalent to `rem(x,y)` | -as well as the negation on [`Bool`](@ref) types: - -| Expression | Name | Description | -|:---------- |:-------- |:---------------------------------------- | -| `!x` | negation | changes `true` to `false` and vice versa | - A numeric literal placed directly before an identifier or parentheses, e.g. `2x` or `2(x+y)`, is treated as a multiplication, except with higher precedence than other binary operations. See [Numeric Literal Coefficients](@ref man-numeric-literal-coefficients) for details. Julia's promotion system makes arithmetic operations on mixtures of argument types "just work" @@ -65,17 +59,18 @@ This is useful for preventing the propagation of `NaN` values in quantities that ## Boolean Operators -The following [Boolean operators](https://en.wikipedia.org/wiki/Boolean_algebra#Operations) are supported on `Bool`: +The following [Boolean operators](https://en.wikipedia.org/wiki/Boolean_algebra#Operations) are supported on [`Bool`](@ref) types: + +| Expression | Name | +|:---------- |:--------------------------------------------------------| +| `!x` | negation | +| `x && y` | [short-circuiting and](@ref man-conditional-evaluation) | +| `x \|\| y` | [short-circuiting or](@ref man-conditional-evaluation) | -| Expression | Name | -|:---------- |:--------------------------------------| -| `!x` | not | -| `x && y` | [short-circuiting and][short-circuit] | -| `x \|\| y` | [short-circuiting or][short-circuit] | +Negation changes `true` to `false` and vice versa. The short-circuiting opeations are explained on the linked page. Note that `Bool` is an integer type and all the usual promotion rules and numeric operators are also defined on it. -[short-circuit]: https://docs.julialang.org/en/v1/manual/control-flow/#Short-Circuit-Evaluation-1 ## Bitwise Operators The following [bitwise operators](https://en.wikipedia.org/wiki/Bitwise_operation#Bitwise_operators) From 9cb419335f30bcf44f7dcb074acec8de8bb29f3e Mon Sep 17 00:00:00 2001 From: KristofferC Date: Fri, 5 Jun 2020 15:59:20 +0200 Subject: [PATCH 112/232] bump Pkg Version --- .../Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/md5 | 1 + .../Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/sha512 | 1 + .../Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/md5 | 1 - .../Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/md5 create mode 100644 deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/sha512 diff --git a/deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/md5 b/deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/md5 new file mode 100644 index 0000000000000..f90a80b832756 --- /dev/null +++ b/deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/md5 @@ -0,0 +1 @@ +c6f22402e2ac1e3d55f56c917b5b5dda diff --git a/deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/sha512 b/deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/sha512 new file mode 100644 index 0000000000000..62cdcb7cdf11d --- /dev/null +++ b/deps/checksums/Pkg-f9a5cc7dfce849c0b5f2765b8d462170a23d562e.tar.gz/sha512 @@ -0,0 +1 @@ +a46c7f574e49eeec10bf2962a1a227a83af3001e078f3c8260a00db87a925fe0d099d6a6eeda592436542d9a91b32da69d3dada17affcd2fc1f3d1f329af0125 diff --git a/deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/md5 b/deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/md5 deleted file mode 100644 index a47a137bb59b5..0000000000000 --- a/deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -3ef6ca69b7cdef18228ccab2ce20d84f diff --git a/deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/sha512 b/deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/sha512 deleted file mode 100644 index d91c754e91496..0000000000000 --- a/deps/checksums/Pkg-fff698d3d7d90a55431ad2f4fa0af9719d899954.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -eb69f2f5abb8b9a3ec7abd52beff2286d7c2b3861b57459d63dc6783c28c64cb034c539d2b1b05f3bd696ae8adae32cc330b540ad111a06005879f00066a05b9 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index cab9544700174..270aa2c1e0e89 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,2 +1,2 @@ PKG_BRANCH = master -PKG_SHA1 = fff698d3d7d90a55431ad2f4fa0af9719d899954 +PKG_SHA1 = f9a5cc7dfce849c0b5f2765b8d462170a23d562e From 4b7e36a77bce95311c7f71b9da43fac54a66c90f Mon Sep 17 00:00:00 2001 From: KristofferC Date: Fri, 5 Jun 2020 16:01:30 +0200 Subject: [PATCH 113/232] bump Statistics version --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/Statistics.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/md5 delete mode 100644 deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/sha512 create mode 100644 deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 create mode 100644 deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 diff --git a/deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/md5 b/deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/md5 deleted file mode 100644 index cf856f514f1fe..0000000000000 --- a/deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -14b820d6b0c164e36440329304dd8dbb diff --git a/deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/sha512 b/deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/sha512 deleted file mode 100644 index b7c828d720ff7..0000000000000 --- a/deps/checksums/Statistics-42f46093985058665b085f24001882f3f33a400c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7160582d061aeeed18d24fe8981a85e02086e2dbe3cda4f3786382351f1764117701a01d5f217b82d9263e0ac63f2caa2ce6ee5ad20dfc6e5cebba6af03b8826 diff --git a/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 new file mode 100644 index 0000000000000..a7f8d6e48bf8b --- /dev/null +++ b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 @@ -0,0 +1 @@ +f038b99cd73f2ed8d132b88676b4ec64 diff --git a/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 new file mode 100644 index 0000000000000..3c6eb1a4a45cd --- /dev/null +++ b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 @@ -0,0 +1 @@ +7c3b7086e74e53465405c7130182c8016d894151a89f16af8037018aebde495cf599edfd631c7fc14faca5a5052c168b1f0373bc124bec3c0ac7851667689bcf diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index 7cfb1ca67170b..02e1285dce56f 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,2 +1,2 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = 42f46093985058665b085f24001882f3f33a400c +STATISTICS_SHA1 = cde87c8062032883165cd242f4a5c6b7943cb0b1 From 1ef13ac34bbf3cb247527e8b7f78f3f57d4cee65 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Fri, 5 Jun 2020 16:18:57 +0200 Subject: [PATCH 114/232] Bump version in readme (#36157) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 608863b8904df..8da7a3a480af3 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Julia. However, most users should use the most recent stable version of Julia. You can get this version by changing to the Julia directory and running: - git checkout v1.3.0 + git checkout v1.4.2 Now run `make` to build the `julia` executable. From d825de85f89034bca81524b3def72f6237f08de0 Mon Sep 17 00:00:00 2001 From: Adam B Date: Fri, 5 Jun 2020 13:56:25 -0400 Subject: [PATCH 115/232] Give example docstring one-line summary (#36158) Point 2 of conventions immediately below this example calls for one line summary. Closes #36147 --- doc/src/manual/documentation.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/documentation.md b/doc/src/manual/documentation.md index 66a9f8128a202..6eab8b031e294 100644 --- a/doc/src/manual/documentation.md +++ b/doc/src/manual/documentation.md @@ -29,8 +29,9 @@ Here is a more complex example, still using Markdown: """ bar(x[, y]) -Compute the Bar index between `x` and `y`. If `y` is missing, compute -the Bar index between all pairs of columns of `x`. +Compute the Bar index between `x` and `y`. + +If `y` is unspecified, compute the Bar index between all pairs of columns of `x`. # Examples ```julia-repl From 9fbd135550f240b0a45822008a0d5fb6a0ce28b1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 28 May 2020 14:06:23 -0400 Subject: [PATCH 116/232] IdDict: handle size zero inputs gracefully --- src/gf.c | 10 +++++----- src/iddict.c | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/gf.c b/src/gf.c index ca51b5f24917b..49f3c5a5709e3 100644 --- a/src/gf.c +++ b/src/gf.c @@ -418,11 +418,11 @@ static void foreach_mtable_in_module( jl_module_t *m, void (*visit)(jl_methtable_t *mt, void *env), void *env, - jl_array_t *visited) + jl_array_t **visited) { size_t i; void **table = m->bindings.table; - jl_eqtable_put(visited, (jl_value_t*)m, jl_true, NULL); + *visited = jl_eqtable_put(*visited, (jl_value_t*)m, jl_true, NULL); for (i = 1; i < m->bindings.size; i += 2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; @@ -440,7 +440,7 @@ static void foreach_mtable_in_module( else if (jl_is_module(v)) { jl_module_t *child = (jl_module_t*)v; if (child != m && child->parent == m && child->name == b->name && - !jl_eqtable_get(visited, v, NULL)) { + !jl_eqtable_get(*visited, v, NULL)) { // this is the original/primary binding for the submodule foreach_mtable_in_module(child, visit, env, visited); } @@ -463,11 +463,11 @@ void jl_foreach_reachable_mtable(void (*visit)(jl_methtable_t *mt, void *env), v jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(mod_array, i); assert(jl_is_module(m)); if (!jl_eqtable_get(visited, (jl_value_t*)m, NULL)) - foreach_mtable_in_module(m, visit, env, visited); + foreach_mtable_in_module(m, visit, env, &visited); } } else { - foreach_mtable_in_module(jl_main_module, visit, env, visited); + foreach_mtable_in_module(jl_main_module, visit, env, &visited); } JL_GC_POP(); } diff --git a/src/iddict.c b/src/iddict.c index 6bec76563d07d..4245ce61a5f80 100644 --- a/src/iddict.c +++ b/src/iddict.c @@ -37,7 +37,11 @@ static int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_t *val) jl_array_t *a = *pa; size_t orig, index, iter, empty_slot; size_t newsz, sz = hash_size(a); - assert(sz >= 1); + if (sz == 0) { + a = jl_alloc_vec_any(HT_N_INLINE); + sz = hash_size(a); + *pa = a; + } size_t maxprobe = max_probe(sz); void **tab = (void **)a->data; @@ -108,7 +112,8 @@ static int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_t *val) jl_value_t **jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEPOINT { size_t sz = hash_size(a); - assert(sz >= 1); + if (sz == 0) + return NULL; size_t maxprobe = max_probe(sz); void **tab = (void **)a->data; uint_t hv = keyhash(key); From 40bb06d62f51d1f213d43a188b5b9fc8f976177b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 01:35:33 -0400 Subject: [PATCH 117/232] fix bad tests --- test/channels.jl | 2 ++ test/reflection.jl | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/channels.jl b/test/channels.jl index 0eb1f589f4f5c..d4d8cf6e2e67d 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -390,6 +390,7 @@ end t = Timer(0) do t tc[] += 1 end + Libc.systemsleep(0.005) @test isopen(t) Base.process_events() @test !isopen(t) @@ -402,6 +403,7 @@ end t = Timer(0) do t tc[] += 1 end + Libc.systemsleep(0.005) @test isopen(t) close(t) @test !isopen(t) diff --git a/test/reflection.jl b/test/reflection.jl index 4b77f2e2ac058..23a6ff0dfbe69 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -507,7 +507,6 @@ f18888() = nothing let world = Core.Compiler.get_world_counter() m = first(methods(f18888, Tuple{})) - @test isempty(m.specializations) ft = typeof(f18888) code_typed(f18888, Tuple{}; optimize=false) From 166031c06cefa1b10d6e7eb1ae1af4a87d05328f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 02:01:24 -0400 Subject: [PATCH 118/232] improve code quality --- base/errorshow.jl | 2 +- stdlib/REPL/src/REPLCompletions.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 05c6c6d787ecd..2f925c11b29ca 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -176,7 +176,7 @@ function showerror(io::IO, ex::InexactError) Experimental.show_error_hints(io, ex) end -typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} +typesof(@nospecialize args...) = Tuple{Any[ Core.Typeof(args[i]) for i in 1:length(args) ]...} function print_with_compare(io::IO, @nospecialize(a::DataType), @nospecialize(b::DataType), color::Symbol) if a.name === b.name diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index a16565bba3751..4007122aee41d 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -395,7 +395,7 @@ end # Returns the return type. example: get_type(:(Base.strip("", ' ')), Main) returns (String, true) function try_get_type(sym::Expr, fn::Module) val, found = get_value(sym, fn) - found && return Base.typesof(val).parameters[1], found + found && return Core.Typeof(val), found if sym.head === :call # getfield call is special cased as the evaluation of getfield provides good type information, # is inexpensive and it is also performed in the complete_symbol function. @@ -403,7 +403,7 @@ function try_get_type(sym::Expr, fn::Module) if isa(a1,GlobalRef) && isconst(a1.mod,a1.name) && isdefined(a1.mod,a1.name) && eval(a1) === Core.getfield val, found = get_value_getfield(sym, Main) - return found ? Base.typesof(val).parameters[1] : Any, found + return found ? Core.Typeof(val) : Any, found end return get_type_call(sym) elseif sym.head === :thunk @@ -430,7 +430,7 @@ end function get_type(sym, fn::Module) val, found = get_value(sym, fn) - return found ? Base.typesof(val).parameters[1] : Any, found + return found ? Core.Typeof(val) : Any, found end # Method completion on function call expression that look like :(max(1)) From f4a983d51b91f3fe7ef2e4f4c27ff51fabfe8add Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 14:13:50 -0400 Subject: [PATCH 119/232] remove incorrect assertion Since we are doing a subtype search (like a the top of the function), the resulting object from the search does not need to be the same. --- src/gf.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/gf.c b/src/gf.c index 49f3c5a5709e3..91d0d89afcd3d 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1072,26 +1072,15 @@ static jl_method_instance_t *cache_method( temp2 = (jl_value_t*)simplett; } - // short-circuit if this exact entry is already present - // to avoid adding a new duplicate copy of it - if (cachett != tt && simplett == NULL) { - struct jl_typemap_assoc search = {(jl_value_t*)cachett, min_valid, NULL, 0, ~(size_t)0}; + // short-circuit if an existing entry is already present + // that satisfies our requirements + if (cachett != tt) { + struct jl_typemap_assoc search = {(jl_value_t*)cachett, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(*cache, &search, offs, /*subtype*/1); - if (entry && (jl_value_t*)entry->simplesig == jl_nothing) { - if (jl_egal((jl_value_t*)guardsigs, (jl_value_t*)entry->guardsigs)) { - // just update the existing entry to reflect new knowledge - if (entry->min_world > min_valid) - entry->min_world = min_valid; - if (entry->max_world < max_valid) - entry->max_world = max_valid; - if (entry->func.linfo == NULL) { - entry->func.linfo = newmeth; - jl_gc_wb(entry, newmeth); - } - assert(entry->func.linfo == newmeth); - JL_GC_POP(); - return newmeth; - } + if (entry && jl_egal((jl_value_t*)entry->simplesig, simplett ? (jl_value_t*)simplett : jl_nothing) && + jl_egal((jl_value_t*)guardsigs, (jl_value_t*)entry->guardsigs)) { + JL_GC_POP(); + return entry->func.linfo; } } From be77eb8c1d7eb99ce60a7ece077dbbf85b1ab16c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 28 May 2020 14:44:12 -0400 Subject: [PATCH 120/232] For dispatch, move from using a tree to a hash lookup of leaf types This lets us put more objects in here without incurring additional search code (just the initial cost of computing the hash for the tuple type lookup computation). --- src/ast.c | 2 +- src/builtins.c | 3 +- src/datatype.c | 1 + src/dump.c | 3 - src/gf.c | 213 ++++++++++++++++++++++++++++++++++--------- src/init.c | 1 - src/jltypes.c | 42 ++++----- src/julia.h | 1 + src/julia_internal.h | 17 ++-- src/precompile.c | 7 -- src/typemap.c | 61 +++++++------ 11 files changed, 232 insertions(+), 119 deletions(-) diff --git a/src/ast.c b/src/ast.c index 3b4b3e5d78f7d..51cf262ad9ff2 100644 --- a/src/ast.c +++ b/src/ast.c @@ -962,7 +962,7 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule jl_value_t *result; JL_TRY { margs[0] = jl_toplevel_eval(*ctx, margs[0]); - jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, 1, world); + jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, world); JL_GC_PROMISE_ROOTED(mfunc); if (mfunc == NULL) { jl_method_error(margs[0], &margs[1], nargs, world); diff --git a/src/builtins.c b/src/builtins.c index 9c9fec857f9da..3ea207cf7d55a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -990,8 +990,7 @@ JL_CALLABLE(jl_f_applicable) { JL_NARGSV(applicable, 1); size_t world = jl_get_ptls_states()->world_age; - return jl_method_lookup(args, nargs, 1, world) != NULL ? - jl_true : jl_false; + return jl_method_lookup(args, nargs, world) != NULL ? jl_true : jl_false; } JL_CALLABLE(jl_f_invoke) diff --git a/src/datatype.c b/src/datatype.c index 0260281c7470b..d22551895d99a 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -49,6 +49,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo mt->name = jl_demangle_typename(name); mt->module = module; mt->defs = jl_nothing; + mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; mt->cache = jl_nothing; mt->max_args = 0; mt->kwsorter = NULL; diff --git a/src/dump.c b/src/dump.c index 7cb4ffc47d092..35303932bc161 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2237,9 +2237,6 @@ STATIC_INLINE jl_value_t *verify_type(jl_value_t *v) JL_NOTSAFEPOINT } #endif -jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type); -void jl_cache_type_(jl_datatype_t *type); - static jl_datatype_t *jl_recache_type(jl_datatype_t *dt) JL_GC_DISABLED { jl_datatype_t *t; // the type after unique'ing diff --git a/src/gf.c b/src/gf.c index 91d0d89afcd3d..08de860090bed 100644 --- a/src/gf.c +++ b/src/gf.c @@ -231,7 +231,8 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a m->sig = (jl_value_t*)jl_anytuple_type; m->slot_syms = jl_an_empty_string; - JL_GC_PUSH1(&m); + jl_typemap_entry_t *newentry = NULL; + JL_GC_PUSH2(&m, &newentry); jl_method_instance_t *mi = jl_get_specialized(m, (jl_value_t*)jl_anytuple_type, jl_emptysvec); m->unspecialized = mi; jl_gc_wb(m, mi); @@ -242,8 +243,10 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a codeinst->invoke = jl_fptr_args; jl_methtable_t *mt = dt->name->mt; - jl_typemap_insert(&mt->cache, (jl_value_t*)mt, jl_anytuple_type, - NULL, jl_emptysvec, (jl_value_t*)mi, 0, &lambda_cache, 1, ~(size_t)0); + newentry = jl_typemap_alloc(jl_anytuple_type, NULL, jl_emptysvec, + (jl_value_t*)mi, 1, ~(size_t)0); + jl_typemap_insert(&mt->cache, (jl_value_t*)mt, newentry, 0, &lambda_cache); + mt->frozen = 1; JL_GC_POP(); return dt; @@ -475,8 +478,10 @@ void jl_foreach_reachable_mtable(void (*visit)(jl_methtable_t *mt, void *env), v static void reset_mt_caches(jl_methtable_t *mt, void *env) { // removes all method caches - if (mt->defs != jl_nothing) // make sure not to reset builtin functions + if (mt->defs != jl_nothing) { // make sure not to reset builtin functions + mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; mt->cache = jl_nothing; + } jl_typemap_visitor(mt->defs, get_method_unspec_list, env); } @@ -543,9 +548,10 @@ jl_value_t *jl_nth_slot_type(jl_value_t *sig, size_t i) // return 1; //} -static jl_value_t *ml_matches(jl_typemap_t *ml, int offs, +static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, jl_tupletype_t *type, int lim, int include_ambiguous, - size_t world, size_t *min_valid, size_t *max_valid); + size_t world, size_t *min_valid, size_t *max_valid, + int cache_result); // get the compilation signature specialization for this method static void jl_compilation_sig( @@ -930,6 +936,19 @@ JL_DLLEXPORT int jl_isa_compileable_sig( return 1; } +static inline jl_typemap_entry_t *lookup_leafcache(jl_array_t *leafcache JL_PROPAGATES_ROOT, jl_value_t *tt, size_t world) JL_NOTSAFEPOINT +{ + jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_eqtable_get(leafcache, (jl_value_t*)tt, NULL); + if (entry) { + do { + if (entry->min_world <= world && world <= entry->max_world) + return entry; + entry = entry->next; + } while ((jl_value_t*)entry != jl_nothing); + } + return NULL; +} + static jl_method_instance_t *cache_method( jl_methtable_t *mt, jl_typemap_t **cache, jl_value_t *parent JL_PROPAGATES_ROOT, jl_tupletype_t *tt, // the original tupletype of the signature @@ -941,6 +960,12 @@ static jl_method_instance_t *cache_method( // short-circuit (now that we hold the lock) if this entry is already present int8_t offs = mt ? jl_cachearg_offset(mt) : 1; { // scope block + if (mt) { + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + if (entry) + return entry->func.linfo; + } struct jl_typemap_assoc search = {(jl_value_t*)tt, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(*cache, &search, offs, /*subtype*/1); if (entry && entry->func.value) @@ -976,7 +1001,7 @@ static jl_method_instance_t *cache_method( if (!cache_with_orig && mt) { // now examine what will happen if we chose to use this sig in the cache // TODO: should we first check `compilationsig <: definition`? - temp = ml_matches(mt->defs, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid, &max_valid); + temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid, &max_valid, 0); int guards = 0; if (temp == jl_false) { cache_with_orig = 1; @@ -1039,7 +1064,7 @@ static jl_method_instance_t *cache_method( if (cache_with_orig && mt) { // now examine defs to determine the min/max-valid range for this lookup result - (void)ml_matches(mt->defs, 0, cachett, -1, 0, world, &min_valid, &max_valid); + (void)ml_matches(mt, 0, cachett, -1, 0, world, &min_valid, &max_valid, 0); } assert(mt == NULL || min_valid > 1); @@ -1084,9 +1109,27 @@ static jl_method_instance_t *cache_method( } } - jl_typemap_insert(cache, parent, cachett, simplett, guardsigs, - (jl_value_t*)newmeth, offs, &lambda_cache, - min_valid, max_valid); + jl_typemap_entry_t *newentry = jl_typemap_alloc(cachett, simplett, guardsigs, (jl_value_t*)newmeth, min_valid, max_valid); + temp = (jl_value_t*)newentry; + if (mt && cachett == tt && simplett == NULL && jl_svec_len(guardsigs) == 0) { + if (!jl_has_free_typevars((jl_value_t*)tt) && jl_lookup_cache_type_(tt) == NULL) { + // if this type isn't normally in the cache, force it in there now + // anyways so that we can depend on it as a token (especially since + // we just cached it in memory as this method signature anyways) + JL_LOCK(&typecache_lock); + if (jl_lookup_cache_type_(tt) == NULL) + jl_cache_type_(tt); + JL_UNLOCK(&typecache_lock); // Might GC + } + jl_typemap_entry_t *old = (jl_typemap_entry_t*)jl_eqtable_get(mt->leafcache, (jl_value_t*)tt, jl_nothing); + newentry->next = old; + jl_gc_wb(newentry, old); + mt->leafcache = jl_eqtable_put(mt->leafcache, (jl_value_t*)tt, (jl_value_t*)newentry, NULL); + jl_gc_wb(mt, mt->leafcache); + } + else { + jl_typemap_insert(cache, parent, newentry, offs, &lambda_cache); + } JL_GC_POP(); return newmeth; @@ -1160,12 +1203,20 @@ static jl_typemap_entry_t *jl_typemap_morespecific_by_type(jl_typemap_entry_t *f return candidate; } -static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, int mt_cache, size_t world) +static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, size_t world) { // caller must hold the mt->writelock + assert(tt->isdispatchtuple || tt->hasfreetypevars); + if (tt->isdispatchtuple) { + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + if (entry) + return entry->func.linfo; + } + struct jl_typemap_assoc search = {(jl_value_t*)tt, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, &search, jl_cachearg_offset(mt), /*subtype*/1); - if (entry && entry->func.value) + if (entry) return entry->func.linfo; jl_method_instance_t *nf = NULL; @@ -1178,17 +1229,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype entry = jl_typemap_morespecific_by_type(entry, (jl_value_t*)tt, &search.env, world); if (entry != NULL) { jl_method_t *m = entry->func.method; - jl_svec_t *env = search.env; - if (!mt_cache) { - intptr_t nspec = (mt == jl_type_type_mt ? m->nargs + 1 : mt->max_args + 2); - jl_compilation_sig(tt, env, m, nspec, &newparams); - if (newparams) - tt = jl_apply_tuple_type(newparams); - nf = jl_specializations_get_linfo(m, (jl_value_t*)tt, env); - } - else { - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, env); - } + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, search.env); } } JL_GC_POP(); @@ -1675,15 +1716,22 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho (void)check_ambiguous_matches(mt->defs, methodentry, check_disabled_ambiguous_visitor); // drop this method from mt->cache struct invalidate_mt_env mt_cache_env; - mt_cache_env.max_world = methodentry->max_world - 1; + mt_cache_env.max_world = methodentry->max_world; mt_cache_env.shadowed = (jl_value_t*)method; jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); + jl_array_t *leafcache = mt->leafcache; + size_t i, l = jl_array_len(leafcache); + for (i = 1; i < l; i += 2) { + jl_value_t *l = jl_array_ptr_ref(leafcache, i); + if (l && l != jl_nothing) + invalidate_mt_cache((jl_typemap_entry_t*)l, (void*)&mt_cache_env); + } // Invalidate the backedges - jl_svec_t *specializations = methodentry->func.method->specializations; int invalidated = 0; + jl_svec_t *specializations = methodentry->func.method->specializations; jl_value_t *loctag = NULL; JL_GC_PUSH1(&loctag); - size_t i, l = jl_svec_len(specializations); + l = jl_svec_len(specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); if (mi) { @@ -1718,7 +1766,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method size_t max_world = method->primary_world - 1; int invalidated = 0; jl_value_t *loctag = NULL; // debug info for invalidation - JL_GC_PUSH2(&oldvalue, &loctag); + jl_typemap_entry_t *newentry = NULL; + JL_GC_PUSH3(&oldvalue, &newentry, &loctag); JL_LOCK(&mt->writelock); // first delete the existing entry (we'll disable it later) struct jl_typemap_assoc search = {(jl_value_t*)type, method->primary_world, NULL, 0, ~(size_t)0}; @@ -1728,9 +1777,9 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method // TODO: just append our new entry right here } // then add our new entry - jl_typemap_entry_t *newentry = jl_typemap_insert(&mt->defs, (jl_value_t*)mt, - (jl_tupletype_t*)type, simpletype, jl_emptysvec, (jl_value_t*)method, 0, &method_defs, - method->primary_world, method->deleted_world); + newentry = jl_typemap_alloc((jl_tupletype_t*)type, simpletype, jl_emptysvec, + (jl_value_t*)method, method->primary_world, method->deleted_world); + jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, 0, &method_defs); oldvalue = check_ambiguous_matches(mt->defs, newentry, check_ambiguous_visitor); if (oldentry) { oldvalue = oldentry->func.value; @@ -1769,9 +1818,17 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method mt_cache_env.max_world = max_world; mt_cache_env.shadowed = oldvalue; jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); - //TODO: if it's small, might it be better to drop it all too? + jl_array_t *leafcache = mt->leafcache; + size_t i, l = jl_array_len(leafcache); + for (i = 1; i < l; i += 2) { + jl_value_t *l = jl_array_ptr_ref(leafcache, i); + if (l && l != jl_nothing) + invalidate_mt_cache((jl_typemap_entry_t*)l, (void*)&mt_cache_env); + } + //TODO: if it's small, might it be better to drop it all? //if (mt != jl_type_type_mt) { // mt->cache = jl_nothing; + // mt->leafcache = jl_an_empty_vec_any; //} jl_value_t **d; @@ -1855,17 +1912,21 @@ jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs return jl_inst_arg_tuple_type(arg1, args, nargs, 1); } -jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, int cache, size_t world) +jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, size_t world) { assert(nargs > 0 && "expected caller to handle this case"); jl_methtable_t *mt = jl_gf_mtable(args[0]); jl_typemap_entry_t *entry = jl_typemap_assoc_exact(mt->cache, args[0], &args[1], nargs, jl_cachearg_offset(mt), world); if (entry) return entry->func.linfo; - JL_LOCK(&mt->writelock); jl_tupletype_t *tt = arg_type_tuple(args[0], &args[1], nargs); + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + if (entry) + return entry->func.linfo; JL_GC_PUSH1(&tt); - jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, tt, cache, world); + JL_LOCK(&mt->writelock); + jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, tt, world); JL_GC_POP(); JL_UNLOCK(&mt->writelock); return sf; @@ -1888,7 +1949,7 @@ JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int jl_methtable_t *mt = jl_method_table_for(unw); if ((jl_value_t*)mt == jl_nothing) return jl_false; // indeterminate - ml_matches can't deal with this case - return ml_matches(mt->defs, 0, types, lim, include_ambiguous, world, min_valid, max_valid); + return ml_matches(mt, 0, types, lim, include_ambiguous, world, min_valid, max_valid, 1); } jl_method_instance_t *jl_get_unspecialized(jl_method_instance_t *method JL_PROPAGATES_ROOT) @@ -2335,16 +2396,23 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t LOOP_BODY(3); #undef LOOP_BODY i = 4; - // if no method was found in the associative cache, check the full cache + jl_tupletype_t *tt = NULL; + int64_t last_alloc; if (i == 4) { + // if no method was found in the associative cache, check the full cache JL_TIMING(METHOD_LOOKUP_FAST); mt = jl_gf_mtable(F); entry = jl_typemap_assoc_exact(mt->cache, F, args, nargs, jl_cachearg_offset(mt), world); + if (entry == NULL) { + last_alloc = jl_options.malloc_log ? jl_gc_diff_total_bytes() : 0; + tt = arg_type_tuple(F, args, nargs); + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + } if (entry && entry->isleafsig && entry->simplesig == (void*)jl_nothing && entry->guardsigs == jl_emptysvec) { // put the entry into the cache if it's valid for a leafsig lookup, // using pick_which to slightly randomize where it ends up call_cache[cache_idx[++pick_which[cache_idx[0]] & 3]] = entry; - goto have_entry; } } @@ -2354,13 +2422,12 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t mfunc = entry->func.linfo; } else { - int64_t last_alloc = jl_options.malloc_log ? jl_gc_diff_total_bytes() : 0; + JL_GC_PUSH1(&tt); + assert(tt); JL_LOCK(&mt->writelock); // cache miss case JL_TIMING(METHOD_LOOKUP_SLOW); - jl_tupletype_t *tt = arg_type_tuple(F, args, nargs); - JL_GC_PUSH1(&tt); - mfunc = jl_mt_assoc_by_type(mt, tt, /*cache*/1, world); + mfunc = jl_mt_assoc_by_type(mt, tt, world); JL_GC_POP(); JL_UNLOCK(&mt->writelock); if (jl_options.malloc_log) @@ -2767,10 +2834,14 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio // // Returns a match as an array of svec(argtypes, static_params, Method). // See below for the meaning of lim. -static jl_value_t *ml_matches(jl_typemap_t *defs, int offs, +static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, jl_tupletype_t *type, int lim, int include_ambiguous, - size_t world, size_t *min_valid, size_t *max_valid) + size_t world, size_t *min_valid, size_t *max_valid, + int cache_result) { + jl_typemap_t *defs = mt->defs; + if (defs == jl_nothing) // special-case: ignore builtin functions + return jl_an_empty_vec_any; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)type); assert(jl_is_datatype(unw)); size_t l = jl_svec_len(((jl_datatype_t*)unw)->parameters); @@ -2792,10 +2863,54 @@ static jl_value_t *ml_matches(jl_typemap_t *defs, int offs, env.world = world; env.min_valid = *min_valid; env.max_valid = *max_valid; - struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, env.min_valid, env.max_valid}; + struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; JL_GC_PUSH5(&env.t, &env.matc, &env.match.env, &search.env, &env.match.ti); + + if (((jl_datatype_t*)unw)->isdispatchtuple) { + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)type, world); + if (entry) { + jl_method_instance_t *mi = entry->func.linfo; + jl_method_t *meth = mi->def.method; + if (jl_egal((jl_value_t*)type, mi->specTypes)) { + env.match.env = mi->sparam_vals; + env.match.ti = mi->specTypes; + } + else { + // TODO: should we use jl_subtype_env instead (since we know that `type <: meth->sig` by transitivity) + env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); + } + env.matc = jl_svec(3, env.match.ti, env.match.env, meth); + env.t = (jl_value_t*)jl_alloc_vec_any(1); + jl_array_ptr_set(env.t, 0, env.matc); + if (*min_valid < entry->min_world) + *min_valid = entry->min_world; + if (*max_valid > entry->max_world) + *max_valid = entry->max_world; + JL_GC_POP(); + return env.t; + } + } + if (((jl_datatype_t*)unw)->isdispatchtuple) { + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, &search, jl_cachearg_offset(mt), /*subtype*/1); + if (entry && (((jl_datatype_t*)unw)->isdispatchtuple || entry->guardsigs == jl_emptysvec)) { + jl_method_instance_t *mi = entry->func.linfo; + jl_method_t *meth = mi->def.method; + // TODO: should we use jl_subtype_env instead (since we know that `type <: meth->sig` by transitivity) + env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); + env.matc = jl_svec(3, env.match.ti, env.match.env, meth); + env.t = (jl_value_t*)jl_alloc_vec_any(1); + jl_array_ptr_set(env.t, 0, env.matc); + *min_valid = entry->min_world; + *max_valid = entry->max_world; + JL_GC_POP(); + return env.t; + } + } htable_new(&env.visited, 0); if (((jl_datatype_t*)unw)->isdispatchtuple) { + search.min_valid = env.min_valid; + search.max_valid = env.max_valid; jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(defs, &search, offs, /*subtype*/1); env.min_valid = search.min_valid; env.max_valid = search.max_valid; @@ -2810,6 +2925,14 @@ static jl_value_t *ml_matches(jl_typemap_t *defs, int offs, jl_typemap_intersection_visitor(defs, offs, &env.match); } htable_free(&env.visited); + if (cache_result && ((jl_datatype_t*)unw)->isdispatchtuple) { // cache_result parameter keeps this from being recursive + if (env.t != jl_false && jl_array_len(env.t) == 1) { + env.matc = (jl_svec_t*)jl_array_ptr_ref(env.t, 0); + jl_method_t *meth = (jl_method_t*)jl_svecref(env.matc, 2); + jl_svec_t *tpenv = (jl_svec_t*)jl_svecref(env.matc, 1); + cache_method(mt, &mt->cache, (jl_value_t*)mt, type, meth, world, tpenv); + } + } JL_GC_POP(); *min_valid = env.min_valid; *max_valid = env.max_valid; diff --git a/src/init.c b/src/init.c index 415c4b0316e8c..8345d288e31db 100644 --- a/src/init.c +++ b/src/init.c @@ -721,7 +721,6 @@ void _julia_init(JL_IMAGE_SEARCH rel) else { jl_init_types(); jl_init_codegen(); - jl_an_empty_vec_any = (jl_value_t*)jl_alloc_vec_any(0); // used internally } jl_init_tasks(); diff --git a/src/jltypes.c b/src/jltypes.c index bba9362e9a64f..0e9b911f7d22d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -822,17 +822,9 @@ static void cache_insert_type_linear(jl_datatype_t *type, ssize_t insert_at) #ifndef NDEBUG static int is_cacheable(jl_datatype_t *type) { - // only cache types whose behavior will not depend on the identities - // of contained TypeVars - assert(jl_is_datatype(type)); - jl_svec_t *t = type->parameters; - if (jl_svec_len(t) == 0 && jl_emptytuple_type != NULL) // Tuple{} is the only type eligible for this that doesn't have parameters - return 0; - // cache abstract types with no free type vars - if (jl_is_abstracttype(type)) - return !jl_has_free_typevars((jl_value_t*)type); - // ... or concrete types - return jl_is_concrete_type((jl_value_t*)type); + // ensure cache only contains types whose behavior will not depend on the + // identities of contained TypeVars + return !jl_has_free_typevars((jl_value_t*)type); } #endif @@ -1943,18 +1935,19 @@ void jl_init_types(void) JL_GC_DISABLED jl_methtable_type->name->mt = jl_nonfunction_mt; jl_methtable_type->super = jl_any_type; jl_methtable_type->parameters = jl_emptysvec; - jl_methtable_type->name->names = jl_perm_symsvec(11, "name", "defs", - "cache", "max_args", + jl_methtable_type->name->names = jl_perm_symsvec(12, "name", "defs", + "leafcache", "cache", "max_args", "kwsorter", "module", "backedges", "", "", "offs", ""); - jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, jl_any_type/*jl_long*/, + jl_methtable_type->types = jl_svec(12, jl_symbol_type, jl_any_type, jl_any_type, + jl_any_type, jl_any_type/*jl_long*/, jl_any_type, jl_any_type/*module*/, jl_any_type/*any vector*/, jl_any_type/*long*/, jl_any_type/*int32*/, jl_any_type/*uint8*/, jl_any_type/*uint8*/); jl_methtable_type->instance = NULL; jl_methtable_type->abstract = 0; jl_methtable_type->mutabl = 1; - jl_methtable_type->ninitialized = 4; + jl_methtable_type->ninitialized = 5; jl_precompute_memoized_dt(jl_methtable_type, 1); jl_symbol_type->name = jl_new_typename_in(jl_symbol("Symbol"), core); @@ -2152,6 +2145,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_array_symbol_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_symbol_type, jl_box_long(1)); jl_array_uint8_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_uint8_type, jl_box_long(1)); jl_array_int32_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_int32_type, jl_box_long(1)); + jl_an_empty_vec_any = (jl_value_t*)jl_alloc_vec_any(0); // used internally + jl_nonfunction_mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; + jl_type_type_mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; jl_expr_type = jl_new_datatype(jl_symbol("Expr"), core, @@ -2466,18 +2462,18 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 6, jl_long_type); jl_svecset(jl_typename_type->types, 3, jl_type_type); - jl_svecset(jl_methtable_type->types, 3, jl_long_type); - jl_svecset(jl_methtable_type->types, 5, jl_module_type); - jl_svecset(jl_methtable_type->types, 6, jl_array_any_type); + jl_svecset(jl_methtable_type->types, 4, jl_long_type); + jl_svecset(jl_methtable_type->types, 6, jl_module_type); + jl_svecset(jl_methtable_type->types, 7, jl_array_any_type); #ifdef __LP64__ - jl_svecset(jl_methtable_type->types, 7, jl_int64_type); // unsigned long - jl_svecset(jl_methtable_type->types, 8, jl_int64_type); // uint32_t plus alignment + jl_svecset(jl_methtable_type->types, 8, jl_int64_type); // unsigned long + jl_svecset(jl_methtable_type->types, 9, jl_int64_type); // uint32_t plus alignment #else - jl_svecset(jl_methtable_type->types, 7, jl_int32_type); // DWORD - jl_svecset(jl_methtable_type->types, 8, jl_int32_type); // uint32_t + jl_svecset(jl_methtable_type->types, 8, jl_int32_type); // DWORD + jl_svecset(jl_methtable_type->types, 9, jl_int32_type); // uint32_t #endif - jl_svecset(jl_methtable_type->types, 9, jl_uint8_type); jl_svecset(jl_methtable_type->types, 10, jl_uint8_type); + jl_svecset(jl_methtable_type->types, 11, jl_uint8_type); jl_svecset(jl_method_type->types, 13, jl_method_instance_type); jl_svecset(jl_method_instance_type->types, 5, jl_code_instance_type); jl_svecset(jl_code_instance_type->types, 8, jl_voidpointer_type); diff --git a/src/julia.h b/src/julia.h index a6b10f84287a3..1d61b2ca961eb 100644 --- a/src/julia.h +++ b/src/julia.h @@ -537,6 +537,7 @@ typedef struct _jl_methtable_t { JL_DATA_TYPE jl_sym_t *name; // sometimes a hack used by serialization to handle kwsorter jl_typemap_t *defs; + jl_array_t *leafcache; jl_typemap_t *cache; intptr_t max_args; // max # of non-vararg arguments in a signature jl_value_t *kwsorter; // keyword argument sorter function diff --git a/src/julia_internal.h b/src/julia_internal.h index 61123c4b4423d..1fee22750c418 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -462,6 +462,8 @@ jl_datatype_t *jl_new_uninitialized_datatype(void); void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable); jl_datatype_t *jl_wrap_Type(jl_value_t *t); // x -> Type{x} jl_value_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n); +jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type); +void jl_cache_type_(jl_datatype_t *type); void jl_assign_bits(void *dest, jl_value_t *bits) JL_NOTSAFEPOINT; void set_nth_field(jl_datatype_t *st, void *v, size_t i, jl_value_t *rhs) JL_NOTSAFEPOINT; jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); @@ -482,7 +484,7 @@ int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT; jl_value_t *jl_call_scm_on_ast(const char *funcname, jl_value_t *expr, jl_module_t *inmodule); void jl_linenumber_to_lineinfo(jl_code_info_t *ci, jl_value_t *name); -jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, int cache, size_t world); +jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, size_t world); jl_value_t *jl_gf_invoke(jl_value_t *types, jl_value_t *f, jl_value_t **args, size_t nargs); jl_method_instance_t *jl_lookup_generic(jl_value_t **args, uint32_t nargs, uint32_t callsite, size_t world) JL_ALWAYS_LEAFTYPE; JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int include_ambiguous, @@ -1048,13 +1050,12 @@ struct jl_typemap_info { jl_datatype_t **jl_contains; // the type that is being put in this }; -jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, - jl_value_t *parent JL_PROPAGATES_ROOT, - jl_tupletype_t *type, - jl_tupletype_t *simpletype, jl_svec_t *guardsigs, - jl_value_t *newvalue, int8_t offs, - const struct jl_typemap_info *tparams, - size_t min_world, size_t max_world); +void jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, + jl_typemap_entry_t *newrec, int8_t offs, + const struct jl_typemap_info *tparams); +jl_typemap_entry_t *jl_typemap_alloc( + jl_tupletype_t *type, jl_tupletype_t *simpletype, jl_svec_t *guardsigs, + jl_value_t *newvalue, size_t min_world, size_t max_world); struct jl_typemap_assoc { // inputs diff --git a/src/precompile.c b/src/precompile.c index 13ce3d92d7d1c..4aede27d7c1eb 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -314,12 +314,6 @@ static void jl_compile_all_defs(void) JL_GC_POP(); } -static int precompile_enq_all_cache__(jl_typemap_entry_t *l, void *closure) -{ - jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)l->func.linfo); - return 1; -} - static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closure) { assert(jl_is_method_instance(mi)); @@ -370,7 +364,6 @@ static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *c static void precompile_enq_all_specializations_(jl_methtable_t *mt, void *env) { jl_typemap_visitor(mt->defs, precompile_enq_all_specializations__, env); - jl_typemap_visitor(mt->cache, precompile_enq_all_cache__, env); } void jl_compile_now(jl_method_instance_t *mi); diff --git a/src/typemap.c b/src/typemap.c index 849ae6a8e2e02..8b0bf64305fdd 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -872,18 +872,34 @@ static void jl_typemap_level_insert_( jl_typemap_list_insert_(map, &cache->linear, (jl_value_t*)cache, newrec, tparams); } -jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, - jl_tupletype_t *type, - jl_tupletype_t *simpletype, jl_svec_t *guardsigs, - jl_value_t *newvalue, int8_t offs, - const struct jl_typemap_info *tparams, - size_t min_world, size_t max_world) +jl_typemap_entry_t *jl_typemap_alloc( + jl_tupletype_t *type, jl_tupletype_t *simpletype, jl_svec_t *guardsigs, + jl_value_t *newvalue, size_t min_world, size_t max_world) { jl_ptls_t ptls = jl_get_ptls_states(); assert(min_world > 0 && max_world > 0); if (!simpletype) simpletype = (jl_tupletype_t*)jl_nothing; jl_value_t *ttype = jl_unwrap_unionall((jl_value_t*)type); + assert(jl_is_tuple_type(ttype)); + // compute the complexity of this type signature + int isva = jl_is_va_tuple((jl_datatype_t*)ttype); + int issimplesig = !jl_is_unionall(type); // a TypeVar environment needs a complex matching test + int isleafsig = issimplesig && !isva; // entirely leaf types don't need to be sorted + size_t i, l; + for (i = 0, l = jl_nparams(ttype); i < l && issimplesig; i++) { + jl_value_t *decl = jl_tparam(ttype, i); + if (jl_is_kind(decl)) + isleafsig = 0; // Type{} may have a higher priority than a kind + else if (jl_is_type_type(decl)) + isleafsig = 0; // Type{} may need special processing to compute the match + else if (jl_is_vararg_type(decl)) + isleafsig = 0; // makes iteration easier when the endpoints are the same + else if (decl == (jl_value_t*)jl_any_type) + isleafsig = 0; // Any needs to go in the general cache + else if (!jl_is_concrete_type(decl)) // anything else needs to go through the general subtyping test + isleafsig = issimplesig = 0; + } jl_typemap_entry_t *newrec = (jl_typemap_entry_t*)jl_gc_alloc(ptls, sizeof(jl_typemap_entry_t), @@ -895,32 +911,19 @@ jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, newrec->next = (jl_typemap_entry_t*)jl_nothing; newrec->min_world = min_world; newrec->max_world = max_world; - // compute the complexity of this type signature - newrec->va = jl_is_va_tuple((jl_datatype_t*)ttype); - newrec->issimplesig = !jl_is_unionall(type); // a TypeVar environment needs a complex matching test - newrec->isleafsig = newrec->issimplesig && !newrec->va; // entirely leaf types don't need to be sorted - JL_GC_PUSH1(&newrec); - assert(jl_is_tuple_type(ttype)); - size_t i, l; - for (i = 0, l = jl_nparams(ttype); i < l && newrec->issimplesig; i++) { - jl_value_t *decl = jl_tparam(ttype, i); - if (jl_is_kind(decl)) - newrec->isleafsig = 0; // Type{} may have a higher priority than a kind - else if (jl_is_type_type(decl)) - newrec->isleafsig = 0; // Type{} may need special processing to compute the match - else if (jl_is_vararg_type(decl)) - newrec->isleafsig = 0; // makes iteration easier when the endpoints are the same - else if (decl == (jl_value_t*)jl_any_type) - newrec->isleafsig = 0; // Any needs to go in the general cache - else if (!jl_is_concrete_type(decl)) // anything else needs to go through the general subtyping test - newrec->isleafsig = newrec->issimplesig = 0; - } - // TODO: assert that guardsigs == jl_emptysvec && simplesig == jl_nothing if isleafsig and optimize with that knowledge? - jl_typemap_insert_generic(*cache, cache, parent, newrec, offs, tparams); - JL_GC_POP(); + newrec->va = isva; + newrec->issimplesig = issimplesig; + newrec->isleafsig = isleafsig; return newrec; } +void jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, + jl_typemap_entry_t *newrec, int8_t offs, + const struct jl_typemap_info *tparams) +{ + jl_typemap_insert_generic(*cache, cache, parent, newrec, offs, tparams); +} + static void jl_typemap_list_insert_sorted( jl_typemap_t *map, jl_typemap_entry_t **pml, jl_value_t *parent, jl_typemap_entry_t *newrec, const struct jl_typemap_info *tparams) From 6c54523cbcc5d9b9b729e62828778cff1deb01d2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 01:03:41 -0400 Subject: [PATCH 121/232] avoid recomputing ml-matches worlds during method-caching [NFCI] --- src/gf.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/gf.c b/src/gf.c index 08de860090bed..38a91c281fdbd 100644 --- a/src/gf.c +++ b/src/gf.c @@ -953,7 +953,7 @@ static jl_method_instance_t *cache_method( jl_methtable_t *mt, jl_typemap_t **cache, jl_value_t *parent JL_PROPAGATES_ROOT, jl_tupletype_t *tt, // the original tupletype of the signature jl_method_t *definition, - size_t world, + size_t world, size_t min_valid, size_t max_valid, jl_svec_t *sparams) { // caller must hold the mt->writelock @@ -996,12 +996,12 @@ static jl_method_instance_t *cache_method( jl_tupletype_t *cachett = tt; jl_svec_t* guardsigs = jl_emptysvec; - size_t min_valid = 1; - size_t max_valid = ~(size_t)0; if (!cache_with_orig && mt) { // now examine what will happen if we chose to use this sig in the cache // TODO: should we first check `compilationsig <: definition`? - temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid, &max_valid, 0); + size_t min_valid2 = 1; + size_t max_valid2 = ~(size_t)0; + temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid2, &max_valid2, 0); int guards = 0; if (temp == jl_false) { cache_with_orig = 1; @@ -1052,22 +1052,14 @@ static jl_method_instance_t *cache_method( } } } - if (cache_with_orig) { - min_valid = 1; - max_valid = ~(size_t)0; - } - else { + if (!cache_with_orig) { // determined above that there's no ambiguity in also using compilationsig as the cacheablesig + min_valid = min_valid2; + max_valid = max_valid2; cachett = compilationsig; } } - if (cache_with_orig && mt) { - // now examine defs to determine the min/max-valid range for this lookup result - (void)ml_matches(mt, 0, cachett, -1, 0, world, &min_valid, &max_valid, 0); - } - assert(mt == NULL || min_valid > 1); - // now scan `cachett` and ensure that `Type{T}` in the cache will be matched exactly by `typeof(T)` // and also reduce the complexity of rejecting this entry in the cache // by replacing non-simple types with jl_any_type to build a new `type` @@ -1229,7 +1221,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype entry = jl_typemap_morespecific_by_type(entry, (jl_value_t*)tt, &search.env, world); if (entry != NULL) { jl_method_t *m = entry->func.method; - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, search.env); + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, search.min_valid, search.max_valid, search.env); } } JL_GC_POP(); @@ -2088,7 +2080,13 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES return NULL; // find if exactly 1 method matches (issue #7302) - jl_value_t *matches = jl_matching_methods(types, 1, 1, world, min_valid, max_valid); + size_t min_valid2 = 1; + size_t max_valid2 = ~(size_t)0; + jl_value_t *matches = jl_matching_methods(types, 1, 1, world, &min_valid2, &max_valid2); + if (*min_valid < min_valid2) + *min_valid = min_valid2; + if (*max_valid > max_valid2) + *max_valid = max_valid2; if (matches == jl_false || jl_array_len(matches) != 1) return NULL; jl_tupletype_t *tt = NULL; @@ -2109,7 +2107,7 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES // inject it there now if we think it will be // used via dispatch later (e.g. because it was hinted via a call to `precompile`) JL_LOCK(&mt->writelock); - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, ti, m, world, env); + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, ti, m, world, min_valid2, max_valid2, env); JL_UNLOCK(&mt->writelock); } else { @@ -2538,7 +2536,7 @@ static jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, j if (method->invokes == NULL) method->invokes = jl_nothing; - mfunc = cache_method(NULL, &method->invokes, (jl_value_t*)method, tt, method, 1, tpenv); + mfunc = cache_method(NULL, &method->invokes, (jl_value_t*)method, tt, method, 1, 1, ~(size_t)0, tpenv); JL_UNLOCK(&method->writelock); JL_GC_POP(); if (jl_options.malloc_log) @@ -2588,7 +2586,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_typemap_entry_t *entry, jl_valu method->invokes = jl_nothing; jl_method_instance_t *mfunc = cache_method(NULL, &method->invokes, (jl_value_t*)method, - (jl_tupletype_t*)tt, method, 1, tpenv); + (jl_tupletype_t*)tt, method, 1, 1, ~(size_t)0, tpenv); JL_GC_POP(); JL_UNLOCK(&method->writelock); return (jl_value_t*)mfunc; @@ -2930,7 +2928,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, env.matc = (jl_svec_t*)jl_array_ptr_ref(env.t, 0); jl_method_t *meth = (jl_method_t*)jl_svecref(env.matc, 2); jl_svec_t *tpenv = (jl_svec_t*)jl_svecref(env.matc, 1); - cache_method(mt, &mt->cache, (jl_value_t*)mt, type, meth, world, tpenv); + cache_method(mt, &mt->cache, (jl_value_t*)mt, type, meth, world, env.min_valid, env.max_valid, tpenv); } } JL_GC_POP(); From 87a416fed37f828e1ca4376cb6da541105f6d82c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 6 Jun 2020 03:04:05 -0400 Subject: [PATCH 122/232] fix recursion detection in constant prop (#36148) --- base/compiler/abstractinterpretation.jl | 22 ++++++++++++++++++++-- test/compiler/inference.jl | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index d15c56fb2dedb..a3793f2a6fbf3 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -132,7 +132,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # if there's a possibility we could constant-propagate a better result # (hopefully without doing too much work), try to do that now # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge - const_rettype = abstract_call_method_with_const_args(interp, rettype, f, argtypes, applicable[nonbot]::SimpleVector, sv) + const_rettype = abstract_call_method_with_const_args(interp, rettype, f, argtypes, applicable[nonbot]::SimpleVector, sv, edgecycle) if const_rettype ⊑ rettype # use the better result, if it's a refinement of rettype rettype = const_rettype @@ -185,7 +185,7 @@ function const_prop_profitable(@nospecialize(arg)) return false end -function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nospecialize(rettype), @nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) +function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nospecialize(rettype), @nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState, edgecycle::Bool) method = match[3]::Method nargs::Int = method.nargs method.isva && (nargs -= 1) @@ -259,6 +259,24 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp inf_cache = get_inference_cache(interp) inf_result = cache_lookup(mi, argtypes, inf_cache) if inf_result === nothing + if edgecycle + # if there might be a cycle, check to make sure we don't end up + # calling ourselves here. + infstate = sv + cyclei = 0 + while !(infstate === nothing) + if method === infstate.linfo.def && any(infstate.result.overridden_by_const) + return Any + end + if cyclei < length(infstate.callers_in_cycle) + cyclei += 1 + infstate = infstate.callers_in_cycle[cyclei] + else + cyclei = 0 + infstate = infstate.parent + end + end + end inf_result = InferenceResult(mi, argtypes) frame = InferenceState(inf_result, #=cache=#false, interp) frame === nothing && return Any # this is probably a bad generated function (unsound), but just ignore it diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9fadf28bdff51..23767ae32fadc 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2623,3 +2623,27 @@ end _size_ish(F::NotQRSparse, i::Integer) = size(getprop(F, :B), 1) _call_size_ish(x) = _size_ish(x,1) @test Base.return_types(_call_size_ish, (NotQRSparse,)) == Any[Int] + +module TestConstPropRecursion +mutable struct Node + data + child::Node + sibling::Node +end + +function Base.iterate(n::Node, state::Node = n.child) + n === state && return nothing + return state, state === state.sibling ? n : state.sibling +end + +@inline function depth(node::Node, d) + childd = d + 1 + for c in node + d = max(d, depth(c, childd)) + end + return d +end + +f(n) = depth(n, 1) +end +@test Base.return_types(TestConstPropRecursion.f, (TestConstPropRecursion.Node,)) == Any[Int] From cbd854b0f4af800fac543676c4b3f938ebc48e62 Mon Sep 17 00:00:00 2001 From: Sakse <17059936+dalum@users.noreply.github.com> Date: Sat, 6 Jun 2020 15:20:38 +0200 Subject: [PATCH 123/232] Add range indexing to UniformScaling (#24359) * Add range indexing to UniformScaling * Add tests * Return sparse matrix when indexing UniformScaling * start -> first * Correctly handle non-integer steps * Make getindex(::UniformScaling, ranges...) return a dense array * Remove unneccessary (and slower) branch * Add (range, integer) indexing to UniformScaling * Update tests * Fix unbound param * Add NEWS item * Update docstring and add compat annotation Co-authored-by: Evey Dee --- NEWS.md | 1 + stdlib/LinearAlgebra/src/uniformscaling.jl | 36 +++++++++++++++++++-- stdlib/LinearAlgebra/test/uniformscaling.jl | 26 +++++++++++++-- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9966811f8e1c7..71a2d66d7946c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,6 +50,7 @@ Standard library changes #### LinearAlgebra * New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]). +* `UniformScaling` can now be indexed into using ranges to return dense matrices and vectors ([#24359]). #### Markdown diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index ed007fc48fa77..214f06c6af2e1 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -6,8 +6,13 @@ import Base: copy, adjoint, getindex, show, transpose, one, zero, inv, """ UniformScaling{T<:Number} -Generically sized uniform scaling operator defined as a scalar times the -identity operator, `λ*I`. See also [`I`](@ref). +Generically sized uniform scaling operator defined as a scalar times +the identity operator, `λ*I`. Although without an explicit `size`, it +acts similarly to a matrix in many cases and includes support for some +indexing. See also [`I`](@ref). + +!!! compat "Julia 1.6" + Indexing using ranges is available as of Julia 1.6. # Examples ```jldoctest @@ -24,6 +29,11 @@ julia> J*A 2×2 Array{Float64,2}: 2.0 4.0 6.0 8.0 + +julia> J[1:2, 1:2] +2×2 Array{Float64,2}: + 2.0 0.0 + 0.0 2.0 ``` """ struct UniformScaling{T<:Number} @@ -78,6 +88,28 @@ ndims(J::UniformScaling) = 2 Base.has_offset_axes(::UniformScaling) = false getindex(J::UniformScaling, i::Integer,j::Integer) = ifelse(i==j,J.λ,zero(J.λ)) +getindex(x::UniformScaling, n::Integer, m::AbstractRange{<:Integer}) = getindex(x, m, n) +function getindex(x::UniformScaling{T}, n::AbstractRange{<:Integer}, m::Integer) where T + v = zeros(T, length(n)) + @inbounds for (i,ii) in enumerate(n) + if ii == m + v[i] = x.λ + end + end + return v +end + + +function getindex(x::UniformScaling{T}, n::AbstractRange{<:Integer}, m::AbstractRange{<:Integer}) where T + A = zeros(T, length(n), length(m)) + @inbounds for (j,jj) in enumerate(m), (i,ii) in enumerate(n) + if ii == jj + A[i,j] = x.λ + end + end + return A +end + function show(io::IO, ::MIME"text/plain", J::UniformScaling) s = "$(J.λ)" if occursin(r"\w+\s*[\+\-]\s*\w+", s) diff --git a/stdlib/LinearAlgebra/test/uniformscaling.jl b/stdlib/LinearAlgebra/test/uniformscaling.jl index 1564198443f24..320ca993f438c 100644 --- a/stdlib/LinearAlgebra/test/uniformscaling.jl +++ b/stdlib/LinearAlgebra/test/uniformscaling.jl @@ -11,8 +11,6 @@ using .Main.Quaternions Random.seed!(123) @testset "basic functions" begin - @test I[1,1] == 1 # getindex - @test I[1,2] == 0 # getindex @test I === I' # transpose @test ndims(I) == 2 @test one(UniformScaling{Float32}) == UniformScaling(one(Float32)) @@ -27,6 +25,30 @@ Random.seed!(123) @test opnorm(UniformScaling(1+im)) ≈ sqrt(2) end +@testset "getindex" begin + @test I[1,1] == 1 + @test I[1,2] == 0 + + J = I(15) + for (a, b) in [ + # indexing that returns a Vector + (1:10, 1), + (4, 1:10), + (11, 1:10), + # indexing that returns a Matrix + (1:2, 1:2), + (1:2:3, 1:2:3), + (1:2:8, 2:2:9), + (1:2:8, 9:-4:1), + (9:-4:1, 1:2:8), + (2:3, 1:2), + (2:-1:1, 1:2), + (1:2:9, 5:2:13), + ] + @test I[a,b] == J[a,b] + end +end + @testset "sqrt, exp, log, and trigonometric functions" begin # convert to a dense matrix with random size M(J) = (N = rand(1:10); Matrix(J, N, N)) From 98e678fd9806856a4c0a12f711e1ee397994deeb Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 6 Jun 2020 20:34:18 +0200 Subject: [PATCH 124/232] simplify generated code for iterate on codeunits (#36159) * simplify generated code for iterate on codeunits * Update basic.jl --- base/strings/basic.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 6d683e8740583..37c4a0bd2a433 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -734,7 +734,8 @@ size(s::CodeUnits) = (length(s),) elsize(s::CodeUnits{T}) where {T} = sizeof(T) @propagate_inbounds getindex(s::CodeUnits, i::Int) = codeunit(s.s, i) IndexStyle(::Type{<:CodeUnits}) = IndexLinear() -iterate(s::CodeUnits, i=1) = (@_propagate_inbounds_meta; i == length(s)+1 ? nothing : (s[i], i+1)) +@inline iterate(s::CodeUnits, i=1) = (i % UInt) - 1 < length(s) ? (@inbounds s[i], i + 1) : nothing + write(io::IO, s::CodeUnits) = write(io, s.s) From 9d4565b3aa7e9460654e8bcfe68183d6875804de Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 7 Jun 2020 22:26:15 -0400 Subject: [PATCH 125/232] a few compiler improvements (#36176) - improve fieldtype_nothrow for vararg tuple types - remove UnionAll construction code from tuple_type_head - add specializations to avoid some common generated functions --- base/compiler/tfuncs.jl | 8 ++++---- base/essentials.jl | 2 +- base/multidimensional.jl | 32 ++++++++++++++++++++++++++++---- base/namedtuple.jl | 6 +++++- test/compiler/inference.jl | 3 +++ 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index d67f6f7ccc435..fb4d8549295af 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -876,12 +876,12 @@ function _fieldtype_nothrow(@nospecialize(s), exact::Bool, name::Const) isa(fld, Int) || return false ftypes = datatype_fieldtypes(u) nf = length(ftypes) - (fld >= 1 && fld <= nf) || return false if u.name === Tuple.name && fld >= nf && isvarargtype(ftypes[nf]) - # The length of the tuple will be determined at runtime, we can't say - # anything - return false + # If we don't know the exact type, the length of the tuple will be determined + # at runtime and we can't say anything. + return exact end + (fld >= 1 && fld <= nf) || return false return true end diff --git a/base/essentials.jl b/base/essentials.jl index e5870bdb9b893..6a74e99c32a24 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -205,7 +205,7 @@ ERROR: ArgumentError: Cannot call tail on an empty tuple. tail(x::Tuple) = argtail(x...) tail(::Tuple{}) = throw(ArgumentError("Cannot call tail on an empty tuple.")) -tuple_type_head(T::Type) = (@_pure_meta; fieldtype(T::Type{<:Tuple}, 1)) +tuple_type_head(T::Type) = (@_pure_meta; fieldtype(T, 1)) function tuple_type_tail(T::Type) @_pure_meta diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 03b1d151f23b4..51de7184cb92f 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -758,8 +758,7 @@ function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Vararg{Union{Real, return dest end -# Always index with the exactly indices provided. -@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N +function _generate_unsafe_getindex!_body(N::Int) quote @_inline_meta D = eachindex(dest) @@ -776,6 +775,20 @@ end end end +# Always index with the exactly indices provided. +@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N + _generate_unsafe_getindex!_body(N) +end + +# manually written-out specializations for 1 and 2 arguments to save compile time +@eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},1}) + $(_generate_unsafe_getindex!_body(1)) +end + +@eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},2}) + $(_generate_unsafe_getindex!_body(2)) +end + @noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) ## setindex! ## @@ -786,8 +799,7 @@ function _setindex!(l::IndexStyle, A::AbstractArray, x, I::Union{Real, AbstractA A end -@generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Union{Real,AbstractArray}...) - N = length(I) +function _generate_unsafe_setindex!_body(N::Int) quote x′ = unalias(A, x) @nexprs $N d->(I_d = unalias(A, I[d])) @@ -806,6 +818,18 @@ end end end +@generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray}, N}) where N + _generate_unsafe_setindex!_body(N) +end + +@eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},1}) + $(_generate_unsafe_setindex!_body(1)) +end + +@eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},2}) + $(_generate_unsafe_setindex!_body(2)) +end + diff(a::AbstractVector) = diff(a, dims=1) """ diff --git a/base/namedtuple.jl b/base/namedtuple.jl index f315be36256c7..f341291a7f00e 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -248,7 +248,9 @@ function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} end end -merge(a::NamedTuple{()}, b::NamedTuple) = b +merge(a::NamedTuple, b::NamedTuple{()}) = a +merge(a::NamedTuple{()}, b::NamedTuple{()}) = a +merge(a::NamedTuple{()}, b::NamedTuple) = b merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, b.data) @@ -320,6 +322,8 @@ function structdiff(a::NamedTuple{an}, b::Union{NamedTuple{bn}, Type{NamedTuple{ end end +structdiff(a::NamedTuple{an}, b::Union{NamedTuple{an}, Type{NamedTuple{an}}}) where {an} = (;) + """ setindex(nt::NamedTuple, val, key::Symbol) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 23767ae32fadc..7092b8fc38e97 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -655,6 +655,9 @@ let fieldtype_tfunc = Core.Compiler.fieldtype_tfunc, @test fieldtype_nothrow(Union{Type{Base.RefValue{<:Real}}, Type{Base.RefValue{Any}}}, Const(:x)) @test fieldtype_nothrow(Const(Union{Base.RefValue{<:Real}, Base.RefValue{Any}}), Const(:x)) @test fieldtype_nothrow(Type{Union{Base.RefValue{T}, Base.RefValue{Any}}} where {T<:Real}, Const(:x)) + @test fieldtype_nothrow(Type{Tuple{Vararg{Int}}}, Const(1)) + @test fieldtype_nothrow(Type{Tuple{Vararg{Int}}}, Const(42)) + @test !fieldtype_nothrow(Type{<:Tuple{Vararg{Int}}}, Const(1)) end # issue #11480 From d31962cf63b9bf5b42e6b28137adf0b37bb21c5e Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Mon, 8 Jun 2020 05:59:53 +0200 Subject: [PATCH 126/232] Fix minimum and maximum in the presence of missing values (#35989) * Fix minimum and maximum in the presence of missing values * Fix comment --- base/reduce.jl | 10 +++++----- test/reduce.jl | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 6d19fa50e3037..6582e72274aba 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -576,11 +576,11 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)}, start = first + 1 simdstop = start + chunk_len - 4 while simdstop <= last - 3 - # short circuit in case of NaN - v1 == v1 || return v1 - v2 == v2 || return v2 - v3 == v3 || return v3 - v4 == v4 || return v4 + # short circuit in case of NaN or missing + (v1 == v1) === true || return v1 + (v2 == v2) === true || return v2 + (v3 == v3) === true || return v3 + (v4 == v4) === true || return v4 @inbounds for i in start:4:simdstop v1 = _fast(op, v1, f(A[i+0])) v2 = _fast(op, v2, f(A[i+1])) diff --git a/test/reduce.jl b/test/reduce.jl index 9501f41f8b77c..d640bcdde7440 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -341,6 +341,18 @@ A = circshift(reshape(1:24,2,3,4), (0,1,1)) @test size(extrema(A,dims=(1,2,3))) == size(maximum(A,dims=(1,2,3))) @test extrema(x->div(x, 2), A, dims=(2,3)) == reshape([(0,11),(1,12)],2,1,1) +@testset "maximum/minimum/extrema with missing values" begin + for x in (Vector{Union{Int,Missing}}(missing, 10), + Vector{Union{Int,Missing}}(missing, 257)) + @test maximum(x) === minimum(x) === missing + @test extrema(x) === (missing, missing) + fill!(x, 1) + x[1] = missing + @test maximum(x) === minimum(x) === missing + @test extrema(x) === (missing, missing) + end +end + # any & all @test @inferred any([]) == false From d6b33eace1201451442a17fc648d21c9f54111e8 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 8 Jun 2020 09:30:17 -0400 Subject: [PATCH 127/232] temp_cleanup_purge: default to force=false, make keyword argument (#36161) This is a potentially useful function for testing but `force=true` is only useful when the process is shutting down, so it should not be the default. --- base/file.jl | 6 +++--- base/initdefs.jl | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/base/file.jl b/base/file.jl index 1e860f30dc12f..1b21f72f5604b 100644 --- a/base/file.jl +++ b/base/file.jl @@ -470,18 +470,18 @@ function temp_cleanup_later(path::AbstractString; asap::Bool=false) # still be using the path, don't delete it until process exit TEMP_CLEANUP[path] = get(TEMP_CLEANUP, path, true) & asap if length(TEMP_CLEANUP) > TEMP_CLEANUP_MAX[] - temp_cleanup_purge(false) + temp_cleanup_purge() TEMP_CLEANUP_MAX[] = max(TEMP_CLEANUP_MIN[], 2*length(TEMP_CLEANUP)) end unlock(TEMP_CLEANUP_LOCK) return nothing end -function temp_cleanup_purge(all::Bool=true) +function temp_cleanup_purge(; force::Bool=false) need_gc = Sys.iswindows() for (path, asap) in TEMP_CLEANUP try - if (all || asap) && ispath(path) + if (force || asap) && ispath(path) need_gc && GC.gc(true) need_gc = false rm(path, recursive=true, force=true) diff --git a/base/initdefs.jl b/base/initdefs.jl index 85a3d57cd3875..5e18028332c59 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -293,7 +293,9 @@ end ## atexit: register exit hooks ## -const atexit_hooks = Callable[Filesystem.temp_cleanup_purge] +const atexit_hooks = Callable[ + () -> Filesystem.temp_cleanup_purge(force=true) +] """ atexit(f) From 14b8b0564a2f69f21133d69cb1c8ee3100daf0ce Mon Sep 17 00:00:00 2001 From: Andreas Scheidegger Date: Mon, 8 Jun 2020 16:33:30 +0200 Subject: [PATCH 128/232] doc: update `lock` example (#36192) The old example gave the wrong impression that `lock` takes a data structure and not a lock as argument. --- doc/src/manual/multi-threading.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index 6d57626c2c76d..c3ba2ef33e681 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -73,19 +73,20 @@ can be observed from multiple threads. For example, in most cases you should use the following code pattern: ```julia-repl -julia> lock(a) do +julia> lock(lk) do use(a) end julia> begin - lock(a) + lock(lk) try use(a) finally - unlock(a) + unlock(lk) end end ``` +where `lk` is a lock (e.g. `ReentrantLock()`) and `a` data. Additionally, Julia is not memory safe in the presence of a data race. Be very careful about reading a global variable (or closure variable) if another thread From 17cff0392d4615d75aace27866875243792cd5aa Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Mon, 8 Jun 2020 10:48:49 -0500 Subject: [PATCH 129/232] NFC: Remove outdated comment --- base/indices.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/indices.jl b/base/indices.jl index 2cc822bdeeb8f..37eec4ed6c817 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -373,8 +373,7 @@ struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end IdentityUnitRange(S::IdentityUnitRange) = S -# IdentityUnitRanges are offset and thus have offset axes, so they are their own axes... but -# we need to strip the wholedim marker because we don't know how they'll be used +# IdentityUnitRanges are offset and thus have offset axes, so they are their own axes axes(S::IdentityUnitRange) = (S,) unsafe_indices(S::IdentityUnitRange) = (S,) axes1(S::IdentityUnitRange) = S From 07385aba138278904bf1bebb99611ce02e5c491c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 8 Jun 2020 18:02:04 +0200 Subject: [PATCH 130/232] Define convert for UniformScaling (#36193) --- stdlib/LinearAlgebra/src/uniformscaling.jl | 2 ++ stdlib/LinearAlgebra/test/uniformscaling.jl | 1 + 2 files changed, 3 insertions(+) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 214f06c6af2e1..6a49fff137c88 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -119,6 +119,8 @@ function show(io::IO, ::MIME"text/plain", J::UniformScaling) end copy(J::UniformScaling) = UniformScaling(J.λ) +Base.convert(::Type{UniformScaling{T}}, J::UniformScaling) where {T} = UniformScaling(convert(T, J.λ)) + conj(J::UniformScaling) = UniformScaling(conj(J.λ)) real(J::UniformScaling) = UniformScaling(real(J.λ)) imag(J::UniformScaling) = UniformScaling(imag(J.λ)) diff --git a/stdlib/LinearAlgebra/test/uniformscaling.jl b/stdlib/LinearAlgebra/test/uniformscaling.jl index 320ca993f438c..5e4aa22620225 100644 --- a/stdlib/LinearAlgebra/test/uniformscaling.jl +++ b/stdlib/LinearAlgebra/test/uniformscaling.jl @@ -23,6 +23,7 @@ Random.seed!(123) @test sparse(3I,4,5) == sparse(1:4, 1:4, 3, 4, 5) @test sparse(3I,5,4) == sparse(1:4, 1:4, 3, 5, 4) @test opnorm(UniformScaling(1+im)) ≈ sqrt(2) + @test convert(UniformScaling{Float64}, 2I) === 2.0I end @testset "getindex" begin From 0e062e9a239bf1bb10135030afdc388ef25baf8d Mon Sep 17 00:00:00 2001 From: Shan Sikdar Date: Mon, 8 Jun 2020 16:14:42 -0400 Subject: [PATCH 131/232] Fix Broadcasting of Bidiagonal (#35281) --- .../LinearAlgebra/src/structuredbroadcast.jl | 20 ++++++--- .../LinearAlgebra/test/structuredbroadcast.jl | 45 +++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index 3b5ba2475aeb1..a665e21731752 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -60,13 +60,21 @@ structured_broadcast_alloc(bc, ::Type{<:Diagonal}, ::Type{ElType}, n) where {ElT # Bidiagonal is tricky as we need to know if it's upper or lower. The promotion # system will return Tridiagonal when there's more than one Bidiagonal, but when # there's only one, we need to make figure out upper or lower -find_bidiagonal() = throw(ArgumentError("could not find Bidiagonal within broadcast expression")) -find_bidiagonal(a::Bidiagonal, rest...) = a -find_bidiagonal(bc::Broadcast.Broadcasted, rest...) = find_bidiagonal(find_bidiagonal(bc.args...), rest...) -find_bidiagonal(x, rest...) = find_bidiagonal(rest...) +merge_uplos(::Nothing, ::Nothing) = nothing +merge_uplos(a, ::Nothing) = a +merge_uplos(::Nothing, b) = b +merge_uplos(a, b) = a == b ? a : 'T' + +find_uplo(a::Bidiagonal) = a.uplo +find_uplo(a) = nothing +find_uplo(bc::Broadcasted) = mapreduce(find_uplo, merge_uplos, bc.args, init=nothing) + function structured_broadcast_alloc(bc, ::Type{<:Bidiagonal}, ::Type{ElType}, n) where {ElType} - ex = find_bidiagonal(bc) - return Bidiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1), ex.uplo) + uplo = find_uplo(bc) + if uplo == 'T' + return Tridiagonal(Array{ElType}(undef, n-1), Array{ElType}(undef, n), Array{ElType}(undef, n-1)) + end + return Bidiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1), uplo) end structured_broadcast_alloc(bc, ::Type{<:SymTridiagonal}, ::Type{ElType}, n) where {ElType} = SymTridiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1)) diff --git a/stdlib/LinearAlgebra/test/structuredbroadcast.jl b/stdlib/LinearAlgebra/test/structuredbroadcast.jl index 72d304ac6da3b..b8f5e97311588 100644 --- a/stdlib/LinearAlgebra/test/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/test/structuredbroadcast.jl @@ -160,4 +160,49 @@ end @test L .+ UnitL .+ UnitU .+ U .+ D == L + UnitL + UnitU + U + D @test L .+ U .+ D .+ D .+ D .+ D == L + U + D + D + D + D end +@testset "Broadcast Returned Types" begin + # Issue 35245 + N = 3 + dV = rand(N) + evu = rand(N-1) + evl = rand(N-1) + + Bu = Bidiagonal(dV, evu, :U) + Bl = Bidiagonal(dV, evl, :L) + T = Tridiagonal(evl, dV * 2, evu) + + @test typeof(Bu .+ Bl) <: Tridiagonal + @test typeof(Bl .+ Bu) <: Tridiagonal + @test typeof(Bu .+ Bu) <: Bidiagonal + @test typeof(Bl .+ Bl) <: Bidiagonal + @test Bu .+ Bl == T + @test Bl .+ Bu == T + @test Bu .+ Bu == Bidiagonal(dV * 2, evu * 2, :U) + @test Bl .+ Bl == Bidiagonal(dV * 2, evl * 2, :L) + + + @test typeof(Bu .* Bl) <: Tridiagonal + @test typeof(Bl .* Bu) <: Tridiagonal + @test typeof(Bu .* Bu) <: Bidiagonal + @test typeof(Bl .* Bl) <: Bidiagonal + + @test Bu .* Bl == Tridiagonal(zeros(N-1), dV .* dV, zeros(N-1)) + @test Bl .* Bu == Tridiagonal(zeros(N-1), dV .* dV, zeros(N-1)) + @test Bu .* Bu == Bidiagonal(dV .* dV, evu .* evu, :U) + @test Bl .* Bl == Bidiagonal(dV .* dV, evl .* evl, :L) + + Bu2 = Bu .* 2 + @test typeof(Bu2) <: Bidiagonal && Bu2.uplo == 'U' + Bu2 = 2 .* Bu + @test typeof(Bu2) <: Bidiagonal && Bu2.uplo == 'U' + Bl2 = Bl .* 2 + @test typeof(Bl2) <: Bidiagonal && Bl2.uplo == 'L' + Bu2 = 2 .* Bl + @test typeof(Bl2) <: Bidiagonal && Bl2.uplo == 'L' + + # Example of Nested Brodacasts + tmp = (1 .* 2) .* (Bidiagonal(1:3, 1:2, 'U') .* (3 .* 4)) .* (5 .* Bidiagonal(1:3, 1:2, 'L')) + @test typeof(tmp) <: Tridiagonal + +end end From cb41bbbce7938bd716d24fc82094c6eb87beb26c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 8 Jun 2020 18:16:15 -0400 Subject: [PATCH 132/232] in inference, disallow 3-element Unions if one elt is abstract (#36200) --- base/compiler/typelimits.jl | 5 ++++- test/compiler/inference.jl | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 2c8d7a5805573..88948421f1c01 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -268,8 +268,11 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe return true end +union_count_abstract(x::Union) = union_count_abstract(x.a) + union_count_abstract(x.b) +union_count_abstract(@nospecialize(x)) = !isdispatchelem(x) + function issimpleenoughtype(@nospecialize t) - return unionlen(t) <= MAX_TYPEUNION_LENGTH && unioncomplexity(t) <= MAX_TYPEUNION_COMPLEXITY + return unionlen(t)+union_count_abstract(t) <= MAX_TYPEUNION_LENGTH && unioncomplexity(t) <= MAX_TYPEUNION_COMPLEXITY end # pick a wider type that contains both typea and typeb, diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 7092b8fc38e97..f1ddc7349c843 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -91,10 +91,10 @@ tmerge_test(Tuple{ComplexF64, ComplexF64, ComplexF32}, Tuple{Vararg{Union{Comple Tuple{Vararg{Complex}}, false) tmerge_test(Tuple{}, Tuple{Complex, Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Complex}}) -@test Core.Compiler.tmerge(Tuple{}, Union{Int16, Nothing, Tuple{ComplexF32, ComplexF32}}) == - Union{Int16, Nothing, Tuple{Vararg{ComplexF32}}} -@test Core.Compiler.tmerge(Union{Int32, Nothing, Tuple{ComplexF32}}, Union{Int32, Nothing, Tuple{ComplexF32, ComplexF32}}) == - Union{Int32, Nothing, Tuple{Vararg{ComplexF32}}} +@test Core.Compiler.tmerge(Tuple{}, Union{Nothing, Tuple{ComplexF32, ComplexF32}}) == + Union{Nothing, Tuple{Vararg{ComplexF32}}} +@test Core.Compiler.tmerge(Union{Nothing, Tuple{ComplexF32}}, Union{Nothing, Tuple{ComplexF32, ComplexF32}}) == + Union{Nothing, Tuple{Vararg{ComplexF32}}} # issue 9770 @noinline x9770() = false From d57c6486b2245d7e7c7a99ea6aefdea5e215c328 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 8 Jun 2020 18:19:01 -0400 Subject: [PATCH 133/232] fix #36196, failure to give `expected "end"` error in parser (#36198) --- src/julia-parser.scm | 44 ++++++++++++++++++++++---------------------- test/syntax.jl | 4 ++++ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 32a1010d8984f..ba03f1c880917 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -662,16 +662,13 @@ (first? #t) (t (peek-token s))) (if (not (memv t ops)) - (begin - (if (not (or (eof-object? t) (eqv? t #\newline) (closer? t))) - (error (string "extra token \"" t "\" after end of expression"))) - (if (or (null? ex) (pair? (cdr ex)) (not first?)) - ;; () => (head) - ;; (ex2 ex1) => (head ex1 ex2) - ;; (ex1) if operator appeared => (head ex1) (handles "x;") - (cons head (reverse! ex)) - ;; (ex1) => ex1 - (car ex))) + (if (or (null? ex) (pair? (cdr ex)) (not first?)) + ;; () => (head) + ;; (ex2 ex1) => (head ex1 ex2) + ;; (ex1) if operator appeared => (head ex1) (handles "x;") + (cons head (reverse! ex)) + ;; (ex1) => ex1 + (car ex)) (begin (take-token s) ;; allow input to end with the operator, as in a;b; (if (or (eof-object? (peek-token s)) @@ -1270,15 +1267,18 @@ (define (expect-end s word) (let ((t (peek-token s))) - (cond ((eq? t 'end) (take-token s)) - ((eof-object? t) - (error (string "incomplete: \"" word "\" at " ; NOTE: changing this may affect code in base/client.jl - current-filename ":" expect-end-current-line - " requires end"))) - (else - (error (string "\"" word "\" at " - current-filename ":" expect-end-current-line - " expected \"end\", got \"" t "\"")))))) + (if (eq? t 'end) + (take-token s) + (expect-end-error t word)))) + +(define (expect-end-error t word) + (if (eof-object? t) + (error (string "incomplete: \"" word "\" at " ; NOTE: changing this may affect code in base/client.jl + current-filename ":" expect-end-current-line + " requires end")) + (error (string "\"" word "\" at " + current-filename ":" expect-end-current-line + " expected \"end\", got \"" t "\"")))) (define (parse-subtype-spec s) (parse-comparison s)) @@ -1469,10 +1469,10 @@ (expect-end (take-lineendings s) "primitive type")))))) ((try) - (let ((try-block (if (memq (require-token s) '(catch finally)) + (let ((try-block (if (memq (peek-token s) '(catch finally)) '(block) (parse-block s)))) - (let loop ((nxt (require-token s)) + (let loop ((nxt (peek-token s)) (catchb #f) (catchv #f) (finalb #f)) @@ -1519,7 +1519,7 @@ catchb catchv fb))) - (else (error (string "unexpected \"" nxt "\""))))))) + (else (expect-end-error nxt 'try)))))) ((return) (let ((t (peek-token s))) (if (or (eqv? t #\newline) (closing-token? t)) (list 'return '(null)) diff --git a/test/syntax.jl b/test/syntax.jl index 9e23d97fdd2ad..0c907a1d2deb2 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2264,3 +2264,7 @@ x = let var"'"(x) = 2x 3' end @test x == 6 + +# issue #36196 +@test_throws ParseError("\"for\" at none:1 expected \"end\", got \")\"") Meta.parse("(for i=1; println())") +@test_throws ParseError("\"try\" at none:1 expected \"end\", got \")\"") Meta.parse("(try i=1; println())") From a99a87ca59e4dc5b70b9c10af33511be84aaa37e Mon Sep 17 00:00:00 2001 From: Eric Davies Date: Mon, 8 Jun 2020 17:19:24 -0500 Subject: [PATCH 134/232] Add parse method for UUID (#36199) * Add parse method for UUID * Add test for UUID parse function * [skip ci] Add NEWS for UUID parse function --- NEWS.md | 1 + base/uuid.jl | 2 ++ test/loading.jl | 2 ++ 3 files changed, 5 insertions(+) diff --git a/NEWS.md b/NEWS.md index 71a2d66d7946c..61e364f1c63f9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -79,6 +79,7 @@ Standard library changes #### UUIDs * Change `uuid1` and `uuid4` to use `Random.RandomDevice()` as default random number generator ([#35872]). +* Added `parse(::Type{UUID}, ::AbstractString)` method Deprecated or removed --------------------- diff --git a/base/uuid.jl b/base/uuid.jl index 1e64e2e4ae00c..34f8c7f17e232 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -69,6 +69,8 @@ function UUID(s::AbstractString) end end +parse(::Type{UUID}, s::AbstractString) = UUID(s) + let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] global string diff --git a/test/loading.jl b/test/loading.jl index 4b42019da9c63..e87cc998ddc78 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -117,6 +117,8 @@ let uuidstr = "ab"^4 * "-" * "ab"^2 * "-" * "ab"^2 * "-" * "ab"^2 * "-" * "ab"^6 uuid2 = UUID(uuidstr2) uuids = [uuid, uuid2] @test (uuids .== uuid) == [true, false] + + @test parse(UUID, uuidstr2) == uuid2 end @test_throws ArgumentError UUID("@"^4 * "-" * "@"^2 * "-" * "@"^2 * "-" * "@"^2 * "-" * "@"^6) From e6eafcec17261cf650f934a541a36ae17143e00e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 8 Jun 2020 17:46:05 -0500 Subject: [PATCH 135/232] Revise-test: make sure all workers load Revise (#36124) This fixes the errors ``` julia> Base.runtests(["REPL", "cartesian"]; revise=true) ERROR: LoadError: On worker 2: UndefVarError: Revise not defined ``` and then later ``` julia> Base.runtests(["REPL", "cartesian"]; revise=true) ERROR: LoadError: On worker 2: UndefVarError: STDLIBS not defined ``` --- test/runtests.jl | 24 ++++++++++++++---------- test/testdefs.jl | 12 ++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 68a6e526194a7..74ff907deb4bc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,6 +15,12 @@ tests = unique(tests) if use_revise using Revise + # Remote-eval the following to initialize Revise in workers + const revise_init_expr = quote + using Revise + const STDLIBS = $STDLIBS + revise_trackall() + end end const max_worker_rss = if haskey(ENV, "JULIA_TEST_MAXRSS_MB") @@ -84,16 +90,8 @@ cd(@__DIR__) do @everywhere include("testdefs.jl") if use_revise - @everywhere begin - Revise.track(Core.Compiler) - Revise.track(Base) - for (id, mod) in Base.loaded_modules - if id.name in STDLIBS - Revise.track(mod) - end - end - Revise.revise() - end + Base.invokelatest(revise_trackall) + Distributed.remotecall_eval(Main, workers(), revise_init_expr) end #pretty print the information about gc and mem usage @@ -223,6 +221,9 @@ cd(@__DIR__) do rmprocs(wrkr, waitfor=30) p = addprocs_with_testenv(1)[1] remotecall_fetch(include, p, "testdefs.jl") + if use_revise + Distributed.remotecall_eval(Main, p, revise_init_expr) + end end else print_testworker_stats(test, wrkr, resp) @@ -233,6 +234,9 @@ cd(@__DIR__) do rmprocs(wrkr, waitfor=30) p = addprocs_with_testenv(1)[1] remotecall_fetch(include, p, "testdefs.jl") + if use_revise + Distributed.remotecall_eval(Main, p, revise_init_expr) + end else # single process testing error("Halting tests. Memory limit reached : $resp > $max_worker_rss") end diff --git a/test/testdefs.jl b/test/testdefs.jl index b3124b34b343a..894a0cc110a7c 100644 --- a/test/testdefs.jl +++ b/test/testdefs.jl @@ -46,4 +46,16 @@ end # looking in . messes things up badly filter!(x->x!=".", LOAD_PATH) +# Support for Revise +function revise_trackall() + Revise.track(Core.Compiler) + Revise.track(Base) + for (id, mod) in Base.loaded_modules + if id.name in STDLIBS + Revise.track(mod) + end + end + Revise.revise() +end + nothing # File is loaded via a remotecall to "include". Ensure it returns "nothing". From a51015cd4e9f313fe1b72d75d306a6bcfdd7b0f4 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 16:27:09 -0700 Subject: [PATCH 136/232] Allow non-Function callables to be used in count(f, itr) (#36187) --- base/reduce.jl | 2 +- test/reduce.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/reduce.jl b/base/reduce.jl index 6582e72274aba..facf01b476cbf 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -840,7 +840,7 @@ end ## count -_bool(f::Function) = x->f(x)::Bool +_bool(f) = x->f(x)::Bool """ count(p, itr) -> Integer diff --git a/test/reduce.jl b/test/reduce.jl index d640bcdde7440..c395fae5dda7a 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -480,6 +480,11 @@ end @test count(!iszero, Int[1]) == 1 @test count(!iszero, [1, 0, 2, 0, 3, 0, 4]) == 4 +struct NonFunctionIsZero end +(::NonFunctionIsZero)(x) = iszero(x) +@test count(NonFunctionIsZero(), []) == 0 +@test count(NonFunctionIsZero(), [0]) == 1 +@test count(NonFunctionIsZero(), [1]) == 0 ## cumsum, cummin, cummax From d765e5923a6b637fb2570b767b7317fac63cc1a6 Mon Sep 17 00:00:00 2001 From: Clark Fitzgerald Date: Mon, 8 Jun 2020 19:02:22 -0700 Subject: [PATCH 137/232] Remove unnecessary use of ans keyword from manual (#36202) * Remove unnecessary use of ans keyword from manual The ans keyword adds to the cognitive load for new users reading the manual, potentially distracting their focus from the examples. This commit replaces non essential uses of ans in the manual with clear variable assignments. * Fixes wraparound integer doctest from FAQ * Mention ans variable in Getting Started doc * Restore ans variable to Getting Started doc --- doc/src/manual/calling-c-and-fortran-code.md | 2 +- .../manual/complex-and-rational-numbers.md | 4 +- doc/src/manual/constructors.md | 4 +- doc/src/manual/conversion-and-promotion.md | 12 ++--- doc/src/manual/faq.md | 8 ++-- .../integers-and-floating-point-numbers.md | 47 +++++++++---------- doc/src/manual/metaprogramming.md | 8 ++-- doc/src/manual/strings.md | 14 +++--- doc/src/manual/types.md | 16 +++---- 9 files changed, 56 insertions(+), 59 deletions(-) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index fb804e7e5aa2f..40a4591c6e7ff 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -71,7 +71,7 @@ julia> t = ccall(:clock, Int32, ()) julia> t 2292761 -julia> typeof(ans) +julia> typeof(t) Int32 ``` diff --git a/doc/src/manual/complex-and-rational-numbers.md b/doc/src/manual/complex-and-rational-numbers.md index 6f2bfaf2dfa84..faf8ffcf8c198 100644 --- a/doc/src/manual/complex-and-rational-numbers.md +++ b/doc/src/manual/complex-and-rational-numbers.md @@ -269,10 +269,10 @@ Constructing infinite rational values is acceptable: julia> 5//0 1//0 -julia> -3//0 +julia> x = -3//0 -1//0 -julia> typeof(ans) +julia> typeof(x) Rational{Int64} ``` diff --git a/doc/src/manual/constructors.md b/doc/src/manual/constructors.md index 734c9a84be2fb..31c0576e748be 100644 --- a/doc/src/manual/constructors.md +++ b/doc/src/manual/constructors.md @@ -360,10 +360,10 @@ and then delegates construction to the general constructor for the case where bo successfully creates a point of type `Point{Float64}`: ```jldoctest parametric2 -julia> Point(1,2.5) +julia> p = Point(1,2.5) Point{Float64}(1.0, 2.5) -julia> typeof(ans) +julia> typeof(p) Point{Float64} ``` diff --git a/doc/src/manual/conversion-and-promotion.md b/doc/src/manual/conversion-and-promotion.md index 49f07b84587fb..109287fd9d205 100644 --- a/doc/src/manual/conversion-and-promotion.md +++ b/doc/src/manual/conversion-and-promotion.md @@ -60,16 +60,16 @@ julia> x = 12 julia> typeof(x) Int64 -julia> convert(UInt8, x) +julia> xu = convert(UInt8, x) 0x0c -julia> typeof(ans) +julia> typeof(xu) UInt8 -julia> convert(AbstractFloat, x) +julia> xf = convert(AbstractFloat, x) 12.0 -julia> typeof(ans) +julia> typeof(xf) Float64 julia> a = Any[1 2 3; 4 5 6] @@ -271,10 +271,10 @@ Rational(n::Integer, d::Integer) = Rational(promote(n,d)...) This allows calls like the following to work: ```jldoctest -julia> Rational(Int8(15),Int32(-5)) +julia> x = Rational(Int8(15),Int32(-5)) -3//1 -julia> typeof(ans) +julia> typeof(x) Rational{Int32} ``` diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index b02f2d20dbd17..c3ef33c3d098e 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -410,16 +410,16 @@ is bounded and wraps around at either end so that adding, subtracting and multip can overflow or underflow, leading to some results that can be unsettling at first: ```jldoctest -julia> typemax(Int) +julia> x = typemax(Int) 9223372036854775807 -julia> ans+1 +julia> y = x+1 -9223372036854775808 -julia> -ans +julia> z = -y -9223372036854775808 -julia> 2*ans +julia> 2*z 0 ``` diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 9cbaee3d65db4..4152649461e14 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -113,34 +113,34 @@ Unsigned integers are input and output using the `0x` prefix and hexadecimal (ba determined by the number of hex digits used: ```jldoctest -julia> 0x1 +julia> x = 0x1 0x01 -julia> typeof(ans) +julia> typeof(x) UInt8 -julia> 0x123 +julia> x = 0x123 0x0123 -julia> typeof(ans) +julia> typeof(x) UInt16 -julia> 0x1234567 +julia> x = 0x1234567 0x01234567 -julia> typeof(ans) +julia> typeof(x) UInt32 -julia> 0x123456789abcdef +julia> x = 0x123456789abcdef 0x0123456789abcdef -julia> typeof(ans) +julia> typeof(x) UInt64 -julia> 0x11112222333344445555666677778888 +julia> x = 0x11112222333344445555666677778888 0x11112222333344445555666677778888 -julia> typeof(ans) +julia> typeof(x) UInt128 ``` @@ -148,28 +148,25 @@ This behavior is based on the observation that when one uses unsigned hex litera values, one typically is using them to represent a fixed numeric byte sequence, rather than just an integer value. -Recall that the variable [`ans`](@ref) is set to the value of the last expression evaluated in -an interactive session. This does not occur when Julia code is run in other ways. - Binary and octal literals are also supported: ```jldoctest -julia> 0b10 +julia> x = 0b10 0x02 -julia> typeof(ans) +julia> typeof(x) UInt8 -julia> 0o010 +julia> x = 0o010 0x08 -julia> typeof(ans) +julia> typeof(x) UInt8 -julia> 0x00000000000000001111222233334444 +julia> x = 0x00000000000000001111222233334444 0x00000000000000001111222233334444 -julia> typeof(ans) +julia> typeof(x) UInt128 ``` @@ -289,10 +286,10 @@ The above results are all [`Float64`](@ref) values. Literal [`Float32`](@ref) va entered by writing an `f` in place of `e`: ```jldoctest -julia> 0.5f0 +julia> x = 0.5f0 0.5f0 -julia> typeof(ans) +julia> typeof(x) Float32 julia> 2.5f-4 @@ -302,10 +299,10 @@ julia> 2.5f-4 Values can be converted to [`Float32`](@ref) easily: ```jldoctest -julia> Float32(-1.5) +julia> x = Float32(-1.5) -1.5f0 -julia> typeof(ans) +julia> typeof(x) Float32 ``` @@ -319,10 +316,10 @@ julia> 0x1p0 julia> 0x1.8p3 12.0 -julia> 0x.4p-1 +julia> x = 0x.4p-1 0.125 -julia> typeof(ans) +julia> typeof(x) Float64 ``` diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index 3ba652197deaf..9d8039cee5e41 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -105,10 +105,10 @@ an [interned string](https://en.wikipedia.org/wiki/String_interning) used as one of expressions: ```jldoctest -julia> :foo +julia> s = :foo :foo -julia> typeof(ans) +julia> typeof(s) Symbol ``` @@ -354,10 +354,10 @@ Given an expression object, one can cause Julia to evaluate (execute) it at glob [`eval`](@ref): ```jldoctest interp1 -julia> :(1 + 2) +julia> ex1 = :(1 + 2) :(1 + 2) -julia> eval(ans) +julia> eval(ex1) 3 julia> ex = :(a + b) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 56e7336c3d926..9f73a48a47254 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -51,24 +51,24 @@ other subtypes of `AbstractChar`, e.g. to optimize operations for other input and shown: ```jldoctest -julia> 'x' +julia> c = 'x' 'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase) -julia> typeof(ans) +julia> typeof(c) Char ``` You can easily convert a `Char` to its integer value, i.e. code point: ```jldoctest -julia> Int('x') +julia> c = Int('x') 120 -julia> typeof(ans) +julia> typeof(c) Int64 ``` -On 32-bit architectures, [`typeof(ans)`](@ref) will be [`Int32`](@ref). You can convert an +On 32-bit architectures, [`typeof(c)`](@ref) will be [`Int32`](@ref). You can convert an integer value back to a `Char` just as easily: ```jldoctest @@ -756,10 +756,10 @@ using non-standard string literals prefixed with various identifiers beginning w basic regular expression literal without any options turned on just uses `r"..."`: ```jldoctest -julia> r"^\s*(?:#|$)" +julia> re = r"^\s*(?:#|$)" r"^\s*(?:#|$)" -julia> typeof(ans) +julia> typeof(re) Regex ``` diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index c1c77ffa8acb9..30026a885e54b 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -90,10 +90,10 @@ julia> function foo() end foo (generic function with 1 method) -julia> foo() +julia> x = foo() 100 -julia> typeof(ans) +julia> typeof(x) Int8 ``` @@ -667,10 +667,10 @@ Since the type `Point{Float64}` is a concrete type equivalent to `Point` declare in place of `T`, it can be applied as a constructor accordingly: ```jldoctest pointtype -julia> Point{Float64}(1.0, 2.0) +julia> p = Point{Float64}(1.0, 2.0) Point{Float64}(1.0, 2.0) -julia> typeof(ans) +julia> typeof(p) Point{Float64} ``` @@ -695,16 +695,16 @@ that reason, you can also apply `Point` itself as a constructor, provided that t of the parameter type `T` is unambiguous: ```jldoctest pointtype -julia> Point(1.0,2.0) +julia> p1 = Point(1.0,2.0) Point{Float64}(1.0, 2.0) -julia> typeof(ans) +julia> typeof(p1) Point{Float64} -julia> Point(1,2) +julia> p2 = Point(1,2) Point{Int64}(1, 2) -julia> typeof(ans) +julia> typeof(p2) Point{Int64} ``` From 9738e4b3ef2a8cce8ce9bffa576c725930906944 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 8 Jun 2020 23:10:38 -0400 Subject: [PATCH 138/232] Refactor cache logic for easy replacement (#35831) * Refactor cache logic for easy replacement This is the next step in the line of work started by #33955, though a lot of enabling work towards this was previously done by Jameson in his codegen-norecursion branch. The basic thrust here is to allow external packages to manage their own cache of compiled code that may have been generated using entirely difference inference or compiler options. The GPU compilers are one such example, but there are several others, including generating code using offload compilers, such as XLA or compilers for secure computation. A lot of this is just moving code arround to make it clear exactly which parts of the code are accessing the internal code cache (which is now its own type to make it obvious when it's being accessed), as well as providing clear extension points for custom cache implementations. The second part is to refactor CodeInstance construction to separate construction and insertion into the internal cache (so it can be inserted into an external cache instead if desired). The last part of the change is to give cgparams another hook that lets the caller replace the cache lookup to be used by codegen. * Update base/compiler/cicache.jl Co-authored-by: Tim Besard * Apply suggestions from code review Co-authored-by: Jameson Nash * Rename always_cache_tree -> !allow_discard_tree Co-authored-by: Tim Besard Co-authored-by: Jameson Nash --- base/boot.jl | 5 + base/compiler/abstractinterpretation.jl | 2 +- base/compiler/cicache.jl | 52 ++++++++++ base/compiler/compiler.jl | 1 + base/compiler/ssair/inlining.jl | 8 +- base/compiler/typeinfer.jl | 124 ++++++++++++++---------- base/compiler/types.jl | 14 +++ base/compiler/utilities.jl | 5 - base/reflection.jl | 7 +- src/aotcompile.cpp | 44 ++++++--- src/codegen.cpp | 5 +- src/gf.c | 46 ++++++--- src/julia.h | 8 +- test/compiler/inference.jl | 2 +- test/reflection.jl | 4 +- 15 files changed, 227 insertions(+), 100 deletions(-) create mode 100644 base/compiler/cicache.jl diff --git a/base/boot.jl b/base/boot.jl index 034ca642c459a..c2c8c35c34a87 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -381,6 +381,11 @@ eval(Core, :(UpsilonNode(val) = $(Expr(:new, :UpsilonNode, :val)))) eval(Core, :(UpsilonNode() = $(Expr(:new, :UpsilonNode)))) eval(Core, :(LineInfoNode(@nospecialize(method), file::Symbol, line::Int, inlined_at::Int) = $(Expr(:new, :LineInfoNode, :method, :file, :line, :inlined_at)))) +eval(Core, :(CodeInstance(mi::MethodInstance, @nospecialize(rettype), @nospecialize(inferred_const), + @nospecialize(inferred), const_flags::Int32, + min_world::UInt, max_world::UInt) = + ccall(:jl_new_codeinst, Ref{CodeInstance}, (Any, Any, Any, Any, Int32, UInt, UInt), + mi, rettype, inferred_const, inferred, const_flags, min_world, max_world))) Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool), name, std_imports) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index a3793f2a6fbf3..441d6a097bb25 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -241,7 +241,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp mi = mi::MethodInstance # decide if it's likely to be worthwhile if !force_inference - code = inf_for_methodinstance(interp, mi, get_world_counter(interp)) + code = get(code_cache(interp), mi, nothing) declared_inline = isdefined(method, :source) && ccall(:jl_ir_flag_inlineable, Bool, (Any,), method.source) cache_inlineable = declared_inline if isdefined(code, :inferred) && !cache_inlineable diff --git a/base/compiler/cicache.jl b/base/compiler/cicache.jl new file mode 100644 index 0000000000000..d82665b61fc9a --- /dev/null +++ b/base/compiler/cicache.jl @@ -0,0 +1,52 @@ +""" + struct InternalCodeCache + +Internally, each `MethodInstance` keep a unique global cache of code instances +that have been created for the given method instance, stratified by world age +ranges. This struct abstracts over access to this cache. +""" +struct InternalCodeCache +end + +function setindex!(cache::InternalCodeCache, ci::CodeInstance, mi::MethodInstance) + ccall(:jl_mi_cache_insert, Cvoid, (Any, Any), mi, ci) +end + +const GLOBAL_CI_CACHE = InternalCodeCache() + +""" + struct WorldView + +Takes a given cache and provides access to the cache contents for the given +range of world ages, rather than defaulting to the current active world age. +""" +struct WorldView{Cache} + cache::Cache + min_world::UInt + max_world::UInt +end +WorldView(cache, r::UnitRange) = WorldView(cache, first(r), last(r)) +WorldView(cache, world::UInt) = WorldView(cache, world, world) +WorldView(wvc::WorldView, min_world::UInt, max_world::UInt) = + WorldView(wvc.cache, min_world, max_world) + +function haskey(wvc::WorldView{InternalCodeCache}, mi::MethodInstance) + ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, wvc.min_world, wvc.max_world)::Union{Nothing, CodeInstance} !== nothing +end + +function get(wvc::WorldView{InternalCodeCache}, mi::MethodInstance, default) + r = ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, wvc.min_world, wvc.max_world)::Union{Nothing, CodeInstance} + if r === nothing + return default + end + return r::CodeInstance +end + +function getindex(wvc::WorldView{InternalCodeCache}, mi::MethodInstance) + r = get(wvc, mi, nothing) + r === nothing && throw(KeyError(mi)) + return r::CodeInstance +end + +setindex!(wvc::WorldView{InternalCodeCache}, ci::CodeInstance, mi::MethodInstance) = + setindex!(wvc.cache, ci, mi) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 873ce764bccbf..4ac2e3ca9a4fd 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -100,6 +100,7 @@ include("compiler/validation.jl") include("compiler/inferenceresult.jl") include("compiler/inferencestate.jl") +include("compiler/cicache.jl") include("compiler/typeutils.jl") include("compiler/typelimits.jl") diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 2acead5985078..ddd7cfe7a1937 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -798,7 +798,7 @@ function iterate(split::UnionSplitSignature, state::Vector{Int}...) return (sig, state) end -function handle_single_case!(ir::IRCode, stmt::Expr, idx::Int, @nospecialize(case), isinvoke::Bool, todo::Vector{Any}, sv::OptimizationState) +function handle_single_case!(ir::IRCode, stmt::Expr, idx::Int, @nospecialize(case), isinvoke::Bool, todo::Vector{Any}) if isa(case, ConstantCase) ir[SSAValue(idx)] = case.val elseif isa(case, MethodInstance) @@ -949,7 +949,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, invoke_data::Invok methsp = methsp::SimpleVector result = analyze_method!(idx, sig, metharg, methsp, method, stmt, sv, true, invoke_data, calltype) - handle_single_case!(ir, stmt, idx, result, true, todo, sv) + handle_single_case!(ir, stmt, idx, result, true, todo) update_valid_age!(invoke_data.min_valid, invoke_data.max_valid, sv) return nothing end @@ -1117,7 +1117,7 @@ function assemble_inline_todo!(ir::IRCode, sv::OptimizationState) # be able to do the inlining now (for constant cases), or push it directly # onto the todo list if fully_covered && length(cases) == 1 - handle_single_case!(ir, stmt, idx, cases[1][2], false, todo, sv) + handle_single_case!(ir, stmt, idx, cases[1][2], false, todo) continue end length(cases) == 0 && continue @@ -1332,7 +1332,7 @@ function find_inferred(mi::MethodInstance, @nospecialize(atypes), sv::Optimizati end end - linfo = inf_for_methodinstance(sv.interp, mi, sv.world) + linfo = get(WorldView(code_cache(sv.interp), sv.world), mi, nothing) if linfo isa CodeInstance if invoke_api(linfo) == 2 # in this case function can be inlined to a constant diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 0e367bb3f6449..12b3da4a4e2db 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -4,7 +4,7 @@ function typeinf(interp::AbstractInterpreter, result::InferenceResult, cached::Bool) frame = InferenceState(result, cached, interp) frame === nothing && return false - cached && (result.linfo.inInference = true) + cached && lock_mi_inference(interp, result.linfo) return typeinf(interp, frame) end @@ -64,7 +64,7 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) caller.src.min_world = min_valid caller.src.max_world = max_valid if cached - cache_result(interp, caller.result, min_valid, max_valid) + cache_result!(interp, caller.result, min_valid, max_valid) end if max_valid == typemax(UInt) # if we aren't cached, we don't need this edge @@ -79,60 +79,78 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) return true end -# inference completed on `me` -# update the MethodInstance and notify the edges -function cache_result(interp::AbstractInterpreter, result::InferenceResult, min_valid::UInt, max_valid::UInt) - def = result.linfo.def - toplevel = !isa(result.linfo.def, Method) - # check if the existing linfo metadata is also sufficient to describe the current inference result - # to decide if it is worth caching this - already_inferred = !result.linfo.inInference - if inf_for_methodinstance(interp, result.linfo, min_valid, max_valid) isa CodeInstance - already_inferred = true - end - - # TODO: also don't store inferred code if we've previously decided to interpret this function - if !already_inferred - inferred_result = result.src - if inferred_result isa Const - # use constant calling convention - rettype_const = (result.src::Const).val - const_flags = 0x3 +function CodeInstance(result::InferenceResult, min_valid::UInt, max_valid::UInt, + may_compress=true, allow_discard_tree=true) + inferred_result = result.src + local const_flags::Int32 + if inferred_result isa Const + # use constant calling convention + rettype_const = (result.src::Const).val + const_flags = 0x3 + else + if isa(result.result, Const) + rettype_const = (result.result::Const).val + const_flags = 0x2 + elseif isconstType(result.result) + rettype_const = result.result.parameters[1] + const_flags = 0x2 else - if isa(result.result, Const) - rettype_const = (result.result::Const).val - const_flags = 0x2 - elseif isconstType(result.result) - rettype_const = result.result.parameters[1] - const_flags = 0x2 - else - rettype_const = nothing - const_flags = 0x00 - end - if !toplevel && inferred_result isa CodeInfo - cache_the_tree = result.src.inferred && + rettype_const = nothing + const_flags = 0x00 + end + if inferred_result isa CodeInfo + def = result.linfo.def + toplevel = !isa(def, Method) + if !toplevel + cache_the_tree = !allow_discard_tree || (result.src.inferred && (result.src.inlineable || - ccall(:jl_isa_compileable_sig, Int32, (Any, Any), result.linfo.specTypes, def) != 0) + ccall(:jl_isa_compileable_sig, Int32, (Any, Any), result.linfo.specTypes, def) != 0)) if cache_the_tree - # compress code for non-toplevel thunks - nslots = length(inferred_result.slotflags) - resize!(inferred_result.slottypes, nslots) - resize!(inferred_result.slotnames, nslots) - inferred_result = ccall(:jl_compress_ir, Any, (Any, Any), def, inferred_result) + if may_compress + nslots = length(inferred_result.slotflags) + resize!(inferred_result.slottypes, nslots) + resize!(inferred_result.slotnames, nslots) + inferred_result = ccall(:jl_compress_ir, Any, (Any, Any), def, inferred_result) + end else inferred_result = nothing end end end - if !isa(inferred_result, Union{CodeInfo, Vector{UInt8}}) - inferred_result = nothing - end - ccall(:jl_set_method_inferred, Ref{CodeInstance}, (Any, Any, Any, Any, Int32, UInt, UInt), - result.linfo, widenconst(result.result), rettype_const, inferred_result, - const_flags, min_valid, max_valid) end - result.linfo.inInference = false + if !isa(inferred_result, Union{CodeInfo, Vector{UInt8}}) + inferred_result = nothing + end + return CodeInstance(result.linfo, + widenconst(result.result), rettype_const, inferred_result, + const_flags, min_valid, max_valid) +end + +# For the NativeInterpreter, we don't need to do an actual cache query to know +# if something was already inferred. If we reach this point, but the inference +# flag has been turned off, then it's in the cache. This is purely a performance +# optimization. +already_inferred_quick_test(interp::NativeInterpreter, mi::MethodInstance) = + !mi.inInference +already_inferred_quick_test(interp::AbstractInterpreter, mi::MethodInstance) = + false + +# inference completed on `me` +# update the MethodInstance +function cache_result!(interp::AbstractInterpreter, result::InferenceResult, min_valid::UInt, max_valid::UInt) + # check if the existing linfo metadata is also sufficient to describe the current inference result + # to decide if it is worth caching this + already_inferred = already_inferred_quick_test(interp, result.linfo) + if !already_inferred && haskey(WorldView(code_cache(interp), min_valid, max_valid), result.linfo) + already_inferred = true + end + + # TODO: also don't store inferred code if we've previously decided to interpret this function + if !already_inferred + code_cache(interp)[result.linfo] = CodeInstance(result, min_valid, max_valid) + end + unlock_mi_inference(interp, result.linfo) nothing end @@ -142,7 +160,7 @@ function finish(me::InferenceState, interp::AbstractInterpreter) # a top parent will be cached still, but not this intermediate work # we can throw everything else away now me.cached = false - me.linfo.inInference = false + unlock_mi_inference(interp, me.linfo) me.src.inlineable = false else # annotate fulltree with type information @@ -452,7 +470,7 @@ end # compute (and cache) an inferred AST and return the current best estimate of the result type function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atypes), sparams::SimpleVector, caller::InferenceState) mi = specialize_method(method, atypes, sparams)::MethodInstance - code = inf_for_methodinstance(interp, mi, get_world_counter(interp)) + code = get(code_cache(interp), mi, nothing) if code isa CodeInstance # return existing rettype if the code is already inferred update_valid_age!(min_world(code), max_world(code), caller) if isdefined(code, :rettype_const) @@ -470,12 +488,12 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end if frame === false # completely new - mi.inInference = true + lock_mi_inference(interp, mi) result = InferenceResult(mi) frame = InferenceState(result, #=cached=#true, interp) # always use the cache for edge targets if frame === nothing # can't get the source for this, so we know nothing - mi.inInference = false + unlock_mi_inference(interp, mi) return Any, nothing end if caller.cached || caller.limited # don't involve uncached functions in cycle resolution @@ -524,7 +542,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) method = mi.def::Method for i = 1:2 # test-and-lock-and-test i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) - code = inf_for_methodinstance(interp, mi, get_world_counter(interp)) + code = get(code_cache(interp), mi, nothing) if code isa CodeInstance # see if this code already exists in the cache inf = code.inferred @@ -565,7 +583,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) end end end - mi.inInference = true + lock_mi_inference(interp, mi) frame = InferenceState(InferenceResult(mi), #=cached=#true, interp) frame === nothing && return nothing typeinf(interp, frame) @@ -582,7 +600,7 @@ function typeinf_type(interp::AbstractInterpreter, method::Method, @nospecialize mi = specialize_method(method, atypes, sparams)::MethodInstance for i = 1:2 # test-and-lock-and-test i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) - code = inf_for_methodinstance(interp, mi, get_world_counter(interp)) + code = get(code_cache(interp), mi, nothing) if code isa CodeInstance # see if this rettype already exists in the cache i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index be92d0173f29f..068574b80d0c9 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -175,3 +175,17 @@ InferenceParams(ni::NativeInterpreter) = ni.inf_params OptimizationParams(ni::NativeInterpreter) = ni.opt_params get_world_counter(ni::NativeInterpreter) = ni.world get_inference_cache(ni::NativeInterpreter) = ni.cache + +code_cache(ni::NativeInterpreter) = WorldView(GLOBAL_CI_CACHE, ni.world) + +""" + lock_mi_inference(ni::NativeInterpreter, mi::MethodInstance) + +Hint that `mi` is in inference to help accelerate bootstrapping. This helps limit the amount of wasted work we might do when inference is working on initially inferring itself by letting us detect when inference is already in progress and not running a second copy on it. This creates a data-race, but the entry point into this code from C (jl_type_infer) already includes detection and restriction on recursion, so it is hopefully mostly a benign problem (since it should really only happen during the first phase of bootstrapping that we encounter this flag). +""" +lock_mi_inference(ni::NativeInterpreter, mi::MethodInstance) = (mi.inInference = true; nothing) + +""" + See lock_mi_inference +""" +unlock_mi_inference(ni::NativeInterpreter, mi::MethodInstance) = (mi.inInference = false; nothing) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 0fa4f776de681..84ca65834726f 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -118,11 +118,6 @@ function retrieve_code_info(linfo::MethodInstance) end end -function inf_for_methodinstance(interp::AbstractInterpreter, mi::MethodInstance, min_world::UInt, max_world::UInt=min_world) - return ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, min_world, max_world)::Union{Nothing, CodeInstance} -end - - # get a handle to the unique specialization object representing a particular instantiation of a call function specialize_method(method::Method, @nospecialize(atypes), sparams::SimpleVector, preexisting::Bool=false) if preexisting diff --git a/base/reflection.jl b/base/reflection.jl index 3b3e745559593..df4ee17efc89d 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -981,17 +981,20 @@ struct CodegenParams emit_function::Any emitted_function::Any + lookup::Ptr{Cvoid} + function CodegenParams(; track_allocations::Bool=true, code_coverage::Bool=true, static_alloc::Bool=true, prefer_specsig::Bool=false, gnu_pubnames=true, debug_info_kind::Cint = default_debug_info_kind(), module_setup=nothing, module_activation=nothing, raise_exception=nothing, - emit_function=nothing, emitted_function=nothing) + emit_function=nothing, emitted_function=nothing, + lookup::Ptr{Cvoid}=cglobal(:jl_rettype_inferred)) return new( Cint(track_allocations), Cint(code_coverage), Cint(static_alloc), Cint(prefer_specsig), Cint(gnu_pubnames), debug_info_kind, module_setup, module_activation, raise_exception, - emit_function, emitted_function) + emit_function, emitted_function, lookup) end end diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 7ad21841ab693..1cfe0772856ec 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -248,6 +248,33 @@ static void makeSafeName(GlobalObject &G) G.setName(StringRef(SafeName.data(), SafeName.size())); } +static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance_t *mi, size_t world, jl_code_instance_t **ci_out, jl_code_info_t **src_out) +{ + jl_value_t *ci = cgparams.lookup(mi, world, world); + JL_GC_PROMISE_ROOTED(ci); + jl_code_instance_t *codeinst = NULL; + if (ci != jl_nothing) { + codeinst = (jl_code_instance_t*)ci; + *src_out = (jl_code_info_t*)codeinst->inferred; + jl_method_t *def = codeinst->def->def.method; + if ((jl_value_t*)*src_out == jl_nothing) + *src_out = NULL; + if (*src_out && jl_is_method(def)) + *src_out = jl_uncompress_ir(def, codeinst, (jl_array_t*)*src_out); + } + if (*src_out == NULL || !jl_is_code_info(*src_out)) { + if (cgparams.lookup != jl_rettype_inferred) { + jl_error("Refusing to automatically run type inference with custom cache lookup."); + } + else { + *src_out = jl_type_infer(mi, world, 0); + codeinst = jl_get_method_inferred(mi, (*src_out)->rettype, (*src_out)->min_world, (*src_out)->max_world); + if ((*src_out)->inferred && !codeinst->inferred) + codeinst->inferred = jl_nothing; + } + } + *ci_out = codeinst; +} // takes the running content that has collected in the shadow module and dump it to disk // this builds the object file portion of the sysimage files for fast startup, and can @@ -294,23 +321,8 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams, int _p // then we want to compile and emit this if (mi->def.method->primary_world <= params.world && params.world <= mi->def.method->deleted_world) { // find and prepare the source code to compile - jl_value_t *ci = jl_rettype_inferred(mi, params.world, params.world); jl_code_instance_t *codeinst = NULL; - if (ci != jl_nothing) { - codeinst = (jl_code_instance_t*)ci; - src = (jl_code_info_t*)codeinst->inferred; - jl_method_t *def = codeinst->def->def.method; - if ((jl_value_t*)src == jl_nothing) - src = NULL; - if (src && jl_is_method(def)) - src = jl_uncompress_ir(def, codeinst, (jl_array_t*)src); - } - if (src == NULL || !jl_is_code_info(src)) { - src = jl_type_infer(mi, params.world, 0); - codeinst = jl_get_method_inferred(mi, src->rettype, src->min_world, src->max_world); - if (src->inferred && !codeinst->inferred) - codeinst->inferred = jl_nothing; - } + jl_ci_cache_lookup(cgparams, mi, params.world, &codeinst, &src); if (src && !emitted.count(codeinst)) { // now add it to our compilation results JL_GC_PROMISE_ROOTED(codeinst->rettype); diff --git a/src/codegen.cpp b/src/codegen.cpp index ab4b8522c46e5..58aee11558dc7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -366,7 +366,8 @@ extern "C" { #else 1, #endif - jl_default_debug_info_kind, NULL, NULL, NULL, NULL, NULL}; + jl_default_debug_info_kind, NULL, NULL, NULL, NULL, NULL, + jl_rettype_inferred }; } template @@ -2799,7 +2800,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) } } else { - jl_value_t *ci = jl_rettype_inferred(mi, ctx.world, ctx.world); // TODO: need to use the right pair world here + jl_value_t *ci = ctx.params->lookup(mi, ctx.world, ctx.world); // TODO: need to use the right pair world here jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; if (ci != jl_nothing && codeinst->invoke != jl_fptr_sparam) { // check if we know we definitely can't handle this specptr if (codeinst->invoke == jl_fptr_const_return) { diff --git a/src/gf.c b/src/gf.c index ca51b5f24917b..c3d14ad409126 100644 --- a/src/gf.c +++ b/src/gf.c @@ -209,10 +209,12 @@ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_value_t *typ // ----- MethodInstance specialization instantiation ----- // JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t*); -JL_DLLEXPORT jl_code_instance_t* jl_set_method_inferred( +JL_DLLEXPORT jl_code_instance_t* jl_new_codeinst( jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world); +JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, + jl_code_instance_t *ci JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED); jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_args_t fptr) JL_GC_DISABLED { @@ -236,8 +238,10 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a m->unspecialized = mi; jl_gc_wb(m, mi); - jl_code_instance_t *codeinst = jl_set_method_inferred(mi, (jl_value_t*)jl_any_type, jl_nothing, jl_nothing, + jl_code_instance_t *codeinst = jl_new_codeinst(mi, + (jl_value_t*)jl_any_type, jl_nothing, jl_nothing, 0, 1, ~(size_t)0); + jl_mi_cache_insert(mi, codeinst); codeinst->specptr.fptr1 = fptr; codeinst->invoke = jl_fptr_args; @@ -344,7 +348,7 @@ JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_method_instance_t *mi, size_t mi JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( - jl_method_instance_t *mi, jl_value_t *rettype, + jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, size_t min_world, size_t max_world) { jl_code_instance_t *codeinst = mi->cache; @@ -356,13 +360,15 @@ JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( } codeinst = codeinst->next; } - return jl_set_method_inferred( + codeinst = jl_new_codeinst( mi, rettype, NULL, NULL, 0, min_world, max_world); + jl_mi_cache_insert(mi, codeinst); + return codeinst; } -JL_DLLEXPORT jl_code_instance_t *jl_set_method_inferred( - jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, +JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( + jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world /*, jl_array_t *edges, int absolute_max*/) @@ -388,15 +394,24 @@ JL_DLLEXPORT jl_code_instance_t *jl_set_method_inferred( } codeinst->specptr.fptr = NULL; codeinst->isspecsig = 0; + codeinst->next = NULL; + JL_GC_POP(); + return codeinst; +} + +JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, + jl_code_instance_t *ci JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED) +{ + JL_GC_PUSH1(&ci); if (jl_is_method(mi->def.method)) JL_LOCK(&mi->def.method->writelock); - codeinst->next = mi->cache; - mi->cache = codeinst; - jl_gc_wb(mi, codeinst); + ci->next = mi->cache; + mi->cache = ci; + jl_gc_wb(mi, ci); if (jl_is_method(mi->def.method)) JL_UNLOCK(&mi->def.method->writelock); JL_GC_POP(); - return codeinst; + return; } static int get_method_unspec_list(jl_typemap_entry_t *def, void *closure) @@ -1949,20 +1964,24 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (jl_is_method(def) && def->unspecialized) { jl_code_instance_t *unspec = def->unspecialized->cache; if (unspec && unspec->invoke != NULL) { - jl_code_instance_t *codeinst = jl_set_method_inferred(mi, (jl_value_t*)jl_any_type, NULL, NULL, + jl_code_instance_t *codeinst = jl_new_codeinst(mi, + (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0); codeinst->isspecsig = 0; codeinst->specptr = unspec->specptr; codeinst->rettype_const = unspec->rettype_const; jl_atomic_store_release(&codeinst->invoke, unspec->invoke); + jl_mi_cache_insert(mi, codeinst); return codeinst; } } jl_code_info_t *src = jl_code_for_interpreter(mi); if (!jl_code_requires_compiler(src)) { - jl_code_instance_t *codeinst = jl_set_method_inferred(mi, (jl_value_t*)jl_any_type, NULL, NULL, + jl_code_instance_t *codeinst = jl_new_codeinst(mi, + (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0); jl_atomic_store_release(&codeinst->invoke, jl_fptr_interpret_call); + jl_mi_cache_insert(mi, codeinst); return codeinst; } if (jl_options.compile_enabled == JL_OPTIONS_COMPILE_OFF) { @@ -1983,12 +2002,13 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t ucache->invoke != jl_fptr_interpret_call) { return ucache; } - codeinst = jl_set_method_inferred(mi, (jl_value_t*)jl_any_type, NULL, NULL, + codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0); codeinst->isspecsig = 0; codeinst->specptr = ucache->specptr; codeinst->rettype_const = ucache->rettype_const; jl_atomic_store_release(&codeinst->invoke, ucache->invoke); + jl_mi_cache_insert(mi, codeinst); } return codeinst; } diff --git a/src/julia.h b/src/julia.h index a6b10f84287a3..4bf303a1b23d6 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2046,7 +2046,10 @@ typedef struct { #define jl_root_task (jl_get_ptls_states()->root_task) // codegen interface ---------------------------------------------------------- - +// The root propagation here doesn't have to be literal, but callers should +// ensure that the return value outlives the MethodInstance +typedef jl_value_t *(*jl_codeinstance_lookup_t)(jl_method_instance_t *mi JL_PROPAGATES_ROOT, + size_t min_world, size_t max_world); typedef struct { int track_allocations; // can we track allocations? int code_coverage; // can we measure coverage? @@ -2084,6 +2087,9 @@ typedef struct { // parameters: MethodInstance, CodeInfo, world age as UInt // return value: none jl_value_t *emitted_function; + + // Cache access. Default: jl_rettype_inferred. + jl_codeinstance_lookup_t lookup; } jl_cgparams_t; extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; extern JL_DLLEXPORT int jl_default_debug_info_kind; diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index f1ddc7349c843..5eef0d1a125c2 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1148,7 +1148,7 @@ end function test_const_return(@nospecialize(f), @nospecialize(t), @nospecialize(val)) interp = Core.Compiler.NativeInterpreter() - linfo = Core.Compiler.inf_for_methodinstance(interp, get_linfo(f, t), Core.Compiler.get_world_counter())::Core.CodeInstance + linfo = Core.Compiler.getindex(Core.Compiler.code_cache(interp), get_linfo(f, t)) # If coverage is not enabled, make the check strict by requiring constant ABI # Otherwise, check the typed AST to make sure we return a constant. if Base.JLOptions().code_coverage == 0 diff --git a/test/reflection.jl b/test/reflection.jl index 4b77f2e2ac058..9e47a3a0b508a 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -514,14 +514,14 @@ let @test !isempty(m.specializations) # uncached, but creates the specializations entry mi = Core.Compiler.specialize_method(m, Tuple{ft}, Core.svec()) interp = Core.Compiler.NativeInterpreter(world) - @test Core.Compiler.inf_for_methodinstance(interp, mi, world) === nothing + @test !Core.Compiler.haskey(Core.Compiler.code_cache(interp), mi) @test !isdefined(mi, :cache) code_typed(f18888, Tuple{}; optimize=true) @test !isdefined(mi, :cache) Base.return_types(f18888, Tuple{}) - @test Core.Compiler.inf_for_methodinstance(interp, mi, world) === mi.cache + @test Core.Compiler.getindex(Core.Compiler.code_cache(interp), mi) === mi.cache @test mi.cache isa Core.CodeInstance @test !isdefined(mi.cache, :next) end From 36270e90eda7a8507f0c144a983af343cde73f4b Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 9 Jun 2020 02:34:51 -0500 Subject: [PATCH 139/232] Improve the API of REPL.TerminalMenus (#35915) This overhaul of the `AbstractMenu` extension interface of `REPL.TerminalMenus` addresses several concerns: - Printing the "paging" navigation indicators was handled by TerminalMenus, but the subtype methods were expected to print the cursor indicator and, for multiple-selection menus, the selection status indicators in a manner that preserved alignment. There was no obvious reason for the inconsistency. - Printing these indicators relied on accessing a mutable private global variable in TerminalMenus. It was therefore not possible to use different settings simultaneously for two different menus (e.g. a main menu and its submenus). - The API required that subtype methods supply a list of strings for each menu-option when really it only needed to know how many options were available. This was particularly problematic for large, dynamic menus whose options might change and for which lists of options would have to be reallocated regularly but were then thrown away after checking their length. This deprecates the old interface in a backward-compatible manner, so is non-breaking. --- NEWS.md | 5 + stdlib/REPL/docs/src/index.md | 108 +++++-- stdlib/REPL/src/TerminalMenus/AbstractMenu.jl | 285 ++++++++++++------ .../REPL/src/TerminalMenus/MultiSelectMenu.jl | 47 ++- stdlib/REPL/src/TerminalMenus/RadioMenu.jl | 25 +- .../REPL/src/TerminalMenus/TerminalMenus.jl | 8 + stdlib/REPL/src/TerminalMenus/config.jl | 119 +++++++- stdlib/REPL/src/TerminalMenus/util.jl | 38 +-- .../REPL/test/TerminalMenus/dynamic_menu.jl | 118 ++++++++ .../test/TerminalMenus/legacytests/config.jl | 15 + .../legacytests/old_multiselect_menu.jl | 39 +++ .../legacytests/old_radio_menu.jl | 42 +++ .../test/TerminalMenus/multiselect_menu.jl | 64 ++-- stdlib/REPL/test/TerminalMenus/radio_menu.jl | 50 +-- stdlib/REPL/test/TerminalMenus/runtests.jl | 28 +- 15 files changed, 762 insertions(+), 229 deletions(-) create mode 100644 stdlib/REPL/test/TerminalMenus/dynamic_menu.jl create mode 100644 stdlib/REPL/test/TerminalMenus/legacytests/config.jl create mode 100644 stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl create mode 100644 stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl diff --git a/NEWS.md b/NEWS.md index 61e364f1c63f9..63ffa9f8505e0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,6 +60,11 @@ Standard library changes #### REPL +* The `AbstractMenu` extension interface of `REPL.TerminalMenus` has been extensively + overhauled. The new interface does not rely on global configuration variables, is more + consistent in delegating printing of the navigation/selection markers, and provides + improved support for dynamic menus. These changes are compatible with the previous + (deprecated) interface, so are non-breaking. #### SparseArrays diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 87f3eb2ed359c..565399a472f3e 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -544,24 +544,12 @@ You like the following fruits: ### Customization / Configuration -All interface customization is done through the keyword only -`TerminalMenus.config()` function. +#### ConfiguredMenu subtypes -#### Arguments +Starting with Julia 1.6, the recommended way to configure menus is via the constructor. +For instance, the default multiple-selection menu - - `charset::Symbol=:na`: ui characters to use (`:ascii` or `:unicode`); overridden by other arguments - - `cursor::Char='>'|'→'`: character to use for cursor - - `up_arrow::Char='^'|'↑'`: character to use for up arrow - - `down_arrow::Char='v'|'↓'`: character to use for down arrow - - `checked::String="[X]"|"✓"`: string to use for checked - - `unchecked::String="[ ]"|"⬚")`: string to use for unchecked - - `scroll::Symbol=:na`: If `:wrap` then wrap the cursor around top and bottom, if :`nowrap` do not wrap cursor - - `supress_output::Bool=false`: For testing. If true, menu will not be printed to console. - - `ctrl_c_interrupt::Bool=true`: If `false`, return empty on ^C, if `true` throw InterruptException() on ^C - -#### Examples - -```julia +``` julia> menu = MultiSelectMenu(options, pagesize=5); julia> request(menu) # ASCII is used by default @@ -571,9 +559,12 @@ julia> request(menu) # ASCII is used by default [ ] grape > [X] strawberry v [ ] blueberry -Set([4, 2]) +``` -julia> TerminalMenus.config(charset=:unicode) +can instead be rendered with Unicode selection and navigation characters with + +```julia +julia> menu = MultiSelectMenu(options, pagesize=5, charset=:unicode); julia> request(menu) [press: d=done, a=all, n=none] @@ -582,10 +573,14 @@ julia> request(menu) ⬚ grape → ✓ strawberry ↓ ⬚ blueberry -Set([4, 2]) +``` -julia> TerminalMenus.config(checked="YEP!", unchecked="NOPE", cursor='⧐') +More fine-grained configuration is also possible: +```julia +julia> menu = MultiSelectMenu(options, pagesize=5, charset=:unicode, checked="YEP!", unchecked="NOPE", cursor='⧐'); + +julia> request(menu) julia> request(menu) [press: d=done, a=all, n=none] NOPE apple @@ -593,12 +588,81 @@ julia> request(menu) NOPE grape ⧐ YEP! strawberry ↓ NOPE blueberry -Set([4, 2]) - ``` +Aside from the overall `charset` option, for `RadioMenu` the configurable options are: + + - `cursor::Char='>'|'→'`: character to use for cursor + - `up_arrow::Char='^'|'↑'`: character to use for up arrow + - `down_arrow::Char='v'|'↓'`: character to use for down arrow + - `scroll_wrap::Bool=false`: optionally wrap-around at the beginning/end of a menu + - `ctrl_c_interrupt::Bool=true`: If `false`, return empty on ^C, if `true` throw InterruptException() on ^C + +`MultiSelectMenu` adds: + + - `checked::String="[X]"|"✓"`: string to use for checked + - `unchecked::String="[ ]"|"⬚")`: string to use for unchecked + +You can create new menu types of your own. +Types that are derived from `TerminalMenus.ConfiguredMenu` configure the menu options at construction time. + +#### Legacy interface + +Prior to Julia 1.6, and still supported throughout Julia 1.x, one can also configure menus by calling +`TerminalMenus.config()`. + ## References +### REPL + ```@docs Base.atreplinit ``` + +### TerminalMenus + +#### Configuration + +```@docs +REPL.TerminalMenus.Config +REPL.TerminalMenus.MultiSelectConfig +REPL.TerminalMenus.config +``` + +#### User interaction + +```@docs +REPL.TerminalMenus.request +``` + +#### AbstractMenu extension interface + +Any subtype of `AbstractMenu` must be mutable, and must contain the fields `pagesize::Int` and +`pageoffset::Int`. +Any subtype must also implement the following functions: + +```@docs +REPL.TerminalMenus.pick +REPL.TerminalMenus.cancel +REPL.TerminalMenus.writeline +``` + +It must also implement either `options` or `numoptions`: + +```@docs +REPL.TerminalMenus.options +REPL.TerminalMenus.numoptions +``` + +If the subtype does not have a field named `selected`, it must also implement + +```@docs +REPL.TerminalMenus.selected +``` + +The following are optional but can allow additional customization: + +```@docs +REPL.TerminalMenus.header +REPL.TerminalMenus.keypress +``` diff --git a/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl b/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl index 1b935d333013f..ebbebd83671d3 100644 --- a/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl @@ -5,8 +5,6 @@ AbstractMenu The supertype for all Menu types. -See AbstractMenu.jl for descriptions of functions mentioned in this -doc string. # Functions @@ -21,12 +19,12 @@ Details can be found in ## Hidden - - `printMenu(m::AbstractMenu, cursor::Int; init::Bool=false)` + - `printmenu(m::AbstractMenu, cursor::Int; init::Bool=false, oldstate=nothing)` # Subtypes -All subtypes must contain the fields `pagesize::Int` and +All subtypes must be mutable, and must contain the fields `pagesize::Int` and `pageoffset::Int`. They must also implement the following functions. ## Necessary Functions @@ -35,8 +33,10 @@ These functions must be implemented for all subtypes of AbstractMenu. - `pick(m::AbstractMenu, cursor::Int)` - `cancel(m::AbstractMenu)` - - `options(m::AbstractMenu)` - - `writeLine(buf::IOBuffer, m::AbstractMenu, idx::Int, cur::Bool)` + - `options(m::AbstractMenu)` # `numoptions` is an alternative + - `writeline(buf::IO, m::AbstractMenu, idx::Int, iscursor::Bool)` + +If `m` does not have a field called `selected`, then you must also implement `selected(m)`. ## Optional Functions @@ -45,11 +45,17 @@ subtypes. - `header(m::AbstractMenu)` - `keypress(m::AbstractMenu, i::UInt32)` + - `numoptions(m::AbstractMenu)` + - `selected(m::AbstractMenu)` """ abstract type AbstractMenu end - +# TODO Julia2.0: get rid of parametric intermediate, making it just +# abstract type ConfiguredMenu <: AbstractMenu end +# Or perhaps just make all menus ConfiguredMenus +abstract type _ConfiguredMenu{C} <: AbstractMenu end +const ConfiguredMenu = _ConfiguredMenu{<:AbstractConfig} # NECESSARY FUNCTIONS # These functions must be implemented for all subtypes of AbstractMenu @@ -60,6 +66,7 @@ abstract type AbstractMenu end Defines what happens when a user presses the Enter key while the menu is open. If `true` is returned, `request()` will exit. +`cursor` indexes the position of the selection. """ pick(m::AbstractMenu, cursor::Int) = error("unimplemented") @@ -75,16 +82,33 @@ cancel(m::AbstractMenu) = error("unimplemented") options(m::AbstractMenu) Return a list of strings to be displayed as options in the current page. + +Alternatively, implement `numoptions`, in which case `options` is not needed. """ options(m::AbstractMenu) = error("unimplemented") """ - writeLine(buf::IOBuffer, m::AbstractMenu, idx::Int, cur::Bool) + writeline(buf::IO, m::AbstractMenu, idx::Int, iscursor::Bool) + +Write the option at index `idx` to `buf`. `iscursor`, if `true`, indicates that this +item is at the current cursor position (the one that will be selected by hitting "Enter"). + +If `m` is a `ConfiguredMenu`, `TerminalMenus` will print the cursor indicator. +Otherwise the callee is expected to handle such printing. + +!!! compat "Julia 1.6" + `writeline` requires Julia 1.6 or higher. -Write the option at index `idx` to the buffer. If cursor is `true` display the cursor. + On older versions of Julia, this was + `writeLine(buf::IO, m::AbstractMenu, idx, iscursor::Bool)` + and `m` is assumed to be unconfigured. The selection and cursor indicators can be + obtained from `TerminalMenus.CONFIG`. + + This older function is supported on all Julia 1.x versions but will be dropped in Julia 2.0. """ -function writeLine(buf::IOBuffer, m::AbstractMenu, idx::Int, cur::Bool) - error("unimplemented") +function writeline(buf::IO, m::AbstractMenu, idx::Int, iscursor::Bool) + # error("unimplemented") # TODO: use this in Julia 2.0 + writeLine(buf, m, idx, iscursor) end @@ -93,172 +117,255 @@ end ################################################################## """ - header(m::AbstractMenu) + header(m::AbstractMenu) -> String -Displays the header above the menu when it is rendered to the screen. +Returns a header string to be printed above the menu. +Defaults to "". """ header(m::AbstractMenu) = "" """ - keypress(m::AbstractMenu, i::UInt32) + keypress(m::AbstractMenu, i::UInt32) -> Bool -Send any non-standard keypress event to this function. -If `true` is returned, `request()` will exit. +Handle any non-standard keypress event. +If `true` is returned, [`TerminalMenus.request`](@ref) will exit. +Defaults to `false`. """ keypress(m::AbstractMenu, i::UInt32) = false +""" + numoptions(m::AbstractMenu) -> Int +Return the number of options in menu `m`. Defaults to `length(options(m))`. +""" +numoptions(m::AbstractMenu) = length(options(m)) +""" + selected(m::AbstractMenu) +Return information about the user-selected option. +By default it returns `m.selected`. """ - request(m::AbstractMenu) +selected(m::AbstractMenu) = m.selected -Display the menu and enter interactive mode. Returns `m.selected` which -varies based on menu type. """ -request(m::AbstractMenu) = request(terminal, m) + request(m::AbstractMenu; cursor=1) + +Display the menu and enter interactive mode. `cursor` indicates the item +number used for the initial cursor position. -function request(term::REPL.Terminals.TTYTerminal, m::AbstractMenu) - cursor = 1 +Returns `selected(m)`. +""" +request(m::AbstractMenu; kwargs...) = request(terminal, m; kwargs...) +function request(term::REPL.Terminals.TTYTerminal, m::AbstractMenu; cursor::Int=1, suppress_output=false) menu_header = header(m) - !CONFIG[:supress_output] && menu_header != "" && println(term.out_stream, menu_header) + !suppress_output && !isempty(menu_header) && println(term.out_stream, menu_header) - printMenu(term.out_stream, m, cursor, init=true) + state = nothing + if !suppress_output + state = printmenu(term.out_stream, m, cursor, init=true) + end - raw_mode_enabled = enableRawMode(term) - raw_mode_enabled && print(term.out_stream, "\x1b[?25l") # hide the cursor + raw_mode_enabled = try + REPL.Terminals.raw!(term, true) + true + catch err + @warn("TerminalMenus: Unable to enter raw mode: $err") + false + end + # hide the cursor + raw_mode_enabled && !suppress_output && print(term.out_stream, "\x1b[?25l") - lastoption = length(options(m)) try while true - c = readKey(term.in_stream) + lastoption = numoptions(m) + c = readkey(term.in_stream) if c == Int(ARROW_UP) - if cursor > 1 - cursor -= 1 # move selection up - if cursor < (2+m.pageoffset) && m.pageoffset > 0 - m.pageoffset -= 1 # scroll page up - end - elseif CONFIG[:scroll_wrap] # wrap to bottom - cursor = lastoption - m.pageoffset = lastoption - m.pagesize - end - + cursor = move_up!(m, cursor, lastoption) elseif c == Int(ARROW_DOWN) - if cursor < lastoption - cursor += 1 # move selection down - if m.pagesize + m.pageoffset <= cursor < lastoption - m.pageoffset += 1 # scroll page down - end - elseif CONFIG[:scroll_wrap] # wrap to top - cursor = 1 - m.pageoffset = 0 - end - + cursor = move_down!(m, cursor, lastoption) elseif c == Int(PAGE_UP) - # If we're at the bottom, move the page 1 less to move the cursor up from - # the bottom entry, since we try to avoid putting the cursor at bounds. - m.pageoffset -= m.pagesize - (cursor == lastoption ? 1 : 0) - m.pageoffset = max(m.pageoffset, 0) - cursor -= m.pagesize - cursor = max(cursor, 1) - + cursor = page_up!(m, cursor, lastoption) elseif c == Int(PAGE_DOWN) - m.pageoffset += m.pagesize - (cursor == 1 ? 1 : 0) - m.pageoffset = min(m.pageoffset, lastoption - m.pagesize) - cursor += m.pagesize - cursor = min(cursor, lastoption) - + cursor = page_down!(m, cursor, lastoption) elseif c == Int(HOME_KEY) cursor = 1 m.pageoffset = 0 - elseif c == Int(END_KEY) cursor = lastoption m.pageoffset = lastoption - m.pagesize - elseif c == 13 # # will break if pick returns true pick(m, cursor) && break - elseif c == UInt32('q') cancel(m) break - elseif c == 3 # ctrl-c cancel(m) - CONFIG[:ctrl_c_interrupt] ? throw(InterruptException()) : break - + ctrl_c_interrupt(m) ? throw(InterruptException()) : break else # will break if keypress returns true keypress(m, c) && break end - printMenu(term.out_stream, m, cursor) + if !suppress_output + state = printmenu(term.out_stream, m, cursor, oldstate=state) + end end finally # always disable raw mode if raw_mode_enabled - print(term.out_stream, "\x1b[?25h") # unhide cursor - disableRawMode(term) + !suppress_output && print(term.out_stream, "\x1b[?25h") # unhide cursor + REPL.Terminals.raw!(term, false) end end - println(term.out_stream) + !suppress_output && println(term.out_stream) - return m.selected + return selected(m) end """ request([term,] msg::AbstractString, m::AbstractMenu) + Shorthand for `println(msg); request(m)`. """ -request(msg::AbstractString, m::AbstractMenu) = request(terminal, msg, m) +request(msg::AbstractString, m::AbstractMenu; kwargs...) = request(terminal, msg, m; kwargs...) -function request(term::REPL.Terminals.TTYTerminal, msg::AbstractString, m::AbstractMenu) +function request(term::REPL.Terminals.TTYTerminal, msg::AbstractString, m::AbstractMenu; kwargs...) println(term.out_stream, msg) - request(term, m) + request(term, m; kwargs...) end -""" - printMenu(out, m::AbstractMenu, cursor::Int; init::Bool=false) +function move_up!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) + if cursor > 1 + cursor -= 1 # move selection up + if cursor < (2+m.pageoffset) && m.pageoffset > 0 + m.pageoffset -= 1 # scroll page up + end + elseif scroll_wrap(m) + # wrap to bottom + cursor = lastoption + m.pageoffset = lastoption - m.pagesize + end + return cursor +end + +function move_down!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) + if cursor < lastoption + cursor += 1 # move selection down + if m.pagesize + m.pageoffset <= cursor < lastoption + m.pageoffset += 1 # scroll page down + end + elseif scroll_wrap(m) + # wrap to top + cursor = 1 + m.pageoffset = 0 + end + return cursor +end + +function page_up!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) + # If we're at the bottom, move the page 1 less to move the cursor up from + # the bottom entry, since we try to avoid putting the cursor at bounds. + m.pageoffset -= m.pagesize - (cursor == lastoption ? 1 : 0) + m.pageoffset = max(m.pageoffset, 0) + return max(cursor - m.pagesize, 1) +end + +function page_down!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) + m.pageoffset += m.pagesize - (cursor == 1 ? 1 : 0) + m.pageoffset = min(m.pageoffset, lastoption - m.pagesize) + return min(cursor + m.pagesize, lastoption) +end -Display the state of a menu. """ -function printMenu(out, m::AbstractMenu, cursor::Int; init::Bool=false) - CONFIG[:supress_output] && return + printmenu(out, m::AbstractMenu, cursoridx::Int; init::Bool=false, oldstate=nothing) -> newstate - buf = IOBuffer() +Display the state of a menu. `init=true` causes `m.pageoffset` to be initialized to zero, +and starts printing at the current cursor location; when `init` is false, the terminal will +preserve the current setting of `m.pageoffset` and overwrite the previous display. +Returns `newstate`, which can be passed in as `oldstate` on the next call to allow accurate +overwriting of the previous display. + +!!! compat "Julia 1.6" + `printmenu` requires Julia 1.6 or higher. - lines = m.pagesize-1 + On older versions of Julia, this was called `printMenu` and it lacked the `state` argument/return value. + This older function is supported on all Julia 1.x versions but will be dropped in Julia 2.0. +""" +function printmenu(out, m::AbstractMenu, cursoridx::Int; oldstate=nothing, init::Bool=false) + # TODO Julia 2.0?: get rid of `init` and just use `oldstate` + buf = IOBuffer() + lastoption = numoptions(m) + ncleared = oldstate === nothing ? m.pagesize-1 : oldstate if init m.pageoffset = 0 else - # Move the cursor to the beginning of where it should print - print(buf, "\x1b[999D\x1b[$(lines)A") + print(buf, "\x1b[999D\x1b[$(ncleared)A") # move left 999 spaces and up `ncleared` lines end firstline = m.pageoffset+1 - lastline = m.pagesize+m.pageoffset + lastline = min(m.pagesize+m.pageoffset, lastoption) for i in firstline:lastline # clearline print(buf, "\x1b[2K") if i == firstline && m.pageoffset > 0 - print(buf, CONFIG[:up_arrow]) - elseif i == lastline && i != length(options(m)) - print(buf, CONFIG[:down_arrow]) + print_arrow(buf, m, up_arrow(m)) + elseif i == lastline && i != lastoption + print_arrow(buf, m, down_arrow(m)) else - print(buf, " ") + printcursor(buf, m, i == cursoridx) end - writeLine(buf, m, i, i == cursor) + writeline(buf, m, i, i == cursoridx) i != lastline && print(buf, "\r\n") end + newstate = lastline-firstline # final line doesn't have `\n` + if newstate < ncleared && oldstate !== nothing + # we printed fewer lines than last time. Erase the leftovers. + for i = newstate+1:ncleared + print(buf, "\r\n\x1b[2K") + end + print(buf, "\x1b[$(ncleared-newstate)A") + end + print(out, String(take!(buf))) + + return newstate end + +scroll_wrap(m::ConfiguredMenu) = scroll_wrap(m.config) +scroll_wrap(c::AbstractConfig) = scroll_wrap(c.config) +scroll_wrap(c::Config) = c.scroll_wrap +scroll_wrap(::AbstractMenu) = CONFIG[:scroll_wrap] + +ctrl_c_interrupt(m::ConfiguredMenu) = ctrl_c_interrupt(m.config) +ctrl_c_interrupt(c::AbstractConfig) = ctrl_c_interrupt(c.config) +ctrl_c_interrupt(c::Config) = c.ctrl_c_interrupt +ctrl_c_interrupt(::AbstractMenu) = CONFIG[:ctrl_c_interrupt] + +up_arrow(m::ConfiguredMenu) = up_arrow(m.config) +up_arrow(c::AbstractConfig) = up_arrow(c.config) +up_arrow(c::Config) = c.up_arrow +up_arrow(::AbstractMenu) = CONFIG[:up_arrow] + +down_arrow(m::ConfiguredMenu) = down_arrow(m.config) +down_arrow(c::AbstractConfig) = down_arrow(c.config) +down_arrow(c::Config) = c.down_arrow +down_arrow(::AbstractMenu) = CONFIG[:down_arrow] + +print_arrow(buf, ::ConfiguredMenu, c::Char) = print(buf, c, " ") +print_arrow(buf, ::AbstractMenu, c::Char) = print(buf, c) + +printcursor(buf, m::ConfiguredMenu, iscursor::Bool) = print(buf, ' ', iscursor ? cursor(m.config) : ' ', ' ') +cursor(c::AbstractConfig) = cursor(c.config) +cursor(c::Config) = c.cursor +printcursor(buf, ::AbstractMenu, ::Bool) = print(buf, ' ') # `writeLine` is expected to do the printing (get from CONFIG[:cursor]) diff --git a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl index 76177169384fa..9bbc658085c90 100644 --- a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl @@ -27,28 +27,32 @@ You like the following fruits: ``` """ -mutable struct MultiSelectMenu <: AbstractMenu +mutable struct MultiSelectMenu{C} <: _ConfiguredMenu{C} options::Array{String,1} pagesize::Int pageoffset::Int selected::Set{Int} + config::C end """ - MultiSelectMenu(options::Array{String,1}; pagesize::Int=10) + MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, selected=[], kwargs...) Create a MultiSelectMenu object. Use `request(menu::MultiSelectMenu)` to get -user input. `request()` returns a `Set` containing the indices of options that +user input. It returns a `Set` containing the indices of options that were selected by the user. # Arguments - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize + - `selected=[]`: pre-selected items. `i ∈ selected` means that `options[i]` is preselected. + +Any additional keyword arguments will be passed to [`TerminalMenus.MultiSelectConfig`](@ref). """ -function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10) +function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, selected=Int[], warn::Bool=true, kwargs...) length(options) < 2 && error("MultiSelectMenu must have at least two options") # if pagesize is -1, use automatic paging @@ -59,9 +63,18 @@ function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10) pagesize < 2 && error("pagesize must be >= 2") pageoffset = 0 - selected = Set{Int}() # none + _selected = Set{Int}() + for item in selected + push!(_selected, item) + end + + if !isempty(kwargs) + MultiSelectMenu(options, pagesize, pageoffset, _selected, MultiSelectConfig(; kwargs...)) + else + warn && Base.depwarn("Legacy `MultiSelectMenu` interface is deprecated, set a configuration option such as `MultiSelectMenu(options; charset=:ascii)` to trigger the new interface.", :MultiSelectMenu) + MultiSelectMenu(options, pagesize, pageoffset, _selected, CONFIG) + end - MultiSelectMenu(options, pagesize, pageoffset, selected) end @@ -87,13 +100,11 @@ function pick(menu::MultiSelectMenu, cursor::Int) return false #break out of the menu end -function writeLine(buf::IOBuffer, menu::MultiSelectMenu, idx::Int, cursor::Bool) - # print a ">" on the selected entry - cursor ? print(buf, CONFIG[:cursor]," ") : print(buf, " ") +function writeline(buf::IOBuffer, menu::MultiSelectMenu{MultiSelectConfig}, idx::Int, iscursor::Bool) if idx in menu.selected - print(buf, CONFIG[:checked], " ") + print(buf, menu.config.checked, " ") else - print(buf, CONFIG[:unchecked], " ") + print(buf, menu.config.unchecked, " ") end print(buf, replace(menu.options[idx], "\n" => "\\n")) @@ -112,3 +123,17 @@ function keypress(menu::MultiSelectMenu, key::UInt32) end false # don't break end + + +## Legacy interface +function TerminalMenus.writeLine(buf::IOBuffer, menu::MultiSelectMenu{<:Dict}, idx::Int, cursor::Bool) + # print a ">" on the selected entry + cursor ? print(buf, menu.config[:cursor]," ") : print(buf, " ") + if idx in menu.selected + print(buf, menu.config[:checked], " ") + else + print(buf, menu.config[:unchecked], " ") + end + + print(buf, replace(menu.options[idx], "\n" => "\\n")) +end diff --git a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl index 5c8137ff0e61d..6b777e058e975 100644 --- a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl @@ -19,17 +19,18 @@ Your favorite fruit is blueberry! ``` """ -mutable struct RadioMenu <: AbstractMenu +mutable struct RadioMenu{C} <: _ConfiguredMenu{C} options::Array{String,1} pagesize::Int pageoffset::Int selected::Int + config::C end """ - RadioMenu(options::Array{String,1}; pagesize::Int=10) + RadioMenu(options::Array{String,1}; pagesize::Int=10, kwargs...) Create a RadioMenu object. Use `request(menu::RadioMenu)` to get user input. `request()` returns an `Int` which is the index of the option selected by the @@ -39,8 +40,10 @@ user. - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize + +Any additional keyword arguments will be passed to [`TerminalMenus.Config`](@ref). """ -function RadioMenu(options::Array{String,1}; pagesize::Int=10) +function RadioMenu(options::Array{String,1}; pagesize::Int=10, warn::Bool=true, kwargs...) length(options) < 2 && error("RadioMenu must have at least two options") # if pagesize is -1, use automatic paging @@ -53,7 +56,12 @@ function RadioMenu(options::Array{String,1}; pagesize::Int=10) pageoffset = 0 selected = -1 # none - RadioMenu(options, pagesize, pageoffset, selected) + if !isempty(kwargs) + RadioMenu(options, pagesize, pageoffset, selected, Config(; kwargs...)) + else + warn && Base.depwarn("Legacy `RadioMenu` interface is deprecated, set a configuration option such as `RadioMenu(options; charset=:ascii)` to trigger the new interface.", :RadioMenu) + RadioMenu(options, pagesize, pageoffset, selected, CONFIG) + end end @@ -71,9 +79,14 @@ function pick(menu::RadioMenu, cursor::Int) return true #break out of the menu end -function writeLine(buf::IOBuffer, menu::RadioMenu, idx::Int, cursor::Bool) +function writeline(buf::IOBuffer, menu::RadioMenu{Config}, idx::Int, iscursor::Bool) + print(buf, replace(menu.options[idx], "\n" => "\\n")) +end + +# Legacy interface +function writeLine(buf::IOBuffer, menu::RadioMenu{<:Dict}, idx::Int, cursor::Bool) # print a ">" on the selected entry - cursor ? print(buf, CONFIG[:cursor] ," ") : print(buf, " ") + cursor ? print(buf, menu.config[:cursor] ," ") : print(buf, " ") print(buf, replace(menu.options[idx], "\n" => "\\n")) end diff --git a/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl b/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl index 97e06d579d7df..d9d3dc8598f7d 100644 --- a/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl +++ b/stdlib/REPL/src/TerminalMenus/TerminalMenus.jl @@ -23,4 +23,12 @@ export MultiSelectMenu, request +# TODO: remove in Julia 2.0 +# While not exported, AbstractMenu documented these as an extension interface +@deprecate printMenu printmenu +function writeLine(buf::IOBuffer, m::AbstractMenu, idx::Int, cursor::Bool) + Base.depwarn("`writeLine` is deprecated, use `writeline` instead.", :writeLine) + error("unimplemented") +end + end # module diff --git a/stdlib/REPL/src/TerminalMenus/config.jl b/stdlib/REPL/src/TerminalMenus/config.jl index d84a14aac7595..9aded1ee0a186 100644 --- a/stdlib/REPL/src/TerminalMenus/config.jl +++ b/stdlib/REPL/src/TerminalMenus/config.jl @@ -1,7 +1,113 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -"""global menu configuration parameters""" -CONFIG = Dict() +abstract type AbstractConfig end + +struct Config <: AbstractConfig + cursor::Char + up_arrow::Char + down_arrow::Char + scroll_wrap::Bool + ctrl_c_interrupt::Bool +end + +struct MultiSelectConfig <: AbstractConfig + config::Config + checked::String + unchecked::String +end + +""" + Config(; scroll_wrap=false, ctrl_c_interrupt=true, charset=:ascii, cursor::Char, up_arrow::Char, down_arrow::Char) + +Configure behavior for selection menus via keyword arguments: + +- `scroll_wrap`, if `true`, causes the menu to wrap around when scrolling above the first + or below the last entry +- `ctrl_c_interrupt`, if `true`, throws an `InterruptException` if the user hits Ctrl-C + during menu selection. If `false`, [`TerminalMenus.request`](@ref) will return the + default result from [`TerminalMenus.selected`](@ref). +- `charset` affects the default values for `cursor`, `up_arrow`, and `down_arrow`, + and can be `:ascii` or `:unicode` +- `cursor` is the character printed to indicate the option that will be chosen by + hitting "Enter." Defaults are '>' or '→', depending on `charset`. +- `up_arrow` is the character printed when the display does not include the first entry. + Defaults are '^' or '↑', depending on `charset`. +- `down_arrow` is the character printed when the display does not include the last entry. + Defaults are 'v' or '↓', depending on `charset`. + +Subtypes of `ConfiguredMenu` will print `cursor`, `up_arrow`, and `down_arrow` automatically +as needed, your `writeline` method should not print them. + +!!! compat Julia 1.6 + `Config` is available as of Julia 1.6. On older releases use the global `CONFIG`. +""" +function Config(; + charset::Symbol = :ascii, + cursor::Char = '\0', + up_arrow::Char = '\0', + down_arrow::Char = '\0', + scroll_wrap::Bool = false, + ctrl_c_interrupt::Bool = true) + charset === :ascii || charset === :unicode || + throw(ArgumentError("charset should be :ascii or :unicode, received $charset")) + if cursor == '\0' + cursor = charset === :ascii ? '>' : '→' + end + if up_arrow == '\0' + up_arrow = charset === :ascii ? '^' : '↑' + end + if down_arrow == '\0' + down_arrow = charset === :ascii ? 'v' : '↓' + end + return Config(cursor, up_arrow, down_arrow, scroll_wrap, ctrl_c_interrupt) +end + +""" + MultiSelectConfig(; charset=:ascii, checked::String, unchecked::String, kwargs...) + +Configure behavior for a multiple-selection menu via keyword arguments: + +- `checked` is the string to print when an option has been selected. + Defaults are "[X]" or "✓", depending on `charset`. +- `unchecked` is the string to print when an option has not been selected. + Defaults are "[ ]" or "⬚", depending on `charset`. + +All other keyword arguments are as described for [`TerminalMenus.Config`](@ref). +`checked` and `unchecked` are not printed automatically, and should be printed by +your `writeline` method. + +!!! compat Julia 1.6 + `MultiSelectConfig` is available as of Julia 1.6. On older releases use the global `CONFIG`. +""" +function MultiSelectConfig(; + charset::Symbol = :ascii, + checked::String = "", + unchecked::String = "", + kwargs...) + charset === :ascii || charset === :unicode || throw(ArgumentError("charset should be :ascii or :unicode, received $charset")) + if isempty(checked) + checked = charset === :ascii ? "[X]" : "✓" + end + if isempty(unchecked) + unchecked = charset === :ascii ? "[ ]" : "⬚" + end + return MultiSelectConfig(Config(; charset=charset, kwargs...), checked, unchecked) +end + + + +## Below is the old-style CONFIG interface, kept for backwards compatibility. +## Not recommended for any new menu types. + +""" + CONFIG + +Global menu configuration parameters + +!!! compat Julia 1.6 + `CONFIG` is deprecated, instead configure menus via their constructors. +""" +const CONFIG = Dict{Symbol,Union{Char,String,Bool}}() """ config( ) @@ -16,8 +122,11 @@ Keyword-only function to configure global menu parameters - `checked::String="[X]"|"✓"`: string to use for checked - `unchecked::String="[ ]"|"⬚")`: string to use for unchecked - `scroll::Symbol=:nowrap`: If `:wrap` wrap cursor around top and bottom, if :`nowrap` do not wrap cursor - - `supress_output::Bool=false`: For testing. If true, menu will not be printed to console. + - `supress_output::Bool=false`: Ignored legacy argument, pass `suppress_output` as a keyword argument to `request` instead. - `ctrl_c_interrupt::Bool=true`: If `false`, return empty on ^C, if `true` throw InterruptException() on ^C + +!!! compat Julia 1.6 + As of Julia 1.6, `config` is deprecated. Use `Config` or `MultiSelectConfig` instead. """ function config(;charset::Symbol = :na, scroll::Symbol = :na, @@ -26,7 +135,7 @@ function config(;charset::Symbol = :na, down_arrow::Char = '\0', checked::String = "", unchecked::String = "", - supress_output::Union{Nothing, Bool}=nothing, + supress_output::Union{Nothing, Bool}=nothing, # typo was documented, unfortunately ctrl_c_interrupt::Union{Nothing, Bool}=nothing) if charset === :ascii @@ -55,7 +164,7 @@ function config(;charset::Symbol = :na, down_arrow != '\0' && (CONFIG[:down_arrow] = down_arrow) checked != "" && (CONFIG[:checked] = checked) unchecked != "" && (CONFIG[:unchecked] = unchecked) - supress_output isa Bool && (CONFIG[:supress_output] = supress_output) + supress_output isa Bool && (CONFIG[:supress_output] = supress_output) ctrl_c_interrupt isa Bool && (CONFIG[:ctrl_c_interrupt] = ctrl_c_interrupt) return nothing diff --git a/stdlib/REPL/src/TerminalMenus/util.jl b/stdlib/REPL/src/TerminalMenus/util.jl index 84bc465529670..68d98dd57c199 100644 --- a/stdlib/REPL/src/TerminalMenus/util.jl +++ b/stdlib/REPL/src/TerminalMenus/util.jl @@ -12,54 +12,30 @@ PAGE_UP, PAGE_DOWN) -# Enable raw mode. Allows us to process keyboard inputs directly. -function enableRawMode(term) - try - REPL.Terminals.raw!(term, true) - return true - catch err - @warn("TerminalMenus: Unable to enter raw mode: $err") - end - return false -end - -# Disable raw mode. Give control back to Julia REPL if interactive session. -function disableRawMode(term) - try - REPL.Terminals.raw!(term, false) - return true - catch err - @warn("TerminalMenus: Unable to disable raw mode: $err") - end - return false -end - - -# Reads a single byte from stdin -readNextChar(stream::IO=stdin) = Char(read(stream,1)[1]) +readbyte(stream::IO=stdin) = Char(read(stream,1)[1]) # Read the next key from stdin. It is also able to read several bytes for # escaped keys such as the arrow keys, home/end keys, etc. # Escaped keys are returned using the `Key` enum. -readKey(stream::IO=stdin) = UInt32(_readKey(stream)) -function _readKey(stream::IO=stdin) - c = readNextChar(stream) +readkey(stream::IO=stdin) = UInt32(_readkey(stream)) +function _readkey(stream::IO=stdin) + c = readbyte(stream) # Escape characters if c == '\x1b' stream.buffer.size < 2 && return '\x1b' - esc_a = readNextChar(stream) + esc_a = readbyte(stream) esc_a == 'v' && return PAGE_UP # M-v esc_a == '<' && return HOME_KEY # M-< esc_a == '>' && return END_KEY # M-> stream.buffer.size < 3 && return '\x1b' - esc_b = readNextChar(stream) + esc_b = readbyte(stream) if esc_a == '[' || esc_a == 'O' if esc_b >= '0' && esc_b <= '9' stream.buffer.size < 4 && return '\x1b' - esc_c = readNextChar(stream) + esc_c = readbyte(stream) if esc_c == '~' esc_b == '1' && return HOME_KEY esc_b == '4' && return END_KEY diff --git a/stdlib/REPL/test/TerminalMenus/dynamic_menu.jl b/stdlib/REPL/test/TerminalMenus/dynamic_menu.jl new file mode 100644 index 0000000000000..23d026358385f --- /dev/null +++ b/stdlib/REPL/test/TerminalMenus/dynamic_menu.jl @@ -0,0 +1,118 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using REPL.TerminalMenus, Test + +mutable struct DynamicMenu <: TerminalMenus._ConfiguredMenu{TerminalMenus.Config} + pagesize::Int + pageoffset::Int + selected::Int + numopts::Int + config::TerminalMenus.Config +end + +TerminalMenus.numoptions(m::DynamicMenu) = m.numopts +function TerminalMenus.writeline(buf::IO, m::DynamicMenu, idx::Int, iscursor::Bool) + print(buf, idx) + iscursor && print(buf, '*') +end + +matchback(str) = match(r"\e\[(\d+)A", str) + +function linesplitter(str) + strs = split(str, '\n') + s1 = strs[1] + m = matchback(s1) + if m === nothing + nback, startidx = 0, 1 + else + nback = parse(Int, m.captures[1]) + startidx = m.offset+length(m.match) + end + strs[1] = s1[startidx:end] # discard the portion that moves the terminal + @test all(s->startswith(s, "\e[2K"), strs) + return nback, replace.(map(s->s[5:end], strs), ('\r'=>"",)) +end + +io = IOBuffer() + +menu = DynamicMenu(4, 0, -1, 8, TerminalMenus.Config()) + +cursor = 1 +state = TerminalMenus.printmenu(io, menu, cursor; init=true) +str = String(take!(io)) +@test count(isequal('\n'), str) == state +nback, strs = linesplitter(str) +@test nback == 0 +@test strs == [" > 1*", " 2", " 3", "v 4"] + +cursor = TerminalMenus.move_down!(menu, cursor) +@test cursor == 2 +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +@test count(isequal('\n'), str) == state +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == [" 1", " > 2*", " 3", "v 4"] + +menu.numopts = 3 # dynamically changing the number of options +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test count(isequal('\n'), str) - parse(Int, matchback(strs[end]).captures[1]) == state +@test strs[1:end-1] == [" 1", " > 2*", " 3"] + +menu.numopts = 6 +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 2 +@test strs == [" 1", " > 2*", " 3", "v 4"] + +cursor = TerminalMenus.move_down!(menu, cursor) +cursor = TerminalMenus.move_down!(menu, cursor) +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == ["^ 2", " 3", " > 4*", "v 5"] + +cursor = TerminalMenus.move_down!(menu, cursor) +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == ["^ 3", " 4", " > 5*", " 6"] + +cursor = TerminalMenus.move_down!(menu, cursor) +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == ["^ 3", " 4", " 5", " > 6*"] + +cursor = TerminalMenus.move_up!(menu, cursor) +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == ["^ 3", " 4", " > 5*", " 6"] + +cursor = TerminalMenus.page_up!(menu, cursor) +@test cursor == 1 +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == [" > 1*", " 2", " 3", "v 4"] + +cursor = TerminalMenus.move_down!(menu, cursor) +cursor = TerminalMenus.move_down!(menu, cursor) +cursor = TerminalMenus.move_down!(menu, cursor) +cursor = TerminalMenus.page_down!(menu, cursor) +@test cursor == menu.numopts +state = TerminalMenus.printmenu(io, menu, cursor; oldstate=state) +str = String(take!(io)) +nback, strs = linesplitter(str) +@test nback == 3 +@test strs == ["^ 3", " 4", " 5", " > 6*"] diff --git a/stdlib/REPL/test/TerminalMenus/legacytests/config.jl b/stdlib/REPL/test/TerminalMenus/legacytests/config.jl new file mode 100644 index 0000000000000..6654da261cc0e --- /dev/null +++ b/stdlib/REPL/test/TerminalMenus/legacytests/config.jl @@ -0,0 +1,15 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license +# This file tests the Julia 1.0-1.5 extension interface of TerminalMenus + +# scroll must only accept symbols +@test_throws TypeError TerminalMenus.config(scroll=true) +# :foo is not a valid scroll option +@test_throws ArgumentError TerminalMenus.config(scroll=:foo) +# Test scroll wrap +TerminalMenus.config(scroll=:wrap) +@test TerminalMenus.CONFIG[:scroll_wrap] == true +# Updating some params shouldn't change other ones +TerminalMenus.config(charset=:ascii) +@test TerminalMenus.CONFIG[:scroll_wrap] == true +TerminalMenus.config(scroll=:nowrap) +@test TerminalMenus.CONFIG[:scroll_wrap] == false diff --git a/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl b/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl new file mode 100644 index 0000000000000..0c1bd08940289 --- /dev/null +++ b/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl @@ -0,0 +1,39 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license +# This file tests the legacy Julia 1.0-1.5 extension interface of TerminalMenus +# They are run with `warn=false` to avoid triggering test failures. + +# Check to make sure types are imported properly +@test MultiSelectMenu <: TerminalMenus.AbstractMenu + +# Invalid Menu Params +@test_throws ErrorException MultiSelectMenu(["one"], warn=false) +@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1, warn=false) + +# Constructor +@test MultiSelectMenu(["one", "two", "three"], warn=false).pagesize == 3 +@test MultiSelectMenu(string.(1:30), pagesize=-1, warn=false).pagesize == 30 +@test MultiSelectMenu(string.(1:4), pagesize=10, warn=false).pagesize == 4 +@test MultiSelectMenu(string.(1:100), warn=false).pagesize == 10 + +multi_menu = MultiSelectMenu(string.(1:20), warn=false) +@test TerminalMenus.options(multi_menu) == string.(1:20) +@test TerminalMenus.header(multi_menu) == "[press: d=done, a=all, n=none]" + +# Output +TerminalMenus.config() # Use default chars +CONFIG = TerminalMenus.CONFIG + +multi_menu = MultiSelectMenu(string.(1:10), warn=false) +buf = IOBuffer() +TerminalMenus.writeLine(buf, multi_menu, 1, true) +@test String(take!(buf)) == string(CONFIG[:cursor], " ", CONFIG[:unchecked], " 1") +TerminalMenus.config(cursor='+') +TerminalMenus.writeLine(buf, multi_menu, 1, true) +@test String(take!(buf)) == string("+ ", CONFIG[:unchecked], " 1") +TerminalMenus.config(charset=:unicode) +TerminalMenus.writeLine(buf, multi_menu, 1, true) +@test String(take!(buf)) == string(CONFIG[:cursor], " ", CONFIG[:unchecked], " 1") + +# Test SDTIN +multi_menu = MultiSelectMenu(string.(1:10), warn=false) +@test simulate_input(Set([1,2]), multi_menu, :enter, :down, :enter, 'd') diff --git a/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl b/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl new file mode 100644 index 0000000000000..05038af460835 --- /dev/null +++ b/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl @@ -0,0 +1,42 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license +# This file tests the legacy Julia 1.0-1.5 extension interface of TerminalMenus +# They are run with `warn=false` to avoid triggering test failures. + +# Check to make sure types are imported properly +@test RadioMenu <: TerminalMenus.AbstractMenu + +# Invalid Menu Params +@test_throws ErrorException RadioMenu(["one"], warn=false) +@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1, warn=false) + +# Constructor +@test RadioMenu(["one", "two", "three"], warn=false).pagesize == 3 +@test RadioMenu(string.(1:30), pagesize=-1, warn=false).pagesize == 30 +@test RadioMenu(string.(1:4), pagesize=10, warn=false).pagesize == 4 +@test RadioMenu(string.(1:100), warn=false).pagesize == 10 + +radio_menu = RadioMenu(string.(1:20), warn=false) +@test TerminalMenus.options(radio_menu) == string.(1:20) +radio_menu.selected = 2 +TerminalMenus.cancel(radio_menu) +@test radio_menu.selected == -1 +@test TerminalMenus.header(radio_menu) == "" + +# Output +TerminalMenus.config() # Use default chars +CONFIG = TerminalMenus.CONFIG + +radio_menu = RadioMenu(string.(1:10), warn=false) +buf = IOBuffer() +TerminalMenus.writeLine(buf, radio_menu, 1, true) +@test String(take!(buf)) == string(CONFIG[:cursor], " 1") +TerminalMenus.config(cursor='+') +TerminalMenus.writeLine(buf, radio_menu, 1, true) +@test String(take!(buf)) == "+ 1" +TerminalMenus.config(charset=:unicode) +TerminalMenus.writeLine(buf, radio_menu, 1, true) +@test String(take!(buf)) == string(CONFIG[:cursor], " 1") + +# Test using stdin +radio_menu = RadioMenu(string.(1:10), warn=false) +@test simulate_input(3, radio_menu, :down, :down, :enter) diff --git a/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl b/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl index 5fb97c91bf00b..bfeb27590baf0 100644 --- a/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl @@ -1,37 +1,59 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# This file tests the new Julia 1.6+ extension interface of TerminalMenus +# To trigger the new interface, at least one configuration keyword argument must be supplied. + # Check to make sure types are imported properly -@test MultiSelectMenu <: TerminalMenus.AbstractMenu +@test MultiSelectMenu{TerminalMenus.MultiSelectConfig} <: TerminalMenus.ConfiguredMenu # TODO Julia 2.0: delete parameter # Invalid Menu Params -@test_throws ErrorException MultiSelectMenu(["one"]) -@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1) +@test_throws ErrorException MultiSelectMenu(["one"], charset=:ascii) +@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1, charset=:ascii) # Constructor -@test MultiSelectMenu(["one", "two", "three"]).pagesize == 3 -@test MultiSelectMenu(string.(1:30), pagesize=-1).pagesize == 30 -@test MultiSelectMenu(string.(1:4), pagesize=10).pagesize == 4 -@test MultiSelectMenu(string.(1:100)).pagesize == 10 +@test MultiSelectMenu(["one", "two", "three"], charset=:ascii).pagesize == 3 +@test MultiSelectMenu(string.(1:30), pagesize=-1, charset=:ascii).pagesize == 30 +@test MultiSelectMenu(string.(1:4), pagesize=10, charset=:ascii).pagesize == 4 +@test MultiSelectMenu(string.(1:100), charset=:ascii).pagesize == 10 -multi_menu = MultiSelectMenu(string.(1:20)) +multi_menu = MultiSelectMenu(string.(1:20), charset=:ascii) @test TerminalMenus.options(multi_menu) == string.(1:20) @test TerminalMenus.header(multi_menu) == "[press: d=done, a=all, n=none]" # Output -TerminalMenus.config() # Use default chars -CONFIG = TerminalMenus.CONFIG +for kws in ((charset=:ascii,), + (charset=:unicode,), + (cursor='@', checked="c", unchecked="u",)) + local multi_menu + multi_menu = MultiSelectMenu(string.(1:10); kws...) + cur = isdefined(kws, :cursor) ? kws.cursor : (kws.charset === :ascii ? '>' : '→') + chk = isdefined(kws, :checked) ? kws.checked : (kws.charset === :ascii ? "[X]" : "✓") + uck = isdefined(kws, :unchecked) ? kws.unchecked : (kws.charset === :ascii ? "[ ]" : "⬚") + + buf = IOBuffer() + TerminalMenus.writeline(buf, multi_menu, 1, true) + @test String(take!(buf)) == "$uck 1" + TerminalMenus.printmenu(buf, multi_menu, 1; init=true) + @test startswith(String(take!(buf)), string("\e[2K $cur $uck 1")) + push!(multi_menu.selected, 1) + TerminalMenus.printmenu(buf, multi_menu, 2; init=true) + @test startswith(String(take!(buf)), string("\e[2K $chk 1\r\n\e[2K $cur $uck 2")) +end -multi_menu = MultiSelectMenu(string.(1:10)) +# Preselection +sel = [2,5] +multi_menu = MultiSelectMenu(string.(1:20), pagesize=6, selected=sel, charset=:ascii) buf = IOBuffer() -TerminalMenus.writeLine(buf, multi_menu, 1, true) -@test String(take!(buf)) == string(CONFIG[:cursor], " ", CONFIG[:unchecked], " 1") -TerminalMenus.config(cursor='+') -TerminalMenus.writeLine(buf, multi_menu, 1, true) -@test String(take!(buf)) == string("+ ", CONFIG[:unchecked], " 1") -TerminalMenus.config(charset=:unicode) -TerminalMenus.writeLine(buf, multi_menu, 1, true) -@test String(take!(buf)) == string(CONFIG[:cursor], " ", CONFIG[:unchecked], " 1") +TerminalMenus.printmenu(buf, multi_menu, 1; init=true) +str = String(take!(buf)) +for i = 1:multi_menu.pagesize + if i ∈ sel + @test occursin("[X] $i", str) + else + @test occursin("[ ] $i", str) + end +end # Test SDTIN -multi_menu = MultiSelectMenu(string.(1:10)) -@test simulateInput(Set([1,2]), multi_menu, :enter, :down, :enter, 'd') +multi_menu = MultiSelectMenu(string.(1:10), charset=:ascii) +@test simulate_input(Set([1,2]), multi_menu, :enter, :down, :enter, 'd') diff --git a/stdlib/REPL/test/TerminalMenus/radio_menu.jl b/stdlib/REPL/test/TerminalMenus/radio_menu.jl index d94622b3efb8d..a45bb39acb7b1 100644 --- a/stdlib/REPL/test/TerminalMenus/radio_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/radio_menu.jl @@ -1,19 +1,22 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# This file tests the new Julia 1.6+ extension interface of TerminalMenus +# To trigger the new interface, at least one configuration keyword argument must be supplied. + # Check to make sure types are imported properly -@test RadioMenu <: TerminalMenus.AbstractMenu +@test RadioMenu{TerminalMenus.Config} <: TerminalMenus.ConfiguredMenu # TODO Julia 2.0: delete parameter # Invalid Menu Params -@test_throws ErrorException RadioMenu(["one"]) -@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1) +@test_throws ErrorException RadioMenu(["one"]; charset=:ascii) +@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1, charset=:ascii) # Constructor -@test RadioMenu(["one", "two", "three"]).pagesize == 3 -@test RadioMenu(string.(1:30), pagesize=-1).pagesize == 30 -@test RadioMenu(string.(1:4), pagesize=10).pagesize == 4 -@test RadioMenu(string.(1:100)).pagesize == 10 +@test RadioMenu(["one", "two", "three"]; charset=:ascii).pagesize == 3 +@test RadioMenu(string.(1:30), pagesize=-1, charset=:ascii).pagesize == 30 +@test RadioMenu(string.(1:4), pagesize=10, charset=:ascii).pagesize == 4 +@test RadioMenu(string.(1:100); charset=:ascii).pagesize == 10 -radio_menu = RadioMenu(string.(1:20)) +radio_menu = RadioMenu(string.(1:20); charset=:ascii) @test TerminalMenus.options(radio_menu) == string.(1:20) radio_menu.selected = 2 TerminalMenus.cancel(radio_menu) @@ -21,20 +24,21 @@ TerminalMenus.cancel(radio_menu) @test TerminalMenus.header(radio_menu) == "" # Output -TerminalMenus.config() # Use default chars -CONFIG = TerminalMenus.CONFIG - -radio_menu = RadioMenu(string.(1:10)) -buf = IOBuffer() -TerminalMenus.writeLine(buf, radio_menu, 1, true) -@test String(take!(buf)) == string(CONFIG[:cursor], " 1") -TerminalMenus.config(cursor='+') -TerminalMenus.writeLine(buf, radio_menu, 1, true) -@test String(take!(buf)) == "+ 1" -TerminalMenus.config(charset=:unicode) -TerminalMenus.writeLine(buf, radio_menu, 1, true) -@test String(take!(buf)) == string(CONFIG[:cursor], " 1") +for kws in ((charset=:ascii,), + (charset=:unicode,), + (cursor='@',)) + local radio_menu + radio_menu = RadioMenu(string.(1:10); kws...) + c = isdefined(kws, :cursor) ? kws.cursor : (kws.charset === :ascii ? '>' : '→') + buf = IOBuffer() + TerminalMenus.writeline(buf, radio_menu, 1, true) + @test String(take!(buf)) == "1" + TerminalMenus.printmenu(buf, radio_menu, 1; init=true) + @test startswith(String(take!(buf)), "\e[2K $c 1") + TerminalMenus.printmenu(buf, radio_menu, 2; init=true) + @test startswith(String(take!(buf)), string("\e[2K 1\r\n\e[2K $c 2")) +end # Test using stdin -radio_menu = RadioMenu(string.(1:10)) -@test simulateInput(3, radio_menu, :down, :down, :enter) +radio_menu = RadioMenu(string.(1:10); charset=:ascii) +@test simulate_input(3, radio_menu, :down, :down, :enter) diff --git a/stdlib/REPL/test/TerminalMenus/runtests.jl b/stdlib/REPL/test/TerminalMenus/runtests.jl index a695e46ee1c35..d4e1b5c1a83bd 100644 --- a/stdlib/REPL/test/TerminalMenus/runtests.jl +++ b/stdlib/REPL/test/TerminalMenus/runtests.jl @@ -4,12 +4,7 @@ import REPL using REPL.TerminalMenus using Test -TerminalMenus.config(supress_output=true) - -function simulateInput(expectedResult, menu::TerminalMenus.AbstractMenu, keys...) - # If we cannot write to the buffer, skip the test - !(:buffer in fieldnames(typeof(stdin))) && return true - +function simulate_input(expected, menu::TerminalMenus.AbstractMenu, keys...) keydict = Dict(:up => "\e[A", :down => "\e[B", :enter => "\r") @@ -22,23 +17,14 @@ function simulateInput(expectedResult, menu::TerminalMenus.AbstractMenu, keys... end end - request(menu) == expectedResult + request(menu; suppress_output=true) == expected end include("radio_menu.jl") include("multiselect_menu.jl") +include("dynamic_menu.jl") -# Other test - -# scroll must only accept symbols -@test_throws TypeError TerminalMenus.config(scroll=true) -# :foo is not a valid scroll option -@test_throws ArgumentError TerminalMenus.config(scroll=:foo) -# Test scroll wrap -TerminalMenus.config(scroll=:wrap) -@test TerminalMenus.CONFIG[:scroll_wrap] == true -# Updating some params shouldn't change other ones -TerminalMenus.config(charset=:ascii) -@test TerminalMenus.CONFIG[:scroll_wrap] == true -TerminalMenus.config(scroll=:nowrap) -@test TerminalMenus.CONFIG[:scroll_wrap] == false +# Legacy tests +include("legacytests/old_radio_menu.jl") +include("legacytests/old_multiselect_menu.jl") +include("legacytests/config.jl") From d20e34f16309328f1ac731db5b0fe57656f9fce2 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 9 Jun 2020 13:54:32 +0200 Subject: [PATCH 140/232] Normalize (simplify) `UnionAll`s when used as type parameter --- src/jltypes.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ test/subtype.jl | 2 +- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/jltypes.c b/src/jltypes.c index bba9362e9a64f..f1845ae68c0d1 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1231,6 +1231,88 @@ static jl_value_t *normalize_vararg(jl_value_t *va) return va; } +int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_count) JL_NOTSAFEPOINT +{ + if (v == (jl_value_t*)var) { + if (inside_inv) { + return 0; + } else { + (*cov_count)++; + return *cov_count <= 1 || jl_is_concrete_type(var->ub); + } + } + else if (jl_is_uniontype(v)) { + return _may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count) && + _may_substitute_ub(((jl_uniontype_t*)v)->b, var, inside_inv, cov_count); + } + else if (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + if (ua->var == var) + return 1; + return _may_substitute_ub(ua->var->lb, var, inside_inv, cov_count) && + _may_substitute_ub(ua->var->ub, var, inside_inv, cov_count) && + _may_substitute_ub(ua->body, var, inside_inv, cov_count); + } + else if (jl_is_datatype(v)) { + int istuple = jl_is_tuple_type(v); + int isva = jl_is_vararg_type(v); + for (size_t i = 0; i < jl_nparams(v); i++) { + int invar = isva ? i == 1 : !istuple; + int ins_i = inside_inv || invar; + int old_count = *cov_count; + if (!_may_substitute_ub(jl_tparam(v,i), var, ins_i, cov_count)) + return 0; + if (isva && i == 0 && *cov_count > old_count && !jl_is_concrete_type(var->ub)) + return 0; + } + return 1; + } + return 1; +} + +// Check whether `var` may be replaced with its upper bound `ub` in `v where var<:ub` +// Conditions: +// * `var` does not appear in invariant position +// * `var` appears at most once (in covariant position) and not in a `Vararg` +// unless the upper bound is concrete (diagonal rule) +int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT +{ + int cov_count = 0; + return _may_substitute_ub(v, var, 0, &cov_count); +} + +jl_value_t *normalize_unionalls(jl_value_t *t) +{ + JL_GC_PUSH1(&t); + if (jl_is_uniontype(t)) { + jl_uniontype_t *u = (jl_uniontype_t *) t; + jl_value_t *a = NULL; + jl_value_t *b = NULL; + JL_GC_PUSH2(&a, &b); + a = normalize_unionalls(u->a); + b = normalize_unionalls(u->b); + if (a != u->a || b != u->b) { + t = jl_new_struct(jl_uniontype_type, a, b); + } + JL_GC_POP(); + } + else if (jl_is_unionall(t)) { + jl_unionall_t *u = (jl_unionall_t *) t; + jl_value_t *body = normalize_unionalls(u->body); + if (body != u->body) { + JL_GC_PUSH1(&body); + t = jl_new_struct(jl_unionall_type, u->var, body); + JL_GC_POP(); + u = (jl_unionall_t *) t; + } + + if (u->var->lb == u->var->ub || may_substitute_ub(body, u->var)) + t = jl_instantiate_unionall(u, u->var->ub); + } + JL_GC_POP(); + return t; +} + static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev, jl_typestack_t *stack); static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, @@ -1240,6 +1322,11 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_typename_t *tn = dt->name; int istuple = (tn == jl_tuple_typename); int isnamedtuple = (tn == jl_namedtuple_typename); + if (dt->name != jl_type_typename) { + for (size_t i = 0; i < ntp; i++) + iparams[i] = normalize_unionalls(iparams[i]); + } + // check type cache if (cacheable) { size_t i; diff --git a/test/subtype.jl b/test/subtype.jl index d528a2230fca9..5fa0b7b8d73fd 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -140,7 +140,7 @@ function test_diagonal() @test !issub(Type{Tuple{T,Any} where T}, Type{Tuple{T,T}} where T) @test !issub(Type{Tuple{T,Any,T} where T}, Type{Tuple{T,T,T}} where T) @test_broken issub(Type{Tuple{T} where T}, Type{Tuple{T}} where T) - @test_broken issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T) + @test issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T) @test !issub(Type{Tuple{T,T} where T}, Type{Tuple{T,T}} where T) @test !issub(Type{Tuple{T,T,T} where T}, Type{Tuple{T,T,T}} where T) @test isequal_type(Ref{Tuple{T, T} where Int<:T<:Int}, From 4e2fb5c72c28bf541492ada0036dbf551b11868b Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 9 Jun 2020 14:13:41 +0200 Subject: [PATCH 141/232] Revert "Normalize (simplify) `UnionAll`s when used as type parameter" This reverts commit d20e34f16309328f1ac731db5b0fe57656f9fce2. Sorry! --- src/jltypes.c | 87 ------------------------------------------------- test/subtype.jl | 2 +- 2 files changed, 1 insertion(+), 88 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index f1845ae68c0d1..bba9362e9a64f 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1231,88 +1231,6 @@ static jl_value_t *normalize_vararg(jl_value_t *va) return va; } -int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_count) JL_NOTSAFEPOINT -{ - if (v == (jl_value_t*)var) { - if (inside_inv) { - return 0; - } else { - (*cov_count)++; - return *cov_count <= 1 || jl_is_concrete_type(var->ub); - } - } - else if (jl_is_uniontype(v)) { - return _may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count) && - _may_substitute_ub(((jl_uniontype_t*)v)->b, var, inside_inv, cov_count); - } - else if (jl_is_unionall(v)) { - jl_unionall_t *ua = (jl_unionall_t*)v; - if (ua->var == var) - return 1; - return _may_substitute_ub(ua->var->lb, var, inside_inv, cov_count) && - _may_substitute_ub(ua->var->ub, var, inside_inv, cov_count) && - _may_substitute_ub(ua->body, var, inside_inv, cov_count); - } - else if (jl_is_datatype(v)) { - int istuple = jl_is_tuple_type(v); - int isva = jl_is_vararg_type(v); - for (size_t i = 0; i < jl_nparams(v); i++) { - int invar = isva ? i == 1 : !istuple; - int ins_i = inside_inv || invar; - int old_count = *cov_count; - if (!_may_substitute_ub(jl_tparam(v,i), var, ins_i, cov_count)) - return 0; - if (isva && i == 0 && *cov_count > old_count && !jl_is_concrete_type(var->ub)) - return 0; - } - return 1; - } - return 1; -} - -// Check whether `var` may be replaced with its upper bound `ub` in `v where var<:ub` -// Conditions: -// * `var` does not appear in invariant position -// * `var` appears at most once (in covariant position) and not in a `Vararg` -// unless the upper bound is concrete (diagonal rule) -int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT -{ - int cov_count = 0; - return _may_substitute_ub(v, var, 0, &cov_count); -} - -jl_value_t *normalize_unionalls(jl_value_t *t) -{ - JL_GC_PUSH1(&t); - if (jl_is_uniontype(t)) { - jl_uniontype_t *u = (jl_uniontype_t *) t; - jl_value_t *a = NULL; - jl_value_t *b = NULL; - JL_GC_PUSH2(&a, &b); - a = normalize_unionalls(u->a); - b = normalize_unionalls(u->b); - if (a != u->a || b != u->b) { - t = jl_new_struct(jl_uniontype_type, a, b); - } - JL_GC_POP(); - } - else if (jl_is_unionall(t)) { - jl_unionall_t *u = (jl_unionall_t *) t; - jl_value_t *body = normalize_unionalls(u->body); - if (body != u->body) { - JL_GC_PUSH1(&body); - t = jl_new_struct(jl_unionall_type, u->var, body); - JL_GC_POP(); - u = (jl_unionall_t *) t; - } - - if (u->var->lb == u->var->ub || may_substitute_ub(body, u->var)) - t = jl_instantiate_unionall(u, u->var->ub); - } - JL_GC_POP(); - return t; -} - static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev, jl_typestack_t *stack); static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp, @@ -1322,11 +1240,6 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_typename_t *tn = dt->name; int istuple = (tn == jl_tuple_typename); int isnamedtuple = (tn == jl_namedtuple_typename); - if (dt->name != jl_type_typename) { - for (size_t i = 0; i < ntp; i++) - iparams[i] = normalize_unionalls(iparams[i]); - } - // check type cache if (cacheable) { size_t i; diff --git a/test/subtype.jl b/test/subtype.jl index 5fa0b7b8d73fd..d528a2230fca9 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -140,7 +140,7 @@ function test_diagonal() @test !issub(Type{Tuple{T,Any} where T}, Type{Tuple{T,T}} where T) @test !issub(Type{Tuple{T,Any,T} where T}, Type{Tuple{T,T,T}} where T) @test_broken issub(Type{Tuple{T} where T}, Type{Tuple{T}} where T) - @test issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T) + @test_broken issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T) @test !issub(Type{Tuple{T,T} where T}, Type{Tuple{T,T}} where T) @test !issub(Type{Tuple{T,T,T} where T}, Type{Tuple{T,T,T}} where T) @test isequal_type(Ref{Tuple{T, T} where Int<:T<:Int}, From ed7332c3185adacb5883782b1c62ecf7ac40715b Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 9 Jun 2020 19:38:28 -0700 Subject: [PATCH 142/232] Include Threads.foreach in documentation (#36218) --- doc/src/base/multi-threading.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/src/base/multi-threading.md b/doc/src/base/multi-threading.md index 71a693a2c619a..4f3e4e53634a9 100644 --- a/doc/src/base/multi-threading.md +++ b/doc/src/base/multi-threading.md @@ -2,6 +2,7 @@ ```@docs Base.Threads.@threads +Base.Threads.foreach Base.Threads.@spawn Base.Threads.threadid Base.Threads.nthreads From a018dc0ad5eea43b759237e53a9679edf129af8d Mon Sep 17 00:00:00 2001 From: Lyndon White Date: Wed, 10 Jun 2020 05:25:04 +0100 Subject: [PATCH 143/232] Reorganize logmsg_code and line-length fixes (#36183) * more processing of logmsg args to own function * respect 92 character limit --- base/logging.jl | 143 ++++++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 64 deletions(-) diff --git a/base/logging.jl b/base/logging.jl index 9e1b29fd544fc..9dc45fab187d4 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -75,12 +75,20 @@ catch_exceptions(logger) = true # Using invoke in combination with @nospecialize eliminates backedges to these methods function _invoked_shouldlog(logger, level, _module, group, id) @nospecialize - invoke(shouldlog, Tuple{typeof(logger), typeof(level), typeof(_module), typeof(group), typeof(id)}, logger, level, _module, group, id) + return invoke( + shouldlog, + Tuple{typeof(logger), typeof(level), typeof(_module), typeof(group), typeof(id)}, + logger, level, _module, group, id + ) end -_invoked_min_enabled_level(@nospecialize(logger)) = invoke(min_enabled_level, Tuple{typeof(logger)}, logger) +function _invoked_min_enabled_level(@nospecialize(logger)) + return invoke(min_enabled_level, Tuple{typeof(logger)}, logger) +end -_invoked_catch_exceptions(@nospecialize(logger)) = invoke(catch_exceptions, Tuple{typeof(logger)}, logger) +function _invoked_catch_exceptions(@nospecialize(logger)) + return invoke(catch_exceptions, Tuple{typeof(logger)}, logger) +end """ NullLogger() @@ -247,7 +255,7 @@ _log_record_ids = Set{Symbol}() # statement itself doesn't change. function log_record_id(_module, level, message, log_kws) modname = _module === nothing ? "" : join(fullname(_module), "_") - # Use an arbitriraly chosen eight hex digits here. TODO: Figure out how to + # Use an arbitrarily chosen eight hex digits here. TODO: Figure out how to # make the id exactly the same on 32 and 64 bit systems. h = UInt32(hash(string(modname, level, message, log_kws)) & 0xFFFFFFFF) while true @@ -268,8 +276,40 @@ default_group(file) = Symbol(splitext(basename(file))[1]) # Generate code for logging macros function logmsg_code(_module, file, line, level, message, exs...) - id = Expr(:quote, log_record_id(_module, level, message, exs)) - group = nothing + log_data = process_logmsg_exs(_module, file, line, level, message, exs...) + quote + level = $level + std_level = convert(LogLevel, level) + if std_level >= getindex(_min_enabled_level) + group = $(log_data._group) + _module = $(log_data._module) + logger = current_logger_for_env(std_level, group, _module) + if !(logger === nothing) + id = $(log_data._id) + # Second chance at an early bail-out (before computing the message), + # based on arbitrary logger-specific logic. + if _invoked_shouldlog(logger, level, _module, group, id) + file = $(log_data._file) + line = $(log_data._line) + try + msg = $(esc(message)) + handle_message( + logger, level, msg, _module, group, id, file, line; + $(log_data.kwargs...) + ) + catch err + logging_error(logger, level, _module, group, id, file, line, err) + end + end + end + end + nothing + end +end + +function process_logmsg_exs(_orig_module, _file, _line, level, message, exs...) + local _group, _id + _module = _orig_module kwargs = Any[] for ex in exs if ex isa Expr && ex.head === :(=) && ex.args[1] isa Symbol @@ -277,76 +317,48 @@ function logmsg_code(_module, file, line, level, message, exs...) if !(k isa Symbol) throw(ArgumentError("Expected symbol for key in key value pair `$ex`")) end - k = ex.args[1] + # Recognize several special keyword arguments - if k === :_id - # id may be overridden if you really want several log - # statements to share the same id (eg, several pertaining to - # the same progress step). In those cases it may be wise to - # manually call log_record_id to get a unique id in the same - # format. - id = esc(v) + if k === :_group + _group = esc(v) + elseif k === :_id + _id = esc(v) elseif k === :_module _module = esc(v) - elseif k === :_line - line = esc(v) elseif k === :_file - file = esc(v) - elseif k === :_group - group = esc(v) + _file = esc(v) + elseif k === :_line + _line = esc(v) else # Copy across key value pairs for structured log records push!(kwargs, Expr(:kw, k, esc(v))) end - elseif ex isa Expr && ex.head === :... - # Keyword splatting + elseif ex isa Expr && ex.head === :... # Keyword splatting push!(kwargs, esc(ex)) - else - # Positional arguments - will be converted to key value pairs - # automatically. + else # Positional arguments - will be converted to key value pairs automatically. push!(kwargs, Expr(:kw, Symbol(ex), esc(ex))) end end - if group === nothing - group = if isdefined(Base, :basename) && isa(file, String) - # precompute if we can - QuoteNode(default_group(file)) - else - # memoized run-time execution - ref = Ref{Symbol}() - :(isassigned($ref) ? $ref[] - : $ref[] = default_group(something($file, ""))) - end + if !@isdefined(_group) + _group = default_group_code(_file) + end + if !@isdefined(_id) + _id = Expr(:quote, log_record_id(_orig_module, level, message, exs)) end + return (;_module, _group, _id, _file, _line, kwargs) +end - quote - level = $level - std_level = convert(LogLevel, level) - if std_level >= getindex(_min_enabled_level) - group = $group - _module = $_module - logger = current_logger_for_env(std_level, group, _module) - if !(logger === nothing) - id = $id - # Second chance at an early bail-out (before computing the message), - # based on arbitrary logger-specific logic. - if _invoked_shouldlog(logger, level, _module, group, id) - file = $file - line = $line - try - msg = $(esc(message)) - handle_message(logger, level, msg, _module, group, id, file, line; $(kwargs...)) - catch err - logging_error(logger, level, _module, group, id, file, line, err) - end - end - end - end - nothing +function default_group_code(file) + if file isa String && isdefined(Base, :basename) + QuoteNode(default_group(file)) # precompute if we can + else + ref = Ref{Symbol}() # memoized run-time execution + :(isassigned($ref) ? $ref[] : $ref[] = default_group(something($file, ""))) end end + # Report an error in log message creation (or in the logger itself). @noinline function logging_error(logger, level, _module, group, id, filepath, line, @nospecialize(err)) @@ -355,7 +367,10 @@ end end try msg = "Exception while generating log record in module $_module at $filepath:$line" - handle_message(logger, Error, msg, _module, :logevent_error, id, filepath, line; exception=(err,catch_backtrace())) + handle_message( + logger, Error, msg, _module, :logevent_error, id, filepath, line; + exception=(err,catch_backtrace()) + ) catch err2 try # Give up and write to stderr, in three independent calls to @@ -379,12 +394,10 @@ function logmsg_shim(level, message, _module, group, id, file, line, kwargs) _file=String(file), _line=line, real_kws...) end -# Global log limiting mechanism for super fast but inflexible global log -# limiting. +# Global log limiting mechanism for super fast but inflexible global log limiting. const _min_enabled_level = Ref(Debug) -# LogState - a concretely typed cache of data extracted from the logger, plus -# the logger itself. +# LogState - a cache of data extracted from the logger, plus the logger itself. struct LogState min_enabled_level::LogLevel logger::AbstractLogger @@ -523,7 +536,9 @@ with_logger(logger) do end ``` """ -with_logger(@nospecialize(f::Function), logger::AbstractLogger) = with_logstate(f, LogState(logger)) +function with_logger(@nospecialize(f::Function), logger::AbstractLogger) + with_logstate(f, LogState(logger)) +end """ current_logger() From 023c59a8035926524d04081a5719dc5d8f11c2f2 Mon Sep 17 00:00:00 2001 From: Reno <25192197+singularitti@users.noreply.github.com> Date: Wed, 10 Jun 2020 08:57:54 -0400 Subject: [PATCH 144/232] Fix a typo in docs: "solve some problem(s)" (#36219) --- doc/src/manual/style-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index e2513bde013bf..4719f259c8d0e 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -261,7 +261,7 @@ Decide whether the concept in question will be written as `MyType` or `MyType()` it. The preferred style is to use instances by default, and only add methods involving `Type{MyType}` -later if they become necessary to solve some problem. +later if they become necessary to solve some problems. If a type is effectively an enumeration, it should be defined as a single (ideally immutable struct or primitive) type, with the enumeration values being instances of it. Constructors and conversions can check From 4c16ccb90598ee052c0e797c3d68fc0bf51cb862 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 10 Jun 2020 09:41:46 -0700 Subject: [PATCH 145/232] Bump LibGit2 to 0.28.5 (#36067) Until we can build 1.0.0 (#35233). --- deps/Versions.make | 4 ++-- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/libgit2.mk | 6 +++++ deps/libgit2.version | 4 ++-- deps/patches/libgit2-case-sensitive.patch | 22 +++++++++++++++++++ 56 files changed, 58 insertions(+), 30 deletions(-) delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 delete mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/patches/libgit2-case-sensitive.patch diff --git a/deps/Versions.make b/deps/Versions.make index c7cea9296fba4..a69bffca1b4e1 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -26,8 +26,8 @@ LIBSSH2_VER = 1.9.0 LIBSSH2_BB_REL = 1 CURL_VER = 7.66.0 CURL_BB_REL = 1 -LIBGIT2_VER = 0.28.2 -LIBGIT2_BB_REL = 1 +LIBGIT2_VER = 0.28.5 +LIBGIT2_BB_REL = 0 LIBUV_VER = 1.29.1 LIBUV_BB_REL = 6 OBJCONV_VER = 2.49.0 diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 deleted file mode 100644 index 56213f1e75f6d..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d1bf78dc08e2aa5a8b96bf6daa294387 diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 2b8a03016b1ff..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b432a73d5078093cc3a3d622746a160cdad5b11f0434df7e61479adeb0e1bf6cb56d6d5aa0fd534f4b236ef779012677379bf2e7d913811344223f1b29dc3b82 diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 deleted file mode 100644 index 4944234e4c7d2..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -43e4409595b449208eced5c58be28eae diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 deleted file mode 100644 index 437ce2ad5afb3..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6f5adba125e8be8e66317e18a2c36a9fa8f3d1f6647e0a9bb03957879a2f023717c7432651b3d33319b72c70e7b78eee0960c140308c6dad66ae6669a2d99922 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 deleted file mode 100644 index 826d7f25cb013..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8abf5ba687e262efa27c065cb20ecf20 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 deleted file mode 100644 index e8c73e1247484..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -36e7f475622721b426180d63c13421bdd6f3147a9179831c82b8fccb8935c69b3b0f6dbdc42c937f83ccedae269cf743b09a689d8c46eb2b155749f8e6a94f80 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 deleted file mode 100644 index 0ee52b55d9b05..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c552d40dd57d25e288ecbe9cc6824df1 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 deleted file mode 100644 index c207e7d64ec1b..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -96fab06657d2f90e1290db1229da94637fbc2b2d96e5882fb0cc237f0f6036f67c3295b6bb7a3671938e8aaa8fb9e75dc1ed7ad00c14e9cc50873396dc6748ef diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 deleted file mode 100644 index de30adb5ec1b1..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a7b0088acfadf471b226a3e13aaa1e52 diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 deleted file mode 100644 index a5c246265cb34..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ce2842de3f5d5e96d7213f4f84216beed3264f4170fe1f3e550dc2429abd8ef5e12b5336dbf618362ca76a0458fef62c4096a2c4634dde05dcb9d0a8a7bfdad3 diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 deleted file mode 100644 index a3e33efdc838b..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b58d9b986c0949a456c3351289122d03 diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 deleted file mode 100644 index c072ea88f58aa..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -83b79203656fe6b227dc6be7cb284245e027bc85f4cfb5011ca97f7273e269effd0e5990792c6cfbc203ae23230d973eddb8a1b10371ae10e4d988d4f3ebf43a diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 73e98f2acc803..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8b3512062571498e79d18ece607c2f3a diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index 84e244e6466a3..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2609cb4c968dc87bb63be88888cdc02d5e7b2333604ecdbef5358883051ba391d2ef66e361d3dd5b79d4af8dce311a80ed11ce3f4be7a746f86c3e834ce97e6e diff --git a/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 deleted file mode 100644 index a808ab245fdd2..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -091eeeac55b90fb3be253bc0b7294851 diff --git a/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 42bab10c991c0..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1f9aa0457c665aa8afb4a808c923816583ee62fd3a1d6aecc0be4814530e599a31e4174b84548856cd0e7d2b1cb3aea177519a25a68c19014938e30a46a57fff diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 deleted file mode 100644 index adc9171e5b740..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -ade2d1d1f0215a63607cfaa3b491834c diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 deleted file mode 100644 index b82847db888ae..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6e56fc5fbae06c091cd07ef353d9fa023cbeaced6d85cf80c9b13c80524587d4ac9026bf92f10d2c1f698783804e208385b8f1821e491f44c7d522dbcddf657c diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 deleted file mode 100644 index bed26eb8f7c1d..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -71d9f246db80a4d2f60e6c12bd9a79e8 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 338f1ddb3b7d2..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4cf4dd2ccaa218c2cc0223eaf19a4b3e834d7c36cf992e8e96a011c3e429347665774e44fdcc913b71c24a74232608fe3a8ce66e75184c58640ce5bb495a07f5 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 deleted file mode 100644 index 5c29061954d4a..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -405902170ac8d62e8dcb985ca952c4e5 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 deleted file mode 100644 index 52e793c014433..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -95f9f17d541d80ed06e915486d7ed73e605197d88f08cb34fdebf2ad89efb9144149cd05dc096c3b1583d3b93e02bbcc27a66d341cc2421d08d3f46ffb7cd733 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 deleted file mode 100644 index 9da4583482949..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7e4c7190e4e7994cdb2d9b8cdc1b3bad diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 deleted file mode 100644 index 406b8702a745a..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -64efd8bb016aa1e9ef8c36004d0b5feb9d433a303687d70eb88fd3eb7dfb3e11081cd6b8eaeb34a2ddd226f3133ed080f615ed2f56c0af5688ad57eafe7937c7 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 4e7043c9b625d..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -cb35d78529b0e4896c2a6101ad01800c diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index 2b965220fef07..0000000000000 --- a/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -dd02d1ad28befbc47a6ddfaaf748895e6ce5a9ead8ec76d81ba19e0004099746187fb5f8e6cd647c0fd1ec611393b65890e4e64de4d042c8288b162c1554f9b6 diff --git a/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..37db6440e0521 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +84758d80ae042a3059c742c7797a9616 diff --git a/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..0e16d99f4b2f0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +fce4ddb85caeb2a90ef33058098d0da771cd6531f05b88c1b8709e0513fb44442fb5bc07c1b97047388e2a0eccdadb9a6e029b97b043ba1a8b3b306f3b6f02a2 diff --git a/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..f5e64a432ddfa --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +c4e79fbac7bda0564e1ef5e113eddc81 diff --git a/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..535cd1f668c50 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +38192935167686d7843debc827877676f302561721ddba971e1cdaf91b586a931e2a87716702f62f38683f92f93f3d227d76ff171a4fd1e46c436c0c5c3125b9 diff --git a/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000000000..387c63d76f755 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +fb9ae89f80ec929637791315eb8c8e13 diff --git a/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..72db6c1e52a26 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +6bc8f057f8e8d50917ddbf2b0e7bb91d1dcb2353bc26139df7bea2e1fab3b2c27fc453cbeafde5577de5521b74ced010f6eb0d7e805b78f5a8b93595e4989c6b diff --git a/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000000000..f85b89d0f59e1 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +4946a6ec5492e9d130d6693d1396d76a diff --git a/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..160f92a2b054a --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +35e81a473e8b540809048d59ff23bc8faaf582fd54a05a6220054f77e9fd2e33f2d120ec991b0e1cda2d0a1dd3934dfe6fb246543251c2a2940f5ef845a7ee46 diff --git a/deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..7e5633b2e4d6d --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +bb3e4b6bff6692b3ba8e1e4685bfda67 diff --git a/deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..66fa99822bb11 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +3ed6266bd2c16ccb678d3a7e3e3508e4cb4f76d145106cef758e1a4cf293dd066b27c7774f81c2fc8137dde74a50f319b8e971c60cefc105743d9b150fce36ff diff --git a/deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..0614e8cedfc96 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +17e8f2ef09ec24bcffd9b3e78b6eea75 diff --git a/deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..a88f3102d6a87 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +92f0de1ad37912a03bb46112186ce9a636ec5e1821aa95a7623a04b1b2d953cc7fe65641865d86b0e09e7f3e9344c1f0cb0d270166aa6fafa00b78c14ebfd356 diff --git a/deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..30d86d37e1d51 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +b670c243ac321543cfe91ecde315cf27 diff --git a/deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..ea4a631e29a35 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +fe3e3963b7e22ae8fddd01c768dc5e2a32095a71a310bd67de50a88c8f4d35184e1e0617b02b3a600edebff391ab57dafdb79bd397b3c215282098d635527e28 diff --git a/deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..9a0d0af68bedc --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +aec4041aaeaa937313e7284946f90775 diff --git a/deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..81e3370618bee --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +b358bba0dee00dbfb5377e8458f20666e2bb7af73915fd94907a3ca50a6155e08732145b7ce7f3c79492b58bdf4a895af2b5bf1bf6a59382c7dec4042756dab3 diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000000000..dc57f6a27e463 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +2b7b91661556853a6fe3d33b9d1bf8c1 diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000000000..753606856371d --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +e7a1e67081d65c7472bf41d981473d2db28aad8e9044a254ccb946f05d644b4b9032af61274afe7bcddac4548251db87ef221531ec5387985f468aa5b9c48a1e diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..a9751dcb59ed5 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +79e4468f8b6e9976bc774095a775f878 diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..38f84146a141a --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ecc1e03726ec36182182f40e44d2ffdf4dcdd6b92a35a26ca25f890deffc2a661dfe79f92a283843ee1983fa8f10287adb71b58f354b84b9da2bbedd8398529a diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..243defb516da6 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +bf88bd21c5b400649e6033953fbf8285 diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..ca82880a061e0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +e3432315a57c323f2d3f26cf8a26d7baab7415bdc4f1d7c8b3a6057462f670975a49f410623af21a09e456f36b5c9e6e4d58d600f9e1778affaee9b8b040be1d diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000000000..df33f9e81648d --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +7b0bb0db8d49d36bdb57ddfcd03e2d08 diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000000000..e99b0f554a384 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +b24e72995f400e7e28843da9387e55f98d2233d2aa99aa5cfc32728e91630a722ecc45fa2c90c81791f17ec95e338e512e91702ccdb8a4812ae5c883a8ee750b diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..4f18dabf26671 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +05b65eb29932eca28b9652209ad79bcb diff --git a/deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..5f6dc1901eeb6 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.5-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +23ca28fb2d74471f5caefa57db6844790670f07cca70b234fa765715e1725933849ed37d4fd123b1a9cd621261e65df360d26826952a3c8ed04437dc93f60416 diff --git a/deps/libgit2.mk b/deps/libgit2.mk index e53fade283b29..c04444be7e8fa 100644 --- a/deps/libgit2.mk +++ b/deps/libgit2.mk @@ -49,8 +49,14 @@ $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied: $(LIBGIT2_SRC_PATH)/so patch -p1 -f < $(SRCDIR)/patches/libgit2-agent-nonfatal.patch echo 1 > $@ +$(LIBGIT2_SRC_PATH)/libgit2-case-sensitive.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted + cd $(LIBGIT2_SRC_PATH) && \ + patch -p1 -f < $(SRCDIR)/patches/libgit2-case-sensitive.patch + echo 1 > $@ + $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: \ $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied \ + $(LIBGIT2_SRC_PATH)/libgit2-case-sensitive.patch-applied \ $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(LIBGIT2_SRC_PATH)/source-extracted mkdir -p $(dir $@) diff --git a/deps/libgit2.version b/deps/libgit2.version index f67bedc414176..66baf582ff3a3 100644 --- a/deps/libgit2.version +++ b/deps/libgit2.version @@ -1,2 +1,2 @@ -LIBGIT2_BRANCH=v0.28.2 -LIBGIT2_SHA1=b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3 +LIBGIT2_BRANCH=v0.28.5 +LIBGIT2_SHA1=7a2b969d559b83798d93728f24d1729ffc97b717 diff --git a/deps/patches/libgit2-case-sensitive.patch b/deps/patches/libgit2-case-sensitive.patch new file mode 100644 index 0000000000000..13719b2e0c11b --- /dev/null +++ b/deps/patches/libgit2-case-sensitive.patch @@ -0,0 +1,22 @@ +From 44d5e47d8c486e149c262974bfab808737ac85c1 Mon Sep 17 00:00:00 2001 +From: Dan Skorupski +Date: Sat, 24 Aug 2019 10:39:56 -0500 +Subject: [PATCH] Fix include casing for case-sensitive filesystems. + +--- + src/path.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/path.c b/src/path.c +index 150e09eb60..732834fbef 100644 +--- a/src/path.c ++++ b/src/path.c +@@ -14,7 +14,7 @@ + #include "win32/w32_buffer.h" + #include "win32/w32_util.h" + #include "win32/version.h" +-#include ++#include + #else + #include + #endif From 70d8497f5cbc86738aa725d40ede9d75cc009c1c Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Thu, 11 Jun 2020 00:50:44 -0700 Subject: [PATCH 146/232] Support `init` keyword in `sum`/`prod`/`maximum`/`minimum` (#36188) Co-authored-by: Tim Holy Co-authored-by: Milan Bouchet-Valat --- NEWS.md | 1 + base/reduce.jl | 139 ++++++++++++++++++++++++++++++++++++++------- base/reducedim.jl | 28 ++++----- base/reflection.jl | 6 +- test/reduce.jl | 34 +++++++++++ test/reducedim.jl | 5 ++ 6 files changed, 175 insertions(+), 38 deletions(-) diff --git a/NEWS.md b/NEWS.md index 63ffa9f8505e0..c4848af3d9ce2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,6 +47,7 @@ Standard library changes * The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]). * `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). * All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). +* `sum`, `prod`, `maximum`, and `minimum` now support `init` keyword argument ([#36188], [#35839]). #### LinearAlgebra * New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]). diff --git a/base/reduce.jl b/base/reduce.jl index facf01b476cbf..c73caaeac7a97 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -45,7 +45,7 @@ function mapfoldl_impl(f::F, op::OP, nt, itr) where {F,OP} end function foldl_impl(op::OP, nt, itr) where {OP} - v = _foldl_impl(op, get(nt, :init, _InitialValue()), itr) + v = _foldl_impl(op, nt, itr) v isa _InitialValue && return reduce_empty_iter(op, itr) return v end @@ -157,7 +157,7 @@ Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl` If provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. """ -mapfoldl(f, op, itr; kw...) = mapfoldl_impl(f, op, kw.data, itr) +mapfoldl(f, op, itr; init=_InitialValue()) = mapfoldl_impl(f, op, init, itr) """ foldl(op, itr; [init]) @@ -200,7 +200,7 @@ Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. """ -mapfoldr(f, op, itr; kw...) = mapfoldr_impl(f, op, kw.data, itr) +mapfoldr(f, op, itr; init=_InitialValue()) = mapfoldr_impl(f, op, init, itr) """ @@ -462,7 +462,7 @@ reduce(op, a::Number) = a # Do we want this? ## sum """ - sum(f, itr) + sum(f, itr; [init]) Sum the results of calling function `f` on each element of `itr`. @@ -470,6 +470,13 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init`. It must be +the additive identity (i.e. zero) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> sum(abs2, [2; 3; 4]) @@ -491,10 +498,10 @@ In the former case, the integers are widened to system word size and therefore the result is 128. In the latter case, no such widening happens and integer overflow results in -128. """ -sum(f, a) = mapreduce(f, add_sum, a) +sum(f, a; kw...) = mapreduce(f, add_sum, a; kw...) """ - sum(itr) + sum(itr; [init]) Returns the sum of all elements in a collection. @@ -502,18 +509,29 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init`. It must be +the additive identity (i.e. zero) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> sum(1:20) 210 + +julia> sum(1:20; init = 0.0) +210.0 ``` """ -sum(a) = sum(identity, a) -sum(a::AbstractArray{Bool}) = count(a) +sum(a; kw...) = sum(identity, a; kw...) +sum(a::AbstractArray{Bool}; kw...) = + kw.data === NamedTuple() ? count(a) : reduce(add_sum, a; kw...) ## prod """ - prod(f, itr) + prod(f, itr; [init]) Returns the product of `f` applied to each element of `itr`. @@ -521,16 +539,23 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init`. It must be the +multiplicative identity (i.e. one) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> prod(abs2, [2; 3; 4]) 576 ``` """ -prod(f, a) = mapreduce(f, mul_prod, a) +prod(f, a; kw...) = mapreduce(f, mul_prod, a; kw...) """ - prod(itr) + prod(itr; [init]) Returns the product of all elements of a collection. @@ -538,13 +563,23 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init`. It must be the +multiplicative identity (i.e. one) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest -julia> prod(1:20) -2432902008176640000 +julia> prod(1:5) +120 + +julia> prod(1:5; init = 1.0) +120.0 ``` """ -prod(a) = mapreduce(identity, mul_prod, a) +prod(a; kw...) = mapreduce(identity, mul_prod, a; kw...) ## maximum & minimum _fast(::typeof(min),x,y) = min(x,y) @@ -610,36 +645,72 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)}, end """ - maximum(f, itr) + maximum(f, itr; [init]) Returns the largest result of calling function `f` on each element of `itr`. +The value returned for empty `itr` can be specified by `init`. It must be +a neutral element for `max` (i.e. which is less than or equal to any +other element) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> maximum(length, ["Julion", "Julia", "Jule"]) 6 + +julia> maximum(length, []; init=-1) +-1 + +julia> maximum(sin, Real[]; init=-1.0) # good, since output of sin is >= -1 +-1.0 ``` """ -maximum(f, a) = mapreduce(f, max, a) +maximum(f, a; kw...) = mapreduce(f, max, a; kw...) """ - minimum(f, itr) + minimum(f, itr; [init]) Returns the smallest result of calling function `f` on each element of `itr`. +The value returned for empty `itr` can be specified by `init`. It must be +a neutral element for `min` (i.e. which is greater than or equal to any +other element) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> minimum(length, ["Julion", "Julia", "Jule"]) 4 + +julia> minimum(length, []; init=typemax(Int64)) +9223372036854775807 + +julia> minimum(sin, Real[]; init=1.0) # good, since output of sin is <= 1 +1.0 ``` """ -minimum(f, a) = mapreduce(f, min, a) +minimum(f, a; kw...) = mapreduce(f, min, a; kw...) """ - maximum(itr) + maximum(itr; [init]) Returns the largest element in a collection. +The value returned for empty `itr` can be specified by `init`. It must be +a neutral element for `max` (i.e. which is less than or equal to any +other element) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> maximum(-20.5:10) @@ -647,15 +718,31 @@ julia> maximum(-20.5:10) julia> maximum([1,2,3]) 3 + +julia> maximum(()) +ERROR: ArgumentError: reducing over an empty collection is not allowed +Stacktrace: +[...] + +julia> maximum((); init=-Inf) +-Inf ``` """ -maximum(a) = mapreduce(identity, max, a) +maximum(a; kw...) = mapreduce(identity, max, a; kw...) """ - minimum(itr) + minimum(itr; [init]) Returns the smallest element in a collection. +The value returned for empty `itr` can be specified by `init`. It must be +a neutral element for `min` (i.e. which is greater than or equal to any +other element) as it is unspecified whether `init` is used +for non-empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> minimum(-20.5:10) @@ -663,9 +750,17 @@ julia> minimum(-20.5:10) julia> minimum([1,2,3]) 1 + +julia> minimum([]) +ERROR: ArgumentError: reducing over an empty collection is not allowed +Stacktrace: +[...] + +julia> minimum([]; init=Inf) +Inf ``` """ -minimum(a) = mapreduce(identity, min, a) +minimum(a; kw...) = mapreduce(identity, min, a; kw...) ## all & any diff --git a/base/reducedim.jl b/base/reducedim.jl index d1e5001492fc4..140e004cc5457 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -307,21 +307,21 @@ julia> mapreduce(isodd, |, a, dims=1) 1 1 1 1 ``` """ -mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, kw...) = - _mapreduce_dim(f, op, kw.data, A, dims) +mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) = + _mapreduce_dim(f, op, init, A, dims) mapreduce(f, op, A::AbstractArrayOrBroadcasted...; kw...) = reduce(op, map(f, A...); kw...) -_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, ::Colon) = - mapfoldl(f, op, A; nt...) +_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, ::Colon) = + mapfoldl_impl(f, op, nt, A) -_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, ::Colon) = +_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, ::Colon) = _mapreduce(f, op, IndexStyle(A), A) -_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, dims) = - mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A) +_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, dims) = + mapreducedim!(f, op, reducedim_initarray(A, dims, nt), A) -_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, dims) = +_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, dims) = mapreducedim!(f, op, reducedim_init(f, op, A, dims), A) """ @@ -717,12 +717,12 @@ for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod, (:maximum, :_maximum, :max), (:minimum, :_minimum, :min)] @eval begin # User-facing methods with keyword arguments - @inline ($fname)(a::AbstractArray; dims=:) = ($_fname)(a, dims) - @inline ($fname)(f, a::AbstractArray; dims=:) = ($_fname)(f, a, dims) + @inline ($fname)(a::AbstractArray; dims=:, kw...) = ($_fname)(a, dims; kw...) + @inline ($fname)(f, a::AbstractArray; dims=:, kw...) = ($_fname)(f, a, dims; kw...) # Underlying implementations using dispatch - ($_fname)(a, ::Colon) = ($_fname)(identity, a, :) - ($_fname)(f, a, ::Colon) = mapreduce(f, $op, a) + ($_fname)(a, ::Colon; kw...) = ($_fname)(identity, a, :; kw...) + ($_fname)(f, a, ::Colon; kw...) = mapreduce(f, $op, a; kw...) end end @@ -743,8 +743,8 @@ for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod), mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A) $(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init) - $(_fname)(A, dims) = $(_fname)(identity, A, dims) - $(_fname)(f, A, dims) = mapreduce(f, $(op), A, dims=dims) + $(_fname)(A, dims; kw...) = $(_fname)(identity, A, dims; kw...) + $(_fname)(f, A, dims; kw...) = mapreduce(f, $(op), A; dims=dims, kw...) end end diff --git a/base/reflection.jl b/base/reflection.jl index df4ee17efc89d..96035b178ffde 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1221,10 +1221,12 @@ See also [`applicable`](@ref). julia> hasmethod(length, Tuple{Array}) true -julia> hasmethod(sum, Tuple{Function, Array}, (:dims,)) +julia> f(; oranges=0) = oranges; + +julia> hasmethod(f, Tuple{}, (:oranges,)) true -julia> hasmethod(sum, Tuple{Function, Array}, (:apples, :bananas)) +julia> hasmethod(f, Tuple{}, (:apples, :bananas)) false julia> g(; xs...) = 4; diff --git a/test/reduce.jl b/test/reduce.jl index c395fae5dda7a..4688709b099c9 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -4,6 +4,9 @@ using Random isdefined(Main, :OffsetArrays) || @eval Main include("testhelpers/OffsetArrays.jl") using .Main.OffsetArrays +==ₜ(::Any, ::Any) = false +==ₜ(a::T, b::T) where {T} = isequal(a, b) + # fold(l|r) & mapfold(l|r) @test foldl(+, Int64[]) === Int64(0) # In reference to issues #7465/#20144 (PR #20160) @test foldl(+, Int16[]) === Int16(0) # In reference to issues #21536 @@ -172,6 +175,20 @@ for f in (sum3, sum4, sum7, sum8) end @test typeof(sum(Int8[])) == typeof(sum(Int8[1])) == typeof(sum(Int8[1 7])) +@testset "`sum` of empty collections with `init`" begin + function noncallable end # should not be called + @testset for init in [0, 0.0] + @test sum([]; init = init) === init + @test sum((x for x in [123] if false); init = init) === init + @test sum(noncallable, []; init = init) === init + @test sum(noncallable, (x for x in [123] if false); init = init) === init + @test sum(Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + zeros(typeof(init), 1, 2, 0) + @test sum(noncallable, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + zeros(typeof(init), 1, 2, 0) + end +end + # check sum(abs, ...) for support of empty collections @testset "sum(abs, [])" begin @test @inferred(sum(abs, Float64[])) === 0.0 @@ -199,6 +216,20 @@ end @test typeof(prod(Array(trues(10)))) == Bool +@testset "`prod` of empty collections with `init`" begin + function noncallable end # should not be called + @testset for init in [1, 1.0, ""] + @test prod([]; init = init) === init + @test prod((x for x in [123] if false); init = init) === init + @test prod(noncallable, []; init = init) === init + @test prod(noncallable, (x for x in [123] if false); init = init) === init + @test prod(Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + ones(typeof(init), 1, 2, 0) + @test prod(noncallable, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + ones(typeof(init), 1, 2, 0) + end +end + # check type-stability prod2(itr) = invoke(prod, Tuple{Any}, itr) @test prod(Int[]) === prod2(Int[]) === 1 @@ -211,6 +242,9 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr) @test_throws ArgumentError maximum(Int[]) @test_throws ArgumentError minimum(Int[]) +@test maximum(Int[]; init=-1) == -1 +@test minimum(Int[]; init=-1) == -1 + @test maximum(5) == 5 @test minimum(5) == 5 @test extrema(5) == (5, 5) diff --git a/test/reducedim.jl b/test/reducedim.jl index 3f59ae6e2570a..e51a075496f1c 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -75,6 +75,11 @@ safe_minabs(A::Array{T}, region) where {T} = safe_mapslices(minimum, abs.(A), re @test @inferred(count(!, Breduc, dims=region)) ≈ safe_count(.!Breduc, region) end +# Combining dims and init +A = Array{Int}(undef, 0, 3) +@test_throws ArgumentError maximum(A; dims=1) +@test maximum(A; dims=1, init=-1) == reshape([-1,-1,-1], 1, 3) + # Test reduction along first dimension; this is special-cased for # size(A, 1) >= 16 Breduc = rand(64, 3) From 38f2c594c49cec46d38be6dd614a5592582a3016 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 11 Jun 2020 04:47:08 -0500 Subject: [PATCH 147/232] Add more details about TerminalMenus API changes to NEWS (#36210) --- NEWS.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/NEWS.md b/NEWS.md index c4848af3d9ce2..b6ffe8c872af5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -67,6 +67,21 @@ Standard library changes improved support for dynamic menus. These changes are compatible with the previous (deprecated) interface, so are non-breaking. + The new API offers several enhancements: + + + Menus are configured in their constructors via keyword arguments + + For custom menu types, the new `Config` and `MultiSelectConfig` replace the global `CONFIG` Dict + + `request(menu; cursor=1)` allows you to control the initial cursor position in the menu (defaults to first item) + + `MultiSelectMenu` allows you to pass a list of initially-selected items with the `selected` keyword argument + + `writeLine` was deprecated to `writeline`, and `writeline` methods are not expected to print the cursor indicator. + The old `writeLine` continues to work, and any of its method extensions should print the cursor indicator as before. + + `printMenu` has been deprecated to `printmenu`, and it both accepts a state input and returns a state output + that controls the number of terminal lines erased when the menu is next refreshed. This plus related changes + makes `printmenu` work properly when the number of menu items might change depending on user choices. + + `numoptions`, returning the number of items in the menu, has been added as an alternative to implementing `options` + + `suppress_output` (primarily a testing option) has been added as a keyword argument to `request`, + rather than a configuration option + #### SparseArrays * Display large sparse matrices with a Unicode "spy" plot of their nonzero patterns, and display small sparse matrices by an `Matrix`-like 2d layout of their contents. From f0b369cca94109a73751c671900357eccefe9e65 Mon Sep 17 00:00:00 2001 From: Michele Zaffalon Date: Thu, 11 Jun 2020 16:24:38 +0200 Subject: [PATCH 148/232] Example of finalizer registered within constructor (#36118) --- base/gcutils.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/base/gcutils.jl b/base/gcutils.jl index 23ba4a82b65b5..c403e4f20f626 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -25,6 +25,21 @@ finalizer(my_mutable_struct) do x ccall(:jl_safe_printf, Cvoid, (Cstring, Cstring), "Finalizing %s.", repr(x)) end ``` + +A finalizer may be registered at object construction. In the following example note that +we implicitly rely on the finalizer returning the newly created mutable struct `x`. + +# Example +```julia +mutable struct MyMutableStruct + bar + function MyMutableStruct(bar) + x = new(bar) + f(t) = @async println("Finalizing \$t.") + finalizer(f, x) + end +end +``` """ function finalizer(@nospecialize(f), @nospecialize(o)) if !ismutable(o) From a3ef188430c7b9726cc7e90f36011d2f31c9e064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 11 Jun 2020 16:41:08 +0200 Subject: [PATCH 149/232] Fix diagm_container for types without zero (#35743) --- stdlib/LinearAlgebra/src/dense.jl | 4 +++- stdlib/LinearAlgebra/test/dense.jl | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 8a57a1eaf52cf..4ea27e26bc7b8 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -301,7 +301,9 @@ function diagm_size(size::Tuple{Int,Int}, kv::Pair{<:Integer,<:AbstractVector}.. end function diagm_container(size, kv::Pair{<:Integer,<:AbstractVector}...) T = promote_type(map(x -> eltype(x.second), kv)...) - return zeros(T, diagm_size(size, kv...)...) + # For some type `T`, `zero(T)` is not a `T` and `zeros(T, ...)` fails. + U = promote_type(T, typeof(zero(T))) + return zeros(U, diagm_size(size, kv...)...) end diagm_container(size, kv::Pair{<:Integer,<:BitVector}...) = falses(diagm_size(size, kv...)...) diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index 1c7eeb5b4acac..2dec8e95402d7 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -939,4 +939,15 @@ end @test exp(log(A2)) ≈ A2 end +struct TypeWithoutZero end +Base.zero(::Type{TypeWithoutZero}) = TypeWithZero() +struct TypeWithZero end +Base.promote_rule(::Type{TypeWithoutZero}, ::Type{TypeWithZero}) = TypeWithZero +Base.zero(::Type{<:Union{TypeWithoutZero, TypeWithZero}}) = TypeWithZero() +Base.:+(x::TypeWithZero, ::TypeWithoutZero) = x + +@testset "diagm for type with no zero" begin + @test diagm(0 => [TypeWithoutZero()]) isa Matrix{TypeWithZero} +end + end # module TestDense From abc7f755ce3b962bf4ea614106680cebcfcac8b0 Mon Sep 17 00:00:00 2001 From: Adam B Date: Thu, 11 Jun 2020 10:44:23 -0400 Subject: [PATCH 150/232] More thoroughly document apropos (#35903) I've frequently found myself wishing for a regex-based apropos but didn't realize it already existed since it's not mentioned in the docstring. * Document apropos help-mode in manual * Update apropos docstring for clarity and terseness --- stdlib/REPL/docs/src/index.md | 15 +++++++++++++++ stdlib/REPL/src/docview.jl | 8 +++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 565399a472f3e..7a7eaf64ada8f 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -120,6 +120,21 @@ search: Int32 UInt32 32-bit signed integer type. ``` +A string or regex literal searches all docstrings using [`apropos`](@ref): + +``` +help?> "aprop" +REPL.stripmd +Base.Docs.apropos + +help?> r"ap..p" +Base.:∘ +Base.shell_escape_posixly +Distributed.CachingPool +REPL.stripmd +Base.Docs.apropos +``` + Help mode can be exited by pressing backspace at the beginning of the line. ### [Shell mode](@id man-shell-mode) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 1b85278cccc03..c7219a4b9a6f7 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -685,14 +685,16 @@ stripmd(x::Markdown.Footnote) = "$(stripmd(x.id)) $(stripmd(x.text))" stripmd(x::Markdown.Table) = join([join(map(stripmd, r), " ") for r in x.rows], " ") -# Apropos searches through all available documentation for some string or regex """ - apropos(string) + apropos([io::IO=stdout], pattern::Union{AbstractString,Regex}) -Search through all documentation for a string, ignoring case. +Search available docstrings for entries containing `pattern`. + +When `pattern` is a string, case is ignored. Results are printed to `io`. """ apropos(string) = apropos(stdout, string) apropos(io::IO, string) = apropos(io, Regex("\\Q$string", "i")) + function apropos(io::IO, needle::Regex) for mod in modules # Module doc might be in README.md instead of the META dict From 70fe622ee445eb7b04b033be72a8a4241218bc19 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 11 Jun 2020 21:26:47 -0400 Subject: [PATCH 151/232] remove unnecessary Box when an argument is used before assigned (#36245) --- src/ast.scm | 2 +- src/julia-syntax.scm | 18 ++++++++++++------ test/core.jl | 6 ++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/ast.scm b/src/ast.scm index a37da4eed387d..7af2aab32024a 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -288,7 +288,7 @@ (symbol? (cadr e)))) (define (lam:args x) (cadr x)) -(define (lam:vars x) (llist-vars (lam:args x))) +(define (lam:argnames x) (llist-vars (lam:args x))) (define (lam:vinfo x) (caddr x)) (define (lam:body x) (cadddr x)) (define (lam:sp x) (cadddr (lam:vinfo x))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index a4489ad313409..b292ee5bae8fb 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2556,7 +2556,7 @@ (and (memq var (scope:locals scope)) 'local) (and (memq var (scope:globals scope)) (if (and exclude-top-level-globals - (null? (lam:vars (scope:lam scope))) + (null? (lam:args (scope:lam scope))) ;; don't inherit global decls from the outermost scope block ;; in a top-level expression. (or (not (scope:prev scope)) @@ -2636,13 +2636,13 @@ '(false) '(true))) ((eq? (car e) 'lambda) - (let* ((args (lam:vars e)) + (let* ((args (lam:argnames e)) (body (resolve-scopes- (lam:body e) (make-scope e args '() '() sp '() scope)))) `(lambda ,(cadr e) ,(caddr e) ,body))) ((eq? (car e) 'scope-block) (let* ((blok (cadr e)) ;; body of scope-block expression (lam (scope:lam scope)) - (argnames (lam:vars lam)) + (argnames (lam:argnames lam)) (toplevel? (and (null? argnames) (eq? e (lam:body lam)))) (current-locals (caddr lam)) ;; locals created so far in our lambda (globals (find-global-decls blok)) @@ -2755,7 +2755,7 @@ ,(resolve-scopes- (cadddr e) scope (method-expr-static-parameters e)))) (else (if (and (eq? (car e) '=) (symbol? (cadr e)) - scope (null? (lam:vars (scope:lam scope))) + scope (null? (lam:args (scope:lam scope))) (warn-var?! (cadr e) scope) (= *scopewarn-opt* 1)) (let* ((v (cadr e)) @@ -2782,7 +2782,7 @@ ;; names of arguments and local vars (define (lambda-all-vars e) - (append (lam:vars e) (caddr e))) + (append (lam:argnames e) (caddr e))) ;; compute set of variables referenced in a lambda but not bound by it (define (free-vars- e tab) @@ -3201,6 +3201,7 @@ f(x) = yt(x) ;; This does a basic-block-local dominance analysis to find variables that ;; are never used undef. (let ((vi (car (lam:vinfo lam))) + (args (lam:argnames lam)) (unused (table)) ;; variables not (yet) used (read from) in the current block (live (table)) ;; variables that have been set in the current block (seen (table))) ;; all variables we've seen assignments to @@ -3221,6 +3222,11 @@ f(x) = yt(x) (restore (table))) (define (mark-used var) ;; remove variable from the unused table + ;; Note arguments are only "used" for purposes of this analysis when + ;; they are captured, since they are never undefined. + (if (and (has? unused var) (not (memq var args))) + (del! unused var))) + (define (mark-captured var) (if (has? unused var) (del! unused var))) (define (assign! var) @@ -3272,7 +3278,7 @@ f(x) = yt(x) (get-methods e (lam:body lam)) (list e)))) (for-each (lambda (ex) - (for-each mark-used + (for-each mark-captured (map car (cadr (lam:vinfo (cadddr ex)))))) all-methods) (assign! (cadr e)))) diff --git a/test/core.jl b/test/core.jl index cb982d4d902d3..790ea3cd08165 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5178,6 +5178,12 @@ end @test let_Box5()() == 46 @test let_noBox()() == 21 +function _assigns_and_captures_arg(a) + a = a + return ()->a +end +@test !any(contains_Box, code_lowered(_assigns_and_captures_arg,(Any,))[1].code) + module TestModuleAssignment using Test @eval $(GlobalRef(TestModuleAssignment, :x)) = 1 From 69ce9d2a97d7d623ccfad6af0622679b317aa837 Mon Sep 17 00:00:00 2001 From: Gustavo Goretkin Date: Fri, 12 Jun 2020 03:27:28 +0200 Subject: [PATCH 152/232] Rewording related to disability (#36249) Some disability related rewording --- base/abstractarray.jl | 2 +- doc/src/manual/interfaces.md | 2 +- doc/src/manual/methods.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 703895a96ba26..4adab5aade0f7 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2260,7 +2260,7 @@ function hash(A::AbstractArray, h::UInt) # hashes will often subsequently be compared by equality -- and equality between arrays # works elementwise forwards and is short-circuiting. This means that a collision # between arrays that differ by elements at the beginning is cheaper than one where the - # difference is towards the end. Furthermore, blindly choosing log(N) entries from a + # difference is towards the end. Furthermore, choosing `log(N)` arbitrary entries from a # sparse array will likely only choose the same element repeatedly (zero in this case). # To achieve this, we work backwards, starting by hashing the last element of the diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 0fd19da754bbe..a8306ae4f4c34 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -113,7 +113,7 @@ julia> Base.length(S::Squares) = S.count ``` Now, when we ask Julia to [`collect`](@ref) all the elements into an array it can preallocate a `Vector{Int}` -of the right size instead of blindly [`push!`](@ref)ing each element into a `Vector{Any}`: +of the right size instead of naively [`push!`](@ref)ing each element into a `Vector{Any}`: ```jldoctest squaretype julia> collect(Squares(4)) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index a731af8ba0521..6ab162e591e13 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -906,7 +906,7 @@ f(x::Int, y::Int) = 3 ``` This is often the right strategy; however, there are circumstances -where following this advice blindly can be counterproductive. In +where following this advice mindlessly can be counterproductive. In particular, the more methods a generic function has, the more possibilities there are for ambiguities. When your method hierarchies get more complicated than this simple example, it can be worth your From 0c92375d40793cda95172e1c7b7441ca5a36842e Mon Sep 17 00:00:00 2001 From: Gautam Mishra Date: Fri, 12 Jun 2020 10:45:19 +0530 Subject: [PATCH 153/232] Fix: rem2pi for negative values greater than 2pi (#36233) * Fix rem2pi for RoundToZero * add extra tests for rem2pi --- base/math.jl | 2 +- test/numbers.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base/math.jl b/base/math.jl index b32b67d4c1c67..768c556e0fcb4 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1001,7 +1001,7 @@ function rem2pi(x::Float64, ::RoundingMode{:ToZero}) ax = abs(x) ax <= 2*Float64(pi,RoundDown) && return x - n,y = rem_pio2_kernel(x) + n,y = rem_pio2_kernel(ax) if iseven(n) if n & 2 == 2 # n % 4 == 2: add pi diff --git a/test/numbers.jl b/test/numbers.jl index a1ab3a53f6a63..bec8ee797ebb4 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2507,6 +2507,14 @@ end @test rem2pi(T(-4), RoundNearest) ≈ 2pi-4 @test rem2pi(T(-4), RoundDown) ≈ 2pi-4 @test rem2pi(T(-4), RoundUp) == -4 + @test rem2pi(T(8), RoundToZero) ≈ 8-2pi + @test rem2pi(T(8), RoundNearest) ≈ 8-2pi + @test rem2pi(T(8), RoundDown) ≈ 8-2pi + @test rem2pi(T(8), RoundUp) ≈ 8-4pi + @test rem2pi(T(-8), RoundToZero) ≈ -8+2pi + @test rem2pi(T(-8), RoundNearest) ≈ -8+2pi + @test rem2pi(T(-8), RoundDown) ≈ -8+4pi + @test rem2pi(T(-8), RoundUp) ≈ -8+2pi end import Base.^ From e7a1b99c129c2e76a684754fb709593c71d7ad44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 12 Jun 2020 09:52:13 +0200 Subject: [PATCH 154/232] Replace LinearAlgebra._iszero by Base.iszero (#36194) --- .../LinearAlgebra/src/structuredbroadcast.jl | 4 +--- .../LinearAlgebra/test/structuredbroadcast.jl | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index a665e21731752..57eddaabb8c32 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -104,9 +104,7 @@ function isstructurepreserving(::typeof(Base.literal_pow), ::Ref{typeof(^)}, ::S end isstructurepreserving(f, args...) = false -_iszero(n::Number) = iszero(n) -_iszero(x) = x == 0 -fzeropreserving(bc) = (v = fzero(bc); !ismissing(v) && _iszero(v)) +fzeropreserving(bc) = (v = fzero(bc); !ismissing(v) && iszero(v)) # Like sparse matrices, we assume that the zero-preservation property of a broadcasted # expression is stable. We can test the zero-preservability by applying the function # in cases where all other arguments are known scalars against a zero from the structured diff --git a/stdlib/LinearAlgebra/test/structuredbroadcast.jl b/stdlib/LinearAlgebra/test/structuredbroadcast.jl index b8f5e97311588..eed0048991525 100644 --- a/stdlib/LinearAlgebra/test/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/test/structuredbroadcast.jl @@ -205,4 +205,22 @@ end @test typeof(tmp) <: Tridiagonal end + +struct Zero end +Base.iszero(::Zero) = true +Base.zero(::Type{Zero}) = Zero() +@testset "PR #36193" begin + z = Zero() + Z = [z z + z z] + zz = [z, z] + U = UpperTriangular(Z) + L = LowerTriangular(Z) + D = Diagonal(zz) + for a in [U, L, D] + @test identity.(a) isa typeof(a) + @test map(identity, a) isa typeof(a) + end +end + end From 6e7bc68865ee7baf27c721b83bc456f287762d9e Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Fri, 12 Jun 2020 13:36:58 +0100 Subject: [PATCH 155/232] docs: Fix indentation on !!! warn section (#36254) Related to #35526. Thanks to @kimikage for the heads up. --- doc/src/manual/types.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 30026a885e54b..30813b71d4ab0 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -237,12 +237,12 @@ of function arguments that are containers of abstract types; see [Performance Ti ## Primitive Types !!! warning - It is almost always preferable to wrap an existing primitive type in a new - composite type than to define your own primitive type. + It is almost always preferable to wrap an existing primitive type in a new + composite type than to define your own primitive type. - This functionality exists to allow Julia to bootstrap the standard primitive - types that LLVM supports. Once they are defined, there is very little reason - to define more. + This functionality exists to allow Julia to bootstrap the standard primitive + types that LLVM supports. Once they are defined, there is very little reason + to define more. A primitive type is a concrete type whose data consists of plain old bits. Classic examples of primitive types are integers and floating-point values. Unlike most languages, Julia lets you declare your From 1e761112038518bafe6ce37b646ae38f38d28253 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 12 Jun 2020 12:23:29 -0400 Subject: [PATCH 156/232] use a function in pkg precompile instead of writing code to stdin (#36028) --- base/loading.jl | 103 ++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 69a7e5cc66d6e..523cc1d077442 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1200,66 +1200,73 @@ function load_path_setup_code(load_path::Bool=true) return code end +# this is called in the external process that generates precompiled package files +function include_package_for_output(input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::typeof(_concrete_dependencies), uuid_tuple::NTuple{2,UInt64}, source::Union{Nothing,String}) + append!(empty!(Base.DEPOT_PATH), depot_path) + append!(empty!(Base.DL_LOAD_PATH), dl_load_path) + append!(empty!(Base.LOAD_PATH), load_path) + ENV["JULIA_LOAD_PATH"] = join(load_path, Sys.iswindows() ? ';' : ':') + Base.HOME_PROJECT[] = Base.ACTIVE_PROJECT[] = nothing + Base._track_dependencies[] = true + append!(empty!(Base._concrete_dependencies), concrete_deps) + + ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, uuid_tuple) + if source !== nothing + task_local_storage()[:SOURCE_PATH] = source + end + + try + Base.include(Base.__toplevel__, input) + catch ex + precompilableerror(ex) || rethrow() + @debug "Aborting `create_expr_cache'" exception=(ErrorException("Declaration of __precompile__(false) not allowed"), catch_backtrace()) + exit(125) # we define status = 125 means PrecompileableError + end +end + +@assert precompile(include_package_for_output, (String,Vector{String},Vector{String},Vector{String},typeof(_concrete_dependencies),NTuple{2,UInt64},Nothing)) +@assert precompile(include_package_for_output, (String,Vector{String},Vector{String},Vector{String},typeof(_concrete_dependencies),NTuple{2,UInt64},String)) + function create_expr_cache(input::String, output::String, concrete_deps::typeof(_concrete_dependencies), uuid::Union{Nothing,UUID}) rm(output, force=true) # Remove file if it exists - code_object = """ - while !eof(stdin) - code = readuntil(stdin, '\\0') - eval(Meta.parse(code)) + depot_path = map(abspath, DEPOT_PATH) + dl_load_path = map(abspath, DL_LOAD_PATH) + load_path = map(abspath, Base.load_path()) + path_sep = Sys.iswindows() ? ';' : ':' + any(path -> path_sep in path, load_path) && + error("LOAD_PATH entries cannot contain $(repr(path_sep))") + + deps_strs = String[] + for (pkg, build_id) in concrete_deps + pkg_str = if pkg.uuid === nothing + "Base.PkgId($(repr(pkg.name)))" + else + "Base.PkgId(Base.UUID(\"$(pkg.uuid)\"), $(repr(pkg.name)))" end - """ + push!(deps_strs, "$pkg_str => $(repr(build_id))") + end + deps = repr(eltype(concrete_deps)) * "[" * join(deps_strs, ",") * "]" + + uuid_tuple = uuid === nothing ? (UInt64(0), UInt64(0)) : convert(NTuple{2, UInt64}, uuid) io = open(pipeline(`$(julia_cmd()) -O0 --output-ji $output --output-incremental=yes --startup-file=no --history-file=no --warn-overwrite=yes --color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no") - --eval $code_object`, stderr=stderr), + --eval 'eval(Meta.parse(read(stdin,String)))'`, stderr=stderr), "w", stdout) - in = io.in - try - write(in, """ - begin - $(Base.load_path_setup_code()) - Base._track_dependencies[] = true - Base.empty!(Base._concrete_dependencies) - """) - for (pkg, build_id) in concrete_deps - pkg_str = if pkg.uuid === nothing - "Base.PkgId($(repr(pkg.name)))" - else - "Base.PkgId(Base.UUID(\"$(pkg.uuid)\"), $(repr(pkg.name)))" - end - write(in, "Base.push!(Base._concrete_dependencies, $pkg_str => $(repr(build_id)))\n") - end - write(io, "end\0") - uuid_tuple = uuid === nothing ? (0, 0) : convert(NTuple{2, UInt64}, uuid) - write(in, "ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, $uuid_tuple)\0") - source = source_path(nothing) - if source !== nothing - write(in, "task_local_storage()[:SOURCE_PATH] = $(repr(source))\0") - end - write(in, """ - try - Base.include(Base.__toplevel__, $(repr(abspath(input)))) - catch ex - Base.precompilableerror(ex) || Base.rethrow() - Base.@debug "Aborting `createexprcache'" exception=(Base.ErrorException("Declaration of __precompile__(false) not allowed"), Base.catch_backtrace()) - Base.exit(125) # we define status = 125 means PrecompileableError - end\0""") - # TODO: cleanup is probably unnecessary here - if source !== nothing - write(in, "delete!(task_local_storage(), :SOURCE_PATH)\0") - end - write(in, "ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, (0, 0))\0") - close(in) - catch - close(in) - process_running(io) && Timer(t -> kill(io), 5.0) # wait a short time before killing the process to give it a chance to clean up on its own first - rethrow() - end + # write data over stdin to avoid the (unlikely) case of exceeding max command line size + write(io.in, """ + Base.include_package_for_output($(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), + $(repr(load_path)), $deps, $(repr(uuid_tuple)), $(repr(source_path(nothing)))) + """) + close(io.in) return io end +@assert precompile(create_expr_cache, (String, String, typeof(_concrete_dependencies), Nothing)) +@assert precompile(create_expr_cache, (String, String, typeof(_concrete_dependencies), UUID)) + function compilecache_path(pkg::PkgId)::String entrypath, entryfile = cache_file_entry(pkg) cachepath = joinpath(DEPOT_PATH[1], entrypath) From 6b2ffd39135ff2a13a2ae8fa188da5ee0ac6f8c5 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 13 Jun 2020 06:42:53 -0500 Subject: [PATCH 157/232] Fix some invalidations from extending `==(::Any, ::SomeType)` (#36255) --- base/errorshow.jl | 6 +-- base/meta.jl | 2 +- base/show.jl | 47 ++++++++++++---------- stdlib/Distributed/src/clusterserialize.jl | 2 +- stdlib/REPL/src/LineEdit.jl | 4 +- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 05c6c6d787ecd..fb73d480261c3 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -636,8 +636,8 @@ function show_backtrace(io::IO, t::Vector) isempty(filtered) && return if length(filtered) == 1 && StackTraces.is_top_level_frame(filtered[1][1]) - f = filtered[1][1] - if f.line == 0 && f.file == Symbol("") + f = filtered[1][1]::StackFrame + if f.line == 0 && f.file === Symbol("") # don't show a single top-level frame with no location info return end @@ -693,7 +693,7 @@ function _simplify_include_frames(trace) # Hack: allow `mod==nothing` as a workaround for inlined functions. # TODO: Fix this by improving debug info. if mod in (Base,Core,nothing) && 1+first_ignored-i <= 5 - if frame.func == :eval + if frame.func === :eval kept_frames[i:first_ignored] .= false first_ignored = nothing end diff --git a/base/meta.jl b/base/meta.jl index 4226b754bdbc3..4cf2ac3676dcd 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -63,7 +63,7 @@ true """ isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head isexpr(@nospecialize(ex), heads::Union{Set,Vector,Tuple}) = isa(ex, Expr) && in(ex.head, heads) -isexpr(@nospecialize(ex), heads, n::Int) = isexpr(ex, heads) && length(ex.args) == n +isexpr(@nospecialize(ex), heads, n::Int) = isexpr(ex, heads) && length((ex::Expr).args) == n """ Meta.show_sexpr([io::IO,], ex) diff --git a/base/show.jl b/base/show.jl index f8b87b2c5b248..e5aae45fcab4c 100644 --- a/base/show.jl +++ b/base/show.jl @@ -965,7 +965,7 @@ function operator_associativity(s::Symbol) end is_expr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && (ex.head === head) -is_expr(@nospecialize(ex), head::Symbol, n::Int) = is_expr(ex, head) && length(ex.args) == n +is_expr(@nospecialize(ex), head::Symbol, n::Int) = is_expr(ex, head) && length((ex::Expr).args) == n is_quoted(ex) = false is_quoted(ex::QuoteNode) = true @@ -1031,13 +1031,15 @@ function show_list(io::IO, items, sep, indent::Int, prec::Int=0, quote_level::In !first && print(io, sep) parens = !is_quoted(item) && (first && prec >= prec_power && - ((item isa Expr && item.head === :call && item.args[1] in uni_ops) || + ((item isa Expr && item.head === :call && (callee = item.args[1]; isa(callee, Symbol) && callee in uni_ops)) || (item isa Real && item < 0))) || (enclose_operators && item isa Symbol && isoperator(item)) parens && print(io, '(') if kw && is_expr(item, :kw, 2) + item = item::Expr show_unquoted(io, Expr(:(=), item.args[1], item.args[2]), indent, parens ? 0 : prec, quote_level) elseif kw && is_expr(item, :(=), 2) + item = item::Expr show_unquoted_expr_fallback(io, item, indent, quote_level) else show_unquoted(io, item, indent, parens ? 0 : prec, quote_level) @@ -1261,7 +1263,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In # . print(io, '.') # item - parens = !(field isa Symbol) || (field in quoted_syms) + parens = !(field isa Symbol) || (field::Symbol in quoted_syms) quoted = parens || isoperator(field) quoted && print(io, ':') parens && print(io, '(') @@ -1284,13 +1286,14 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In elseif head === :tuple print(io, "(") if nargs > 0 && is_expr(args[1], :parameters) + arg1 = args[1]::Expr show_list(io, args[2:end], ", ", indent, 0, quote_level) nargs == 2 && print(io, ',') print(io, ";") - if !isempty(args[1].args) + if !isempty(arg1.args) print(io, " ") end - show_list(io, args[1].args, ", ", indent, 0, quote_level, false, true) + show_list(io, arg1.args, ", ", indent, 0, quote_level, false, true) else show_list(io, args, ", ", indent, 0, quote_level) nargs == 1 && print(io, ',') @@ -1331,7 +1334,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In func = args[1] fname = isa(func, GlobalRef) ? func.name : func func_prec = operator_precedence(fname) - if func_prec > 0 || fname in uni_ops + if func_prec > 0 || (isa(fname, Symbol) && fname in uni_ops) func = fname end func_args = args[2:end] @@ -1343,7 +1346,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In # scalar multiplication (i.e. "100x") elseif (func === :* && length(func_args) == 2 && isa(func_args[1], Union{Int, Int64, Float32, Float64}) && - isa(func_args[2], Symbol) && !in(string(func_args[2])[1], ('e', 'E', 'f'))) + isa(func_args[2], Symbol) && !in(string(func_args[2]::Symbol)[1], ('e', 'E', 'f'))) if func_prec <= prec show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec, quote_level) else @@ -1363,7 +1366,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In # binary operator (i.e. "x + y") elseif func_prec > 0 # is a binary operator na = length(func_args) - if (na == 2 || (na > 2 && func in (:+, :++, :*)) || (na == 3 && func === :(:))) && + if (na == 2 || (na > 2 && isa(func, Symbol) && func in (:+, :++, :*)) || (na == 3 && func === :(:))) && all(!isa(a, Expr) || a.head !== :... for a in func_args) sep = func === :(:) ? "$func" : " $func " @@ -1431,7 +1434,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In # function calls need to transform the function from :call to :calldecl # so that operators are printed correctly elseif head === :function && nargs==2 && is_expr(args[1], :call) - show_block(IOContext(io, beginsym=>false), head, Expr(:calldecl, args[1].args...), args[2], indent, quote_level) + show_block(IOContext(io, beginsym=>false), head, Expr(:calldecl, (args[1]::Expr).args...), args[2], indent, quote_level) print(io, "end") elseif (head === :function || head === :macro) && nargs == 1 @@ -1443,8 +1446,8 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In iob = IOContext(io, beginsym=>false) show_unquoted(iob, args[1], indent, -1, quote_level) print(io, " do ") - show_list(iob, args[2].args[1].args, ", ", 0, 0, quote_level) - for stmt in args[2].args[2].args + show_list(iob, (((args[2]::Expr).args[1])::Expr).args, ", ", 0, 0, quote_level) + for stmt in (((args[2]::Expr).args[2])::Expr).args print(io, '\n', " "^(indent + indent_width)) show_unquoted(iob, stmt, indent + indent_width, -1, quote_level) end @@ -1463,10 +1466,11 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In elseif (head === :if || head === :elseif) && nargs == 3 iob = IOContext(io, beginsym=>false) show_block(iob, head, args[1], args[2], indent, quote_level) - if isa(args[3],Expr) && args[3].head === :elseif - show_unquoted(iob, args[3], indent, prec, quote_level) + arg3 = args[3] + if isa(arg3, Expr) && arg3.head === :elseif + show_unquoted(iob, arg3::Expr, indent, prec, quote_level) else - show_block(iob, "else", args[3], indent, quote_level) + show_block(iob, "else", arg3, indent, quote_level) print(io, "end") end @@ -1562,7 +1566,8 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In show_sym(io, arg1, allow_macroname=true) elseif arg1 isa GlobalRef show_globalref(io, arg1, allow_macroname=true) - elseif is_expr(arg1, :(.)) && length(arg1.args) == 2 + elseif is_expr(arg1, :(.)) && length((arg1::Expr).args) == 2 + arg1 = arg1::Expr m = arg1.args[1] if m isa Symbol || m isa GlobalRef || is_expr(m, :(.), 2) show_unquoted(io, m) @@ -1573,9 +1578,9 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In end print(io, '.') if is_expr(arg1.args[2], :quote) - mname = arg1.args[2].args[1] + mname = (arg1.args[2]::Expr).args[1] else - mname = arg1.args[2].value + mname = (arg1.args[2]::QuoteNode).value end if mname isa Symbol show_sym(io, mname, allow_macroname=true) @@ -1593,10 +1598,10 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In iob = IOContext(io, beginsym=>false) show_block(iob, "try", args[1], indent, quote_level) if is_expr(args[3], :block) - show_block(iob, "catch", args[2] === false ? Any[] : args[2], args[3], indent, quote_level) + show_block(iob, "catch", args[2] === false ? Any[] : args[2], args[3]::Expr, indent, quote_level) end if nargs >= 4 && is_expr(args[4], :block) - show_block(iob, "finally", Any[], args[4], indent, quote_level) + show_block(iob, "finally", Any[], args[4]::Expr, indent, quote_level) end print(io, "end") @@ -1627,9 +1632,9 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In elseif head === :quote && nargs == 1 && isa(args[1], Symbol) show_unquoted_quote_expr(IOContext(io, beginsym=>false), args[1]::Symbol, indent, 0, quote_level+1) - elseif head === :quote && !get(io, :unquote_fallback, true) + elseif head === :quote && !(get(io, :unquote_fallback, true)::Bool) if nargs == 1 && is_expr(args[1], :block) - show_block(IOContext(io, beginsym=>false), "quote", Expr(:quote, args[1].args...), indent, + show_block(IOContext(io, beginsym=>false), "quote", Expr(:quote, (args[1]::Expr).args...), indent, quote_level+1) print(io, "end") elseif nargs == 1 diff --git a/stdlib/Distributed/src/clusterserialize.jl b/stdlib/Distributed/src/clusterserialize.jl index c50393caeb0f5..859824f40419e 100644 --- a/stdlib/Distributed/src/clusterserialize.jl +++ b/stdlib/Distributed/src/clusterserialize.jl @@ -143,7 +143,7 @@ end # d) is a bits type function syms_2b_sent(s::ClusterSerializer, identifier) lst = Symbol[] - check_syms = get(s.glbs_in_tnobj, identifier, []) + check_syms = get(s.glbs_in_tnobj, identifier, Symbol[]) for sym in check_syms v = getfield(Main, sym) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index e3b1e8fe8b2ff..2084a8d2e4bf5 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -378,7 +378,7 @@ prompt_string(f::Function) = Base.invokelatest(f) refresh_multi_line(s::ModeState; kw...) = refresh_multi_line(terminal(s), s; kw...) refresh_multi_line(termbuf::TerminalBuffer, s::ModeState; kw...) = refresh_multi_line(termbuf, terminal(s), s; kw...) -refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState; kw...) = (@assert term == terminal(s); refresh_multi_line(termbuf,s; kw...)) +refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState; kw...) = (@assert term === terminal(s); refresh_multi_line(termbuf,s; kw...)) function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf::IOBuffer, state::InputAreaState, prompt = ""; @@ -777,7 +777,7 @@ function edit_insert_newline(s::PromptState, align::Int = 0 - options(s).auto_in #else # align = 0 end - align < 0 && (align = 0) + align < 0 && (align = 0) edit_insert(buf, '\n' * ' '^align) refresh_line(s) # updating s.last_newline should happen after refresh_line(s) which can take From 5ecb8e2c158dbadd9ceeaa0cf1e581ceee9d7063 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Sat, 13 Jun 2020 14:08:23 -0400 Subject: [PATCH 158/232] When using mismatched deps version, add a removal suggestion (#36247) --- deps/tools/uninstallers.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps/tools/uninstallers.mk b/deps/tools/uninstallers.mk index 421626f3e6559..2fde49503992a 100644 --- a/deps/tools/uninstallers.mk +++ b/deps/tools/uninstallers.mk @@ -28,4 +28,7 @@ $(addprefix version-check-,$(DEP_LIBS_STAGED)) : version-check-% : install-% [ "$(UNINSTALL_$*)" != "`cat $(build_prefix)/manifest/$*`" ]) ; then \ echo "WARNING: using mismatched version for $$(cat $(build_prefix)/manifest/$*):" ; \ echo " want $(UNINSTALL_$*)" ; \ + echo " To resolve this warning, you could try either of the following suggestions: " ; \ + echo " 1. Run the following command: make -C deps uninstall" ; \ + echo " 2. Remove the following directory: $(JULIAHOME)/usr" ; \ fi From 446618dd7a2df8976be1a9c6be6334f1c27cc55d Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Sun, 14 Jun 2020 17:17:23 +0200 Subject: [PATCH 159/232] fix #36270: 3-arg `dot` missing in documentation page (#36271) --- stdlib/LinearAlgebra/docs/src/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index a195282261149..a1e8b9b95972d 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -317,6 +317,7 @@ LinearAlgebra.SingularException LinearAlgebra.PosDefException LinearAlgebra.ZeroPivotException LinearAlgebra.dot +LinearAlgebra.dot(::Any, ::Any, ::Any) LinearAlgebra.cross LinearAlgebra.factorize LinearAlgebra.Diagonal From 13b07fcc57d268b37234d8b8753b4ef3447be8db Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Sun, 14 Jun 2020 19:33:48 +0200 Subject: [PATCH 160/232] mention methodtable Ctrl+Q trick in methodshow (#35556) Co-authored-by: Takafumi Arakaki Co-authored-by: Jameson Nash --- base/errorshow.jl | 13 ++++++------- base/methodshow.jl | 2 ++ stdlib/REPL/src/REPL.jl | 21 ++++++++++++++++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index fb73d480261c3..bb4f94c235376 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -538,13 +538,10 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=() end end -# Contains file name and file number. Gets set when a backtrace -# or methodlist is shown. Used by the REPL to make it possible to open -# the location of a stackframe/method in the editor. -global LAST_SHOWN_LINE_INFOS = Tuple{String, Int}[] - function show_trace_entry(io, frame, n; prefix = "") - push!(LAST_SHOWN_LINE_INFOS, (string(frame.file), frame.line)) + if haskey(io, :LAST_SHOWN_LINE_INFOS) + push!(io[:LAST_SHOWN_LINE_INFOS], (string(frame.file), frame.line)) + end print(io, "\n", prefix) show(io, frame, full_path=true) n > 1 && print(io, " (repeats ", n, " times)") @@ -631,7 +628,9 @@ function show_reduced_backtrace(io::IO, t::Vector, with_prefix::Bool) end function show_backtrace(io::IO, t::Vector) - resize!(LAST_SHOWN_LINE_INFOS, 0) + if haskey(io, :LAST_SHOWN_LINE_INFOS) + resize!(io[:LAST_SHOWN_LINE_INFOS], 0) + end filtered = process_backtrace(t) isempty(filtered) && return diff --git a/base/methodshow.jl b/base/methodshow.jl index e878660a0305e..2762acfb52a5c 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -240,6 +240,7 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru end n = rest = 0 local last + LAST_SHOWN_LINE_INFOS = get(io, :LAST_SHOWN_LINE_INFOS, Tuple{String,Int}[]) resize!(LAST_SHOWN_LINE_INFOS, 0) for meth in ms @@ -373,6 +374,7 @@ show(io::IO, mime::MIME"text/html", mt::Core.MethodTable) = show(io, mime, Metho # pretty-printing of AbstractVector{Method} function show(io::IO, mime::MIME"text/plain", mt::AbstractVector{Method}) + LAST_SHOWN_LINE_INFOS = get(io, :LAST_SHOWN_LINE_INFOS, Tuple{String,Int}[]) resize!(LAST_SHOWN_LINE_INFOS, 0) first = true for (i, m) in enumerate(mt) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 5259a4fd717e8..20c5b2595e8a7 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -211,8 +211,22 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x) io = foldl(IOContext, d.repl.options.iocontext, init=IOContext(io, :limit => true, :module => Main)) end + + infos = Tuple{String,Int}[] + io = IOContext(io, :LAST_SHOWN_LINE_INFOS => infos) + show(io, mime, x) println(io) + + if !isempty(infos) + d.repl.last_shown_line_infos = infos + println( + io, + "\nTo edit a specific method, type the corresponding number into the " * + "REPL and press Ctrl+Q", + ) + end + nothing end display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x) @@ -430,11 +444,12 @@ mutable struct LineEditREPL <: AbstractREPL specialdisplay::Union{Nothing,AbstractDisplay} options::Options mistate::Union{MIState,Nothing} + last_shown_line_infos::Vector{Tuple{String,Int}} interface::ModalInterface backendref::REPLBackendRef LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) = new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, - in_help,envcolors,false,nothing, Options(), nothing) + in_help,envcolors,false,nothing, Options(), nothing, Tuple{String,Int}[]) end outstream(r::LineEditREPL) = r.t specialdisplay(r::LineEditREPL) = r.specialdisplay @@ -1092,10 +1107,10 @@ function setup_interface( end, # Open the editor at the location of a stackframe or method - # This is accessing a global variable that gets set in + # This is accessing a contextual variable that gets set in # the show_backtrace and show_method_table functions. "^Q" => (s, o...) -> begin - linfos = Base.LAST_SHOWN_LINE_INFOS + linfos = repl.last_shown_line_infos str = String(take!(LineEdit.buffer(s))) n = tryparse(Int, str) n === nothing && @goto writeback From 25dc696bdd378d3fdfb83c8e33db0c84ed6adde5 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Mon, 15 Jun 2020 17:43:26 +0200 Subject: [PATCH 161/232] faster repeat(::AbstractArray ...) (#35944) * faster repeat(::AbstractArray ...) * Update base/abstractarraymath.jl Co-authored-by: Matt Bauman * Update base/abstractarraymath.jl Co-authored-by: Matt Bauman * Update base/abstractarraymath.jl Co-authored-by: Matt Bauman * fix * better repeat errors Co-authored-by: Matt Bauman --- base/abstractarraymath.jl | 211 +++++++++++++++++++++----------------- test/arrayops.jl | 2 + 2 files changed, 119 insertions(+), 94 deletions(-) diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl index 32bd3c2d20c4f..e8d9348da6767 100644 --- a/base/abstractarraymath.jl +++ b/base/abstractarraymath.jl @@ -270,30 +270,8 @@ julia> repeat([1, 2, 3], 2, 3) 3 3 3 ``` """ -repeat(a::AbstractArray, counts::Integer...) = repeat(a, outer = counts) - -function repeat(a::AbstractVecOrMat, m::Integer, n::Integer=1) - o, p = size(a,1), size(a,2) - b = similar(a, o*m, p*n) - for j=1:n - d = (j-1)*p+1 - R = d:d+p-1 - for i=1:m - c = (i-1)*o+1 - b[c:c+o-1, R] = a - end - end - return b -end - -function repeat(a::AbstractVector, m::Integer) - o = length(a) - b = similar(a, o*m) - for i=1:m - c = (i-1)*o+1 - b[c:c+o-1] = a - end - return b +function repeat(A::AbstractArray, counts...) + return _RepeatInnerOuter.repeat(A, outer=counts) end """ @@ -330,89 +308,134 @@ julia> repeat([1 2; 3 4], inner=(2, 1), outer=(1, 3)) ``` """ function repeat(A::AbstractArray; inner = nothing, outer = nothing) - return _repeat_inner_outer(A, inner, outer) + return _RepeatInnerOuter.repeat(A, inner=inner, outer=outer) +end + +module _RepeatInnerOuter + +function repeat(arr; inner=nothing, outer=nothing) + check(arr, inner, outer) + arr, inner, outer = resolve(arr, inner, outer) + repeat_inner_outer(arr, inner, outer) +end + +to_tuple(t::Tuple) = t +to_tuple(x::Integer) = (x,) +to_tuple(itr) = tuple(itr...) + +function pad(a, b) + N = max(length(a), length(b)) + Base.fill_to_length(a, 1, Val(N)), Base.fill_to_length(b, 1, Val(N)) +end +function pad(a, b, c) + N = max(max(length(a), length(b)), length(c)) + Base.fill_to_length(a, 1, Val(N)), Base.fill_to_length(b, 1, Val(N)), Base.fill_to_length(c, 1, Val(N)) +end + +function resolve(arr::AbstractArray{<:Any, N}, inner::NTuple{N, Any}, outer::NTuple{N,Any}) where {N} + arr, inner, outer +end +function resolve(arr, inner, outer) + dims, inner, outer = pad(size(arr), to_tuple(inner), to_tuple(outer)) + reshape(arr, dims), inner, outer +end +function resolve(arr, inner::Nothing, outer::Nothing) + return arr, inner, outer +end +function resolve(arr, inner::Nothing, outer) + dims, outer = pad(size(arr), to_tuple(outer)) + reshape(arr, dims), inner, outer +end +function resolve(arr, inner, outer::Nothing) + dims, inner = pad(size(arr), to_tuple(inner)) + reshape(arr, dims), inner, outer end -# we have optimized implementations of these cases above -_repeat_inner_outer(A::AbstractVecOrMat, ::Nothing, r::Union{Tuple{Integer},Tuple{Integer,Integer}}) = repeat(A, r...) -_repeat_inner_outer(A::AbstractVecOrMat, ::Nothing, r::Integer) = repeat(A, r) - -_repeat_inner_outer(A, ::Nothing, ::Nothing) = A -_repeat_inner_outer(A, ::Nothing, outer) = _repeat(A, ntuple(n->1, Val(ndims(A))), rep_kw2tup(outer)) -_repeat_inner_outer(A, inner, ::Nothing) = _repeat(A, rep_kw2tup(inner), ntuple(n->1, Val(ndims(A)))) -_repeat_inner_outer(A, inner, outer) = _repeat(A, rep_kw2tup(inner), rep_kw2tup(outer)) - -rep_kw2tup(n::Integer) = (n,) -rep_kw2tup(v::AbstractArray{<:Integer}) = (v...,) -rep_kw2tup(t::Tuple) = t - -rep_shapes(A, i, o) = _rshps((), (), size(A), i, o) - -_rshps(shp, shp_i, ::Tuple{}, ::Tuple{}, ::Tuple{}) = (shp, shp_i) -@inline _rshps(shp, shp_i, ::Tuple{}, ::Tuple{}, o) = - _rshps((shp..., o[1]), (shp_i..., 1), (), (), tail(o)) -@inline _rshps(shp, shp_i, ::Tuple{}, i, ::Tuple{}) = (n = i[1]; - _rshps((shp..., n), (shp_i..., n), (), tail(i), ())) -@inline _rshps(shp, shp_i, ::Tuple{}, i, o) = (n = i[1]; - _rshps((shp..., n * o[1]), (shp_i..., n), (), tail(i), tail(o))) -@inline _rshps(shp, shp_i, sz, i, o) = (n = sz[1] * i[1]; - _rshps((shp..., n * o[1]), (shp_i..., n), tail(sz), tail(i), tail(o))) -_rshps(shp, shp_i, sz, ::Tuple{}, ::Tuple{}) = - (n = length(shp); N = n + length(sz); _reperr("inner", n, N)) -_rshps(shp, shp_i, sz, ::Tuple{}, o) = - (n = length(shp); N = n + length(sz); _reperr("inner", n, N)) -_rshps(shp, shp_i, sz, i, ::Tuple{}) = - (n = length(shp); N = n + length(sz); _reperr("outer", n, N)) -_reperr(s, n, N) = throw(ArgumentError("number of " * s * " repetitions " * - "($n) cannot be less than number of dimensions of input ($N)")) - -_negreperr(n) = throw(ArgumentError("number of $n repetitions" * - "cannot be negative")) - -@noinline function _repeat(A::AbstractArray, inner, outer) - any(<(0), inner) && _negreperr("inner") - any(<(0), outer) && _negreperr("outer") - - shape, inner_shape = rep_shapes(A, inner, outer) - - R = similar(A, shape) - if any(iszero, shape) - return R +function check(arr, inner, outer) + if inner !== nothing + # TODO: Currently one based indexing is demanded for inner !== nothing, + # but not for outer !== nothing. Decide for something consistent. + Base.require_one_based_indexing(arr) + if any(<(0), inner) + throw(ArgumentError("no inner repetition count may be negative; got $inner")) + end + if length(inner) < ndims(arr) + throw(ArgumentError("number of inner repetitions ($(length(inner))) cannot be less than number of dimensions of input array ($(ndims(arr)))")) + end + end + if outer !== nothing + if any(<(0), outer) + throw(ArgumentError("no outer repetition count may be negative; got $outer")) + end + if (length(outer) < ndims(arr)) && (inner !== nothing) + throw(ArgumentError("number of outer repetitions ($(length(outer))) cannot be less than number of dimensions of input array ($(ndims(arr)))")) + end end +end - # fill the first inner block - if all(isequal(1), inner) - idxs = (axes(A)..., ntuple(n->OneTo(1), ndims(R)-ndims(A))...) # keep dimension consistent - R[idxs...] = A - else - inner_indices = [1:n for n in inner] - for c in CartesianIndices(axes(A)) - for i in 1:ndims(A) - n = inner[i] - inner_indices[i] = (1:n) .+ ((c[i] - 1) * n) - end - fill!(view(R, inner_indices...), A[c]) +repeat_inner_outer(arr, inner::Nothing, outer::Nothing) = arr +repeat_inner_outer(arr, ::Nothing, outer) = repeat_outer(arr, outer) +repeat_inner_outer(arr, inner, ::Nothing) = repeat_inner(arr, inner) +repeat_inner_outer(arr, inner, outer) = repeat_outer(repeat_inner(arr, inner), outer) + +function repeat_outer(a::AbstractMatrix, (m,n)::NTuple{2, Any}) + o, p = size(a,1), size(a,2) + b = similar(a, o*m, p*n) + for j=1:n + d = (j-1)*p+1 + R = d:d+p-1 + for i=1:m + c = (i-1)*o+1 + @inbounds b[c:c+o-1, R] = a end end + return b +end - # fill the outer blocks along each dimension - if all(isequal(1), outer) - return R +function repeat_outer(a::AbstractVector, (m,)::Tuple{Any}) + o = length(a) + b = similar(a, o*m) + for i=1:m + c = (i-1)*o+1 + @inbounds b[c:c+o-1] = a end - src_indices = [1:n for n in inner_shape] - dest_indices = copy(src_indices) - for i in eachindex(outer) - B = view(R, src_indices...) - for j in 2:outer[i] - dest_indices[i] = dest_indices[i] .+ inner_shape[i] - R[dest_indices...] = B + return b +end + +function repeat_outer(arr::AbstractArray{<:Any,N}, dims::NTuple{N,Any}) where {N} + insize = size(arr) + outsize = map(*, insize, dims) + out = similar(arr, outsize) + for I in CartesianIndices(arr) + for J in CartesianIndices(dims) + TIJ = map(Tuple(I), Tuple(J), insize) do i, j, d + i + d * (j-1) + end + IJ = CartesianIndex(TIJ) + @inbounds out[IJ] = arr[I] end - src_indices[i] = dest_indices[i] = 1:shape[i] end + return out +end - return R +function repeat_inner(arr, inner) + basedims = size(arr) + outsize = map(*, size(arr), inner) + out = similar(arr, outsize) + for I in CartesianIndices(arr) + for J in CartesianIndices(inner) + TIJ = map(Tuple(I), Tuple(J), inner) do i, j, d + (i-1) * d + j + end + IJ = CartesianIndex(TIJ) + @inbounds out[IJ] = arr[I] + end + end + return out end +end#module + """ eachrow(A::AbstractVecOrMat) diff --git a/test/arrayops.jl b/test/arrayops.jl index 9ecd9f4a0bab2..059a5ab690e08 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -974,6 +974,8 @@ end 3 4], inner=(2, 2), outer=(2,)) @test_throws ArgumentError repeat([1, 2], inner=(1, -1), outer=(1, -1)) + @test_throws ArgumentError repeat(OffsetArray(rand(2), 1), inner=(2,)) + A = reshape(1:8, 2, 2, 2) R = repeat(A, inner = (1, 1, 2), outer = (1, 1, 1)) T = reshape([1:4; 1:4; 5:8; 5:8], 2, 2, 4) From 8d91b1123f4be3c03e587710fa8b0777762c5e07 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Mon, 15 Jun 2020 18:34:40 +0200 Subject: [PATCH 162/232] fix #36272: Error with optional argument in anonymous function defined in macro (#36273) --- src/macroexpand.scm | 7 +++++-- test/syntax.jl | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/macroexpand.scm b/src/macroexpand.scm index 13d8cf4104e86..0777c9c61846e 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -210,7 +210,7 @@ ((atom? v) '()) (else (case (car v) - ((... kw |::|) (try-arg-name (cadr v))) + ((... kw |::| =) (try-arg-name (cadr v))) ((escape) (list v)) ((hygienic-scope) (try-arg-name (cadr v))) ((meta) ;; allow certain per-argument annotations @@ -275,6 +275,9 @@ ,@(map (lambda (x) (resolve-expansion-vars-with-new-env x env m parent-scope #t)) (cddr e)))) + ((tuple) `(tuple ,@(map (lambda (x) + (resolve-expansion-vars-with-new-env x env m parent-scope #t)) + (cdr e)))) (else (other e)))) (define (new-expansion-env-for x env (outermost #f)) @@ -367,7 +370,7 @@ (resolve-expansion-vars- x env m parent-scope #f))) (cdr e)))) - ((= function) + ((= function ->) (if (and (pair? (cadr e)) (function-def? e)) ;; in (kw x 1) inside an arglist, the x isn't actually a kwarg `(,(car e) ,(resolve-in-function-lhs (cadr e) env m parent-scope inarg) diff --git a/test/syntax.jl b/test/syntax.jl index 0c907a1d2deb2..35178057b1614 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2268,3 +2268,9 @@ end # issue #36196 @test_throws ParseError("\"for\" at none:1 expected \"end\", got \")\"") Meta.parse("(for i=1; println())") @test_throws ParseError("\"try\" at none:1 expected \"end\", got \")\"") Meta.parse("(try i=1; println())") + +# issue #36272 +macro m36272() + :((a, b=1) -> a*b) +end +@test @m36272()(1) == 1 From 4331b240e5568dabbec3ed02fd4f7820bbc543fa Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Mon, 15 Jun 2020 14:44:33 -0400 Subject: [PATCH 163/232] Add keyword argument constant propogation to News (#36292) I think https://github.com/JuliaLang/julia/pull/35976 is a pretty big deal since it makes all of SciML suddenly infer a lot better, and probably another big library that's splatting keyword arguments. So I added a note to the compiler/runtime improvements. --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index b6ffe8c872af5..1671dad1c5685 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,7 @@ Compiler/Runtime improvements * All platforms can now use `@executable_path` within `jl_load_dynamic_library()`. This allows executable-relative paths to be embedded within executables on all platforms, not just MacOS, which the syntax is borrowed from. ([#35627]) +* Constant propogation now occurs through keyword arguments ([#35976]) Command-line option changes --------------------------- From 585310382eca6885aaebd02d3f3faab1cccffeb3 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 15 Jun 2020 13:55:11 -0500 Subject: [PATCH 164/232] Finish adding types to fields in REPL (#36281) This finishes the work started in #22377 --- stdlib/REPL/src/LineEdit.jl | 56 ++++++++++++++++++------------------- stdlib/REPL/src/REPL.jl | 10 +++---- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 2084a8d2e4bf5..c34728b63d373 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -3,8 +3,9 @@ module LineEdit import ..REPL -using ..Terminals +using REPL: AbstractREPL +using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, getY, clear_line, beep @@ -13,6 +14,8 @@ using Base: something abstract type TextInterface end abstract type ModeState end +abstract type HistoryProvider end +abstract type CompletionProvider end export run_interface, Prompt, ModalInterface, transition, reset_state, edit_insert, keymap @@ -31,11 +34,11 @@ mutable struct Prompt <: TextInterface # Same as prefix except after the prompt prompt_suffix::Union{String,Function} keymap_dict::Dict{Char} - repl # ::AbstractREPL - complete # ::REPLCompletionProvider + repl::Union{AbstractREPL,Nothing} + complete::CompletionProvider on_enter::Function on_done::Function - hist # ::REPLHistoryProvider + hist::HistoryProvider sticky::Bool end @@ -141,9 +144,6 @@ function input_string_newlines_aftercursor(s::PromptState) return count(c->(c == '\n'), rest) end -abstract type HistoryProvider end -abstract type CompletionProvider end - struct EmptyCompletionProvider <: CompletionProvider end struct EmptyHistoryProvider <: HistoryProvider end @@ -1602,9 +1602,16 @@ const escape_defaults = merge!( AnyDict("\e[$(c)l" => nothing for c in 1:20) ) +mutable struct HistoryPrompt <: TextInterface + hp::HistoryProvider + complete::CompletionProvider + keymap_dict::Dict{Char,Any} + HistoryPrompt(hp) = new(hp, EmptyCompletionProvider()) +end + mutable struct SearchState <: ModeState terminal::AbstractTerminal - histprompt # ::HistoryPrompt + histprompt::HistoryPrompt #rsearch (true) or ssearch (false) backward::Bool query_buffer::IOBuffer @@ -1617,6 +1624,8 @@ mutable struct SearchState <: ModeState new(terminal, histprompt, backward, query_buffer, response_buffer, false, InputAreaState(0,0)) end +init_state(terminal, p::HistoryPrompt) = SearchState(terminal, p, true, IOBuffer(), IOBuffer()) + terminal(s::SearchState) = s.terminal function update_display_buffer(s::SearchState, data) @@ -1654,18 +1663,20 @@ function reset_state(s::SearchState) nothing end -mutable struct HistoryPrompt <: TextInterface - hp # ::HistoryProvider - complete # ::CompletionProvider +# a meta-prompt that presents itself as parent_prompt, but which has an independent keymap +# for prefix searching +mutable struct PrefixHistoryPrompt <: TextInterface + hp::HistoryProvider + parent_prompt::Prompt + complete::CompletionProvider keymap_dict::Dict{Char,Any} - HistoryPrompt(hp) = new(hp, EmptyCompletionProvider()) + PrefixHistoryPrompt(hp, parent_prompt) = + new(hp, parent_prompt, EmptyCompletionProvider()) end -init_state(terminal, p::HistoryPrompt) = SearchState(terminal, p, true, IOBuffer(), IOBuffer()) - mutable struct PrefixSearchState <: ModeState terminal::AbstractTerminal - histprompt # ::HistoryPrompt + histprompt::PrefixHistoryPrompt prefix::String response_buffer::IOBuffer ias::InputAreaState @@ -1678,6 +1689,8 @@ mutable struct PrefixSearchState <: ModeState new(terminal, histprompt, prefix, response_buffer, InputAreaState(0,0), 0) end +init_state(terminal, p::PrefixHistoryPrompt) = PrefixSearchState(terminal, p, "", IOBuffer()) + function show(io::IO, s::PrefixSearchState) print(io, "PrefixSearchState ", isdefined(s,:parent) ? string("(", s.parent, " active)") : "(no parent)", " for ", @@ -1696,19 +1709,6 @@ end input_string(s::PrefixSearchState) = String(take!(copy(s.response_buffer))) -# a meta-prompt that presents itself as parent_prompt, but which has an independent keymap -# for prefix searching -mutable struct PrefixHistoryPrompt <: TextInterface - hp # ::HistoryProvider - parent_prompt::Prompt - complete # ::CompletionProvider - keymap_dict::Dict{Char,Any} - PrefixHistoryPrompt(hp, parent_prompt) = - new(hp, parent_prompt, EmptyCompletionProvider()) -end - -init_state(terminal, p::PrefixHistoryPrompt) = PrefixSearchState(terminal, p, "", IOBuffer()) - write_prompt(terminal, s::PrefixSearchState) = write_prompt(terminal, s.histprompt.parent_prompt) prompt_string(s::PrefixSearchState) = prompt_string(s.histprompt.parent_prompt.prompt) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 20c5b2595e8a7..378dfb1413118 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -36,6 +36,8 @@ import Base: include("Terminals.jl") using .Terminals +abstract type AbstractREPL end + include("LineEdit.jl") using .LineEdit import ..LineEdit: @@ -66,8 +68,6 @@ function __init__() Base.REPL_MODULE_REF[] = REPL end -abstract type AbstractREPL end - answer_color(::AbstractREPL) = "" const JULIA_PROMPT = "julia> " @@ -496,15 +496,15 @@ function complete_line(c::LatexCompletions, s) end mutable struct REPLHistoryProvider <: HistoryProvider - history::Array{String,1} + history::Vector{String} history_file::Union{Nothing,IO} start_idx::Int cur_idx::Int last_idx::Int last_buffer::IOBuffer last_mode::Union{Nothing,Prompt} - mode_mapping::Dict - modes::Array{Symbol,1} + mode_mapping::Dict{Symbol,Prompt} + modes::Vector{Symbol} end REPLHistoryProvider(mode_mapping) = REPLHistoryProvider(String[], nothing, 0, 0, -1, IOBuffer(), From a66ee3f28a743d575b52cbe15b3d49920d36a8e1 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Mon, 15 Jun 2020 14:40:58 -0500 Subject: [PATCH 165/232] Fix description of options to --trace-compile (#36189) --- src/jloptions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jloptions.c b/src/jloptions.c index 8ab0e300cf802..b5a48bfec59fd 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -161,8 +161,8 @@ static const char opts_hidden[] = " --output-bc name Generate LLVM bitcode (.bc)\n" " --output-asm name Generate an assembly file (.s)\n" " --output-incremental=no Generate an incremental output file (rather than complete)\n" - " --trace-compile={stdout,stderr}\n" - " Print precompile statements for methods compiled during execution.\n\n" + " --trace-compile={stderr,name}\n" + " Print precompile statements for methods compiled during execution or save to a path\n\n" ; JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) From 4441245e096431c4210010ee4163cd009ade5ce5 Mon Sep 17 00:00:00 2001 From: Jeffrey Lin Date: Mon, 15 Jun 2020 16:24:03 -0400 Subject: [PATCH 166/232] Show progress indicator when precompiling (#36190) --- contrib/generate_precompile.jl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index be9ad9fb1319a..36b1b567f15a7 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -84,7 +84,6 @@ function generate_precompile_statements() empty!(DEPOT_PATH) end - print("Generating precompile statements...") mktemp() do precompile_file, precompile_file_h # Run a repl process and replay our script pty_slave, pty_master = open_fake_pty() @@ -132,8 +131,12 @@ function generate_precompile_statements() readavailable(output_copy) # Input our script if have_repl - for l in split(precompile_script, '\n'; keepempty=false) + precompile_lines = split(precompile_script, '\n'; keepempty=false) + curr = 0 + for l in precompile_lines sleep(0.1) + curr += 1 + print("\rGenerating precompile statements... $curr/$(length(precompile_lines))") # consume any other output bytesavailable(output_copy) > 0 && readavailable(output_copy) # push our input @@ -145,6 +148,7 @@ function generate_precompile_statements() readuntil(output_copy, "\n") readuntil(output_copy, "> ") end + println() end write(pty_master, "exit()\n") wait(tee) @@ -179,21 +183,26 @@ function generate_precompile_statements() try Base.include_string(PrecompileStagingArea, statement) n_succeeded += 1 + print("\rExecuting precompile statements... $n_succeeded/$(length(statements))") catch # See #28808 # @error "Failed to precompile $statement" end end + println() if have_repl # Seems like a reasonable number right now, adjust as needed # comment out if debugging script @assert n_succeeded > 1500 end - print(" $(length(statements)) generated in ") tot_time = time_ns() - start_time - Base.time_print(tot_time) - print(" (overhead "); Base.time_print(tot_time - (include_time * 1e9)); println(")") + include_time *= 1e9 + gen_time = tot_time - include_time + println("Precompilation complete. Summary:") + print("Total ─────── "); Base.time_print(tot_time); println() + print("Generation ── "); Base.time_print(gen_time); print(" "); show(IOContext(stdout, :compact=>true), gen_time / tot_time * 100); println("%") + print("Execution ─── "); Base.time_print(include_time); print(" "); show(IOContext(stdout, :compact=>true), include_time / tot_time * 100); println("%") end return From 9ed10de1e668ad1bda56bbcda6f60d560c0b55d2 Mon Sep 17 00:00:00 2001 From: Jerry Ling Date: Tue, 16 Jun 2020 08:58:27 -0700 Subject: [PATCH 167/232] docs note testset creates local scope (#36302) --- stdlib/Test/docs/src/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/Test/docs/src/index.md b/stdlib/Test/docs/src/index.md index 64d4d34a30cc5..8c22c3ead20dc 100644 --- a/stdlib/Test/docs/src/index.md +++ b/stdlib/Test/docs/src/index.md @@ -93,6 +93,9 @@ of inputs. In the event a test fails, the default behavior is to throw an except However, it is normally preferable to run the rest of the tests first to get a better picture of how many errors there are in the code being tested. +!!! note + The `@testset` will create a local scope of its own when running the tests in it. + The `@testset` macro can be used to group tests into *sets*. All the tests in a test set will be run, and at the end of the test set a summary will be printed. If any of the tests failed, or could not be evaluated due to an error, the test set will then throw a `TestSetException`. From 1558e76969b2a77a23aafe39881d8b9a79088ae7 Mon Sep 17 00:00:00 2001 From: Lasse Peters Date: Tue, 16 Jun 2020 19:24:22 +0200 Subject: [PATCH 168/232] Fix undef in cat for eltypes that are not <:Number (#36222) --- base/abstractarray.jl | 2 +- test/abstractarray.jl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 4adab5aade0f7..95fb7fdcad943 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1509,7 +1509,7 @@ _cat(dims, X...) = cat_t(promote_eltypeof(X...), X...; dims=dims) catdims = dims2cat(dims) shape = cat_shape(catdims, (), map(cat_size, X)...) A = cat_similar(X[1], T, shape) - if T <: Number && count(!iszero, catdims) > 1 + if count(!iszero, catdims) > 1 fill!(A, zero(T)) end return __cat(A, shape, catdims, X...) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index f7018fe29d6b4..a35667e28ba72 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -610,6 +610,10 @@ function test_cat(::Type{TestAbstractArray}) # 29172 @test_throws ArgumentError cat([1], [2], dims=0) @test_throws ArgumentError cat([1], [2], dims=[5, -3]) + + # 36041 + @test_throws MethodError cat(["a"], ["b"], dims=[1, 2]) + @test cat([1], [1], dims=[1, 2]) == I(2) end function test_ind2sub(::Type{TestAbstractArray}) From dd76c0ecb42ed0428c73ceee6d926ac7bb2c6c18 Mon Sep 17 00:00:00 2001 From: Liozou Date: Tue, 16 Jun 2020 19:27:24 +0200 Subject: [PATCH 169/232] Fix bug on introspection macros on broadcasting syntax (#36267) --- stdlib/InteractiveUtils/src/macros.jl | 2 +- stdlib/InteractiveUtils/test/runtests.jl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 6bed2a0be0b07..10b247beff11f 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -15,7 +15,7 @@ function recursive_dotcalls!(ex, args, i=1) if !(ex isa Expr) || ((ex.head !== :. || !(ex.args[2] isa Expr)) && (ex.head !== :call || string(ex.args[1])[1] != '.')) newarg = Symbol('x', i) - if ex.head === :... + if Meta.isexpr(ex, :...) push!(args, only(ex.args)) return Expr(:..., newarg), i+1 else diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 7bbc2b321a440..7e2d6f3136b3e 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -332,6 +332,15 @@ B33163(x) = x @test (@code_typed max.(Ref(true).x))[2] == Bool @test !isempty(@code_typed optimize=false max.(Ref.([5, 6])...)) +# Issue #36261 +@test (@code_typed max.(1 .+ 3, 5 - 7))[2] == Int +f36261(x,y) = 3x + 4y +A36261 = Float64[1.0, 2.0, 3.0] +@test (@code_typed f36261.(A36261, pi))[1].inferred +@test (@code_typed f36261.(A36261, 1 .+ pi))[1].inferred +@test (@code_typed f36261.(A36261, 1 + pi))[1].inferred + + module ReflectionTest using Test, Random, InteractiveUtils From 772a40f0cb21fa5fbda6a0902fd9e1bece54ecb8 Mon Sep 17 00:00:00 2001 From: Sascha Mann Date: Tue, 16 Jun 2020 19:29:10 +0200 Subject: [PATCH 170/232] docs: strings: clarify reverse examples (#36268) The examples render differently on different systems and it's not always obvious how they are meant to render. This commit adds comments that explain what they're supposed to be. fixes #35941 --- base/strings/substring.jl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index bd2ae2b948335..73288ea363046 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -140,13 +140,21 @@ and encoding. If they return a string with a different encoding, they must also ```jldoctest julia> reverse("JuliaLang") "gnaLailuJ" +``` + +!!! note + The examples below may be rendered differently on different systems. + The comments indicate how they're supposed to be rendered -julia> reverse("ax̂e") # combining characters can lead to surprising results +Combining characters can lead to surprising results: + +```jldoctest +julia> reverse("ax̂e") # hat is above x in the input, above e in the output "êxa" julia> using Unicode -julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes +julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes; hat is above x in both in- and output "ex̂a" ``` """ From 6c760d2f478ed7713ad6e6fb1c24f458da802137 Mon Sep 17 00:00:00 2001 From: Liozou Date: Tue, 16 Jun 2020 19:30:02 +0200 Subject: [PATCH 171/232] Promote on Rational binary operations (#36279) --- base/rational.jl | 18 +++++++++--------- test/rational.jl | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/base/rational.jl b/base/rational.jl index 05b4a062d8711..0462554664fcc 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -61,16 +61,16 @@ julia> (3 // 5) // (2 // 1) //(n::Integer, d::Integer) = Rational(n,d) function //(x::Rational, y::Integer) - xn, yn = divgcd(x.num,y) + xn, yn = divgcd(promote(x.num, y)...) checked_den(xn, checked_mul(x.den, yn)) end function //(x::Integer, y::Rational) - xn, yn = divgcd(x,y.num) + xn, yn = divgcd(promote(x, y.num)...) checked_den(checked_mul(xn, y.den), yn) end function //(x::Rational, y::Rational) - xn,yn = divgcd(x.num,y.num) - xd,yd = divgcd(x.den,y.den) + xn,yn = divgcd(promote(x.num, y.num)...) + xd,yd = divgcd(promote(x.den, y.den)...) checked_den(checked_mul(xn, yd), checked_mul(xd, yn)) end @@ -280,7 +280,7 @@ end for (op,chop) in ((:+,:checked_add), (:-,:checked_sub), (:rem,:rem), (:mod,:mod)) @eval begin function ($op)(x::Rational, y::Rational) - xd, yd = divgcd(x.den, y.den) + xd, yd = divgcd(promote(x.den, y.den)...) Rational(($chop)(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) end @@ -305,16 +305,16 @@ for (op,chop) in ((:rem,:rem), (:mod,:mod)) end function *(x::Rational, y::Rational) - xn, yd = divgcd(x.num, y.den) - xd, yn = divgcd(x.den, y.num) + xn, yd = divgcd(promote(x.num, y.den)...) + xd, yn = divgcd(promote(x.den, y.num)...) unsafe_rational(checked_mul(xn, yn), checked_mul(xd, yd)) end function *(x::Rational, y::Integer) - xd, yn = divgcd(x.den, y) + xd, yn = divgcd(promote(x.den, y)...) unsafe_rational(checked_mul(x.num, yn), xd) end function *(y::Integer, x::Rational) - yn, xd = divgcd(y, x.den) + yn, xd = divgcd(promote(y, x.den)...) unsafe_rational(checked_mul(yn, x.num), xd) end /(x::Rational, y::Union{Rational, Integer, Complex{<:Union{Integer,Rational}}}) = x//y diff --git a/test/rational.jl b/test/rational.jl index 4b2d48305660b..81a9ab16c5ff7 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -575,3 +575,22 @@ end @test -Int32(1) // typemax(Int32) - Int32(1) == typemin(Int32) // typemax(Int32) @test 1 // (typemax(Int128) + BigInt(1)) - 2 == (1 + BigInt(2)*typemin(Int128)) // (BigInt(1) + typemax(Int128)) end + +@testset "Promotions on binary operations with Rationals (#36277)" begin + inttypes = (Base.BitInteger_types..., BigInt) + for T in inttypes, S in inttypes + U = Rational{promote_type(T, S)} + @test typeof(one(Rational{T}) + one(S)) == typeof(one(S) + one(Rational{T})) == typeof(one(Rational{T}) + one(Rational{S})) == U + @test typeof(one(Rational{T}) - one(S)) == typeof(one(S) - one(Rational{T})) == typeof(one(Rational{T}) - one(Rational{S})) == U + @test typeof(one(Rational{T}) * one(S)) == typeof(one(S) * one(Rational{T})) == typeof(one(Rational{T}) * one(Rational{S})) == U + @test typeof(one(Rational{T}) // one(S)) == typeof(one(S) // one(Rational{T})) == typeof(one(Rational{T}) // one(Rational{S})) == U + end + @test (-40//3) // 0x5 == 0x5 // (-15//8) == -8//3 + @test (-4//7) // (0x1//0x3) == (0x4//0x7) // (-1//3) == -12//7 + @test -3//2 + 0x1//0x1 == -3//2 + 0x1 == 0x1//0x1 + (-3//2) == 0x1 + (-3//2) == -1//2 + @test 0x3//0x5 - 2//3 == 3//5 - 0x2//0x3 == -1//15 + @test rem(-12//5, 0x2//0x1) == rem(-12//5, 0x2) == -2//5 + @test mod(0x3//0x1, -4//7) == mod(0x3, -4//7) == -3//7 + @test -1//5 * 0x3//0x2 == 0x3//0x2 * -1//5 == -3//10 + @test -2//3 * 0x1 == 0x1 * -2//3 == -2//3 +end From cde6268b137167338143c13d97f04de539b529ef Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 16 Jun 2020 12:31:35 -0500 Subject: [PATCH 172/232] Improve inference for `unique` with abstract eltypes (#36280) #20317 improved inference of unique, but problematic cases still arise for containers with known but abstract eltypes. Here, we short-circuit the `typejoin` when the return type is determined by the element type of the input container. For `unique(f, itr)`, this commit also allows the caller to supply `seen::Set` to circumvent the inference challenges. --- NEWS.md | 2 ++ base/set.jl | 44 +++++++++++++++++++++++++++++++------------- test/sets.jl | 22 +++++++++++++--------- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1671dad1c5685..b6df634e55755 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,6 +49,8 @@ Standard library changes * `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). * All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). * `sum`, `prod`, `maximum`, and `minimum` now support `init` keyword argument ([#36188], [#35839]). +* `unique(f, itr; seen=Set{T}())` now allows you to declare the container type used for + keeping track of values returned by `f` on elements of `itr` ([#36280]). #### LinearAlgebra * New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]). diff --git a/base/set.jl b/base/set.jl index a3041ea927e3d..b98e65c844a1c 100644 --- a/base/set.jl +++ b/base/set.jl @@ -120,19 +120,25 @@ julia> unique(Real[1, 1.0, 2]) ``` """ function unique(itr) + if isa(IteratorEltype(itr), HasEltype) + T = eltype(itr) + out = Vector{T}() + seen = Set{T}() + for x in itr + if !in(x, seen) + push!(seen, x) + push!(out, x) + end + end + return out + end T = @default_eltype(itr) - out = Vector{T}() - seen = Set{T}() y = iterate(itr) - y === nothing && return out + y === nothing && return T[] x, i = y - if !isconcretetype(T) && IteratorEltype(itr) == EltypeUnknown() - S = typeof(x) - return _unique_from(itr, S[x], Set{S}((x,)), i) - end - push!(seen, x) - push!(out, x) - return unique_from(itr, out, seen, i) + S = typeof(x) + R = isconcretetype(T) ? T : S + return _unique_from(itr, R[x], Set{R}((x,)), i) end _unique_from(itr, out, seen, i) = unique_from(itr, out, seen, i) @@ -175,8 +181,18 @@ julia> unique(x -> x^2, [1, -1, 3, -3, 4]) 4 ``` """ -function unique(f, C) +function unique(f, C; seen::Union{Nothing,Set}=nothing) out = Vector{eltype(C)}() + if seen !== nothing + for x in C + y = f(x) + if y ∉ seen + push!(out, x) + push!(seen, y) + end + end + return out + end s = iterate(C) if s === nothing @@ -241,7 +257,7 @@ julia> unique!(iseven, [2, 3, 5, 7, 9]) 3 ``` """ -function unique!(f, A::AbstractVector) +function unique!(f, A::AbstractVector; seen::Union{Nothing,Set}=nothing) if length(A) <= 1 return A end @@ -249,7 +265,9 @@ function unique!(f, A::AbstractVector) i = firstindex(A) x = @inbounds A[i] y = f(x) - seen = Set{typeof(y)}() + if seen === nothing + seen = Set{typeof(y)}() + end push!(seen, y) return _unique!(f, A, seen, i, i+1) end diff --git a/test/sets.jl b/test/sets.jl index 5d8db78803cb8..3c213b5426c9d 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -384,27 +384,30 @@ end end @testset "unique" begin - u = unique([1, 1, 2]) + u = @inferred(unique([1, 1, 2])) @test in(1, u) @test in(2, u) @test length(u) == 2 - @test unique(iseven, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6]) == [5, 8] - @test unique(n -> n % 3, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6]) == [5, 1, 9] + @test @inferred(unique(iseven, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6])) == [5, 8] + @test @inferred(unique(x->x^2, Integer[3, -4, 5, 4])) == Integer[3, -4, 5] + @test @inferred(unique(iseven, Integer[3, -4, 5, 4]; seen=Set{Bool}())) == Integer[3, -4] + @test @inferred(unique(n -> n % 3, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6])) == [5, 1, 9] end @testset "issue 20105" begin @test @inferred(unique(x for x in 1:1)) == [1] @test unique(x for x in Any[1, 1.0])::Vector{Real} == [1] @test unique(x for x in Real[1, 1.0])::Vector{Real} == [1] - @test unique(Integer[1, 1, 2])::Vector{Integer} == [1, 2] + @test @inferred(unique(Integer[1, 1, 2]))::Vector{Integer} == [1, 2] + @test unique(x for x in []) isa Vector{Any} end @testset "unique!" begin u = [1,1,3,2,1] - unique!(u) + @inferred(unique!(u)) @test u == [1,3,2] - @test unique!([]) == [] - @test unique!(Float64[]) == Float64[] + @test @inferred(unique!([])) == [] + @test @inferred(unique!(Float64[])) == Float64[] u = [1,2,2,3,5,5] @test unique!(u) === u @test u == [1,2,3,5] @@ -434,8 +437,9 @@ end u = [1,2,5,1,3,2] @test unique!(x -> x ^ 2, [1, -1, 3, -3, 5, -5]) == [1, 3, 5] @test unique!(n -> n % 3, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6]) == [5, 1, 9] - @test unique!(iseven, [2, 3, 5, 7, 9]) == [2, 3] - @test unique!(x -> x % 2 == 0 ? :even : :odd, [1, 2, 3, 4, 2, 2, 1]) == [1, 2] + @test @inferred(unique!(iseven, [2, 3, 5, 7, 9])) == [2, 3] + @test @inferred(unique!(x -> x % 2 == 0 ? :even : :odd, [1, 2, 3, 4, 2, 2, 1])) == [1, 2] + @test @inferred(unique!(x -> x % 2 == 0 ? :even : "odd", [1, 2, 3, 4, 2, 2, 1]; seen=Set{Union{Symbol,String}}())) == [1, 2] end @testset "allunique" begin From 2749582ad43b70e6103c837a0949948b380cc61e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 16 Jun 2020 12:33:12 -0500 Subject: [PATCH 173/232] Fix more invalidations from overloading `==` (#36282) * Improve typing of ProcessGroup.refs * Add type annotation in stacktrace handling * Eliminate boxing in REPL Related to #15276 --- base/cartesian.jl | 18 ++++++++++++++---- base/docs/Docs.jl | 2 +- base/errorshow.jl | 4 ++-- base/multimedia.jl | 2 +- stdlib/Distributed/src/Distributed.jl | 14 ++++++++++++++ stdlib/Distributed/src/cluster.jl | 24 ++++++++++++------------ stdlib/Distributed/src/messages.jl | 12 ------------ stdlib/LibGit2/src/LibGit2.jl | 6 +++--- stdlib/LibGit2/src/rebase.jl | 2 +- stdlib/REPL/src/REPLCompletions.jl | 8 +++++--- 10 files changed, 53 insertions(+), 39 deletions(-) diff --git a/base/cartesian.jl b/base/cartesian.jl index 45276e918b17c..f5825f56ba1aa 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -353,8 +353,13 @@ const exprresolve_cond_dict = Dict{Symbol,Function}(:(==) => ==, :(<) => <, :(>) => >, :(<=) => <=, :(>=) => >=) function exprresolve_arith(ex::Expr) - if ex.head === :call && haskey(exprresolve_arith_dict, ex.args[1]) && all([isa(ex.args[i], Number) for i = 2:length(ex.args)]) - return true, exprresolve_arith_dict[ex.args[1]](ex.args[2:end]...) + if ex.head === :call + callee = ex.args[1] + if isa(callee, Symbol) + if haskey(exprresolve_arith_dict, callee) && all(Bool[isa(ex.args[i], Number) for i = 2:length(ex.args)]) + return true, exprresolve_arith_dict[callee](ex.args[2:end]...) + end + end end false, 0 end @@ -362,8 +367,13 @@ exprresolve_arith(arg) = false, 0 exprresolve_conditional(b::Bool) = true, b function exprresolve_conditional(ex::Expr) - if ex.head === :call && ex.args[1] ∈ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) - return true, exprresolve_cond_dict[ex.args[1]](ex.args[2], ex.args[3]) + if ex.head === :call + callee = ex.args[1] + if isa(callee, Symbol) + if callee ∈ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) + return true, exprresolve_cond_dict[callee](ex.args[2], ex.args[3]) + end + end end false, false end diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 8ba3949082008..50a13244451aa 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -225,7 +225,7 @@ function doc!(__module__::Module, b::Binding, str::DocStr, @nospecialize sig = U # We allow for docstrings to be updated, but print a warning since it is possible # that over-writing a docstring *may* have been accidental. The warning # is suppressed for symbols in Main, for interactive use (#23011). - __module__ == Main || @warn "Replacing docs for `$b :: $sig` in module `$(__module__)`" + __module__ === Main || @warn "Replacing docs for `$b :: $sig` in module `$(__module__)`" else # The ordering of docstrings for each Binding is defined by the order in which they # are initially added. Replacing a specific docstring does not change it's ordering. diff --git a/base/errorshow.jl b/base/errorshow.jl index bb4f94c235376..ad8bf58718f67 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -380,7 +380,7 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=() ft = typeof(f) lines = [] # These functions are special cased to only show if first argument is matched. - special = f in [convert, getindex, setindex!] + special = f === convert || f === getindex || f === setindex! funcs = Any[(f, arg_types_param)] # An incorrect call method produces a MethodError for convert. @@ -681,7 +681,7 @@ function _simplify_include_frames(trace) kept_frames = trues(i) first_ignored = nothing while i >= 1 - frame, _ = trace[i] + frame::StackFrame, _ = trace[i] mod = parentmodule(frame) if isnothing(first_ignored) if mod === Base && frame.func === :_include diff --git a/base/multimedia.jl b/base/multimedia.jl index 4729849cfbc8d..576623202c7f8 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -327,7 +327,7 @@ function display(@nospecialize x) try return display(displays[i], x) catch e - isa(e, MethodError) && e.f in (display, show) || + isa(e, MethodError) && (e.f === display || e.f === show) || rethrow() end end diff --git a/stdlib/Distributed/src/Distributed.jl b/stdlib/Distributed/src/Distributed.jl index dbeaf6282f920..bb641ac9c2d83 100644 --- a/stdlib/Distributed/src/Distributed.jl +++ b/stdlib/Distributed/src/Distributed.jl @@ -82,6 +82,20 @@ function _require_callback(mod::Base.PkgId) end end +const REF_ID = Ref(1) +next_ref_id() = (id = REF_ID[]; REF_ID[] = id+1; id) + +struct RRID + whence::Int + id::Int + + RRID() = RRID(myid(),next_ref_id()) + RRID(whence, id) = new(whence,id) +end + +hash(r::RRID, h::UInt) = hash(r.whence, hash(r.id, h)) +==(r::RRID, s::RRID) = (r.whence==s.whence && r.id==s.id) + include("clusterserialize.jl") include("cluster.jl") # cluster setup and management, addprocs include("messages.jl") diff --git a/stdlib/Distributed/src/cluster.jl b/stdlib/Distributed/src/cluster.jl index 9bdfde7f920d4..8c5bfa102b387 100644 --- a/stdlib/Distributed/src/cluster.jl +++ b/stdlib/Distributed/src/cluster.jl @@ -148,7 +148,7 @@ function set_worker_state(w, state) end function check_worker_state(w::Worker) - if w.state == W_CREATED + if w.state === W_CREATED if !isclusterlazy() if PGRP.topology === :all_to_all # Since higher pids connect with lower pids, the remote worker @@ -185,13 +185,13 @@ function exec_conn_func(w::Worker) end function wait_for_conn(w) - if w.state == W_CREATED + if w.state === W_CREATED timeout = worker_timeout() - (time() - w.ct_time) timeout <= 0 && error("peer $(w.id) has not connected to $(myid())") @async (sleep(timeout); notify(w.c_state; all=true)) wait(w.c_state) - w.state == W_CREATED && error("peer $(w.id) didn't connect to $(myid()) within $timeout seconds") + w.state === W_CREATED && error("peer $(w.id) didn't connect to $(myid()) within $timeout seconds") end nothing end @@ -626,7 +626,7 @@ function create_worker(manager, wconfig) # require the value of config.connect_at which is set only upon connection completion for jw in PGRP.workers if (jw.id != 1) && (jw.id < w.id) - (jw.state == W_CREATED) && wait(jw.c_state) + (jw.state === W_CREATED) && wait(jw.c_state) push!(join_list, jw) end end @@ -649,7 +649,7 @@ function create_worker(manager, wconfig) end for wl in wlist - (wl.state == W_CREATED) && wait(wl.c_state) + (wl.state === W_CREATED) && wait(wl.c_state) push!(join_list, wl) end end @@ -767,7 +767,7 @@ end mutable struct ProcessGroup name::AbstractString workers::Array{Any,1} - refs::Dict # global references + refs::Dict{RRID,Any} # global references topology::Symbol lazy::Union{Bool, Nothing} @@ -851,7 +851,7 @@ function nprocs() n = length(PGRP.workers) # filter out workers in the process of being setup/shutdown. for jw in PGRP.workers - if !isa(jw, LocalProcess) && (jw.state != W_CONNECTED) + if !isa(jw, LocalProcess) && (jw.state !== W_CONNECTED) n = n - 1 end end @@ -902,7 +902,7 @@ julia> procs() function procs() if myid() == 1 || (PGRP.topology === :all_to_all && !isclusterlazy()) # filter out workers in the process of being setup/shutdown. - return Int[x.id for x in PGRP.workers if isa(x, LocalProcess) || (x.state == W_CONNECTED)] + return Int[x.id for x in PGRP.workers if isa(x, LocalProcess) || (x.state === W_CONNECTED)] else return Int[x.id for x in PGRP.workers] end @@ -911,7 +911,7 @@ end function id_in_procs(id) # faster version of `id in procs()` if myid() == 1 || (PGRP.topology === :all_to_all && !isclusterlazy()) for x in PGRP.workers - if (x.id::Int) == id && (isa(x, LocalProcess) || (x::Worker).state == W_CONNECTED) + if (x.id::Int) == id && (isa(x, LocalProcess) || (x::Worker).state === W_CONNECTED) return true end end @@ -933,7 +933,7 @@ Specifically all workers bound to the same ip-address as `pid` are returned. """ function procs(pid::Integer) if myid() == 1 - all_workers = [x for x in PGRP.workers if isa(x, LocalProcess) || (x.state == W_CONNECTED)] + all_workers = [x for x in PGRP.workers if isa(x, LocalProcess) || (x.state === W_CONNECTED)] if (pid == 1) || (isa(map_pid_wrkr[pid].manager, LocalManager)) Int[x.id for x in filter(w -> (w.id==1) || (isa(w.manager, LocalManager)), all_workers)] else @@ -1040,11 +1040,11 @@ function _rmprocs(pids, waitfor) start = time_ns() while (time_ns() - start) < waitfor*1e9 - all(w -> w.state == W_TERMINATED, rmprocset) && break + all(w -> w.state === W_TERMINATED, rmprocset) && break sleep(min(0.1, waitfor - (time_ns() - start)/1e9)) end - unremoved = [wrkr.id for wrkr in filter(w -> w.state != W_TERMINATED, rmprocset)] + unremoved = [wrkr.id for wrkr in filter(w -> w.state !== W_TERMINATED, rmprocset)] if length(unremoved) > 0 estr = string("rmprocs: pids ", unremoved, " not terminated after ", waitfor, " seconds.") throw(ErrorException(estr)) diff --git a/stdlib/Distributed/src/messages.jl b/stdlib/Distributed/src/messages.jl index bbfb13f276fa5..4737f09d19efa 100644 --- a/stdlib/Distributed/src/messages.jl +++ b/stdlib/Distributed/src/messages.jl @@ -2,18 +2,6 @@ abstract type AbstractMsg end -const REF_ID = Ref(1) -next_ref_id() = (id = REF_ID[]; REF_ID[] = id+1; id) - -struct RRID - whence::Int - id::Int - - RRID() = RRID(myid(),next_ref_id()) - RRID(whence, id) = new(whence,id) -end -hash(r::RRID, h::UInt) = hash(r.whence, hash(r.id, h)) -==(r::RRID, s::RRID) = (r.whence==s.whence && r.id==s.id) ## Wire format description # diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 4bfde5a8ee3ac..50a39ca323aef 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -289,7 +289,7 @@ function fetch(repo::GitRepo; remote::AbstractString="origin", fo = FetchOptions(callbacks=remote_callbacks) fetch(rmt, refspecs, msg="from $(url(rmt))", options=fo) catch err - if isa(err, GitError) && err.code == Error.EAUTH + if isa(err, GitError) && err.code === Error.EAUTH reject(cred_payload) else Base.shred!(cred_payload) @@ -345,7 +345,7 @@ function push(repo::GitRepo; remote::AbstractString="origin", push_opts = PushOptions(callbacks=remote_callbacks) push(rmt, refspecs, force=force, options=push_opts) catch err - if isa(err, GitError) && err.code == Error.EAUTH + if isa(err, GitError) && err.code === Error.EAUTH reject(cred_payload) else Base.shred!(cred_payload) @@ -579,7 +579,7 @@ function clone(repo_url::AbstractString, repo_path::AbstractString; repo = try clone(repo_url, repo_path, clone_opts) catch err - if isa(err, GitError) && err.code == Error.EAUTH + if isa(err, GitError) && err.code === Error.EAUTH reject(cred_payload) else Base.shred!(cred_payload) diff --git a/stdlib/LibGit2/src/rebase.jl b/stdlib/LibGit2/src/rebase.jl index d24b2c23367a6..8151217b3950b 100644 --- a/stdlib/LibGit2/src/rebase.jl +++ b/stdlib/LibGit2/src/rebase.jl @@ -83,7 +83,7 @@ function commit(rb::GitRebase, sig::GitSignature) oid_ptr, rb.ptr, C_NULL, sig.ptr, C_NULL, C_NULL) catch err # TODO: return current HEAD instead - err.code == Error.EAPPLIED && return nothing + err.code === Error.EAPPLIED && return nothing rethrow() end return oid_ptr[] diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index a16565bba3751..849a5ef4c977e 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -131,7 +131,9 @@ function complete_symbol(sym, ffunc, context_module=Main)::Vector{Completion} # We will exclude the results that the user does not want, as well # as excluding Main.Main.Main, etc., because that's most likely not what # the user wants - p = s->(!Base.isdeprecated(mod, s) && s != nameof(mod) && ffunc(mod, s)) + p = let mod=mod, modname=nameof(mod) + s->(!Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)) + end # Looking for a binding in a module if mod == context_module # Also look in modules we got through `using` @@ -624,9 +626,9 @@ function completions(string, pos, context_module=Main)::Completions ex = Meta.parse(s * ")", raise=false, depwarn=false) if isa(ex, Expr) - if ex.head==:call + if ex.head === :call return complete_methods(ex, context_module), first(frange):method_name_end, false - elseif ex.head==:. && ex.args[2] isa Expr && ex.args[2].head==:tuple + elseif ex.head === :. && ex.args[2] isa Expr && (ex.args[2]::Expr).head === :tuple return complete_methods(ex, context_module), first(frange):(method_name_end - 1), false end end From 33659c73c9c7d8d0636697a22377bbf8fd8368cf Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 16 Jun 2020 10:46:54 -0700 Subject: [PATCH 174/232] Remove `init` from `count!` docstring (#36305) --- base/reducedim.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/reducedim.jl b/base/reducedim.jl index 140e004cc5457..8b0d8327f4e39 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -390,11 +390,10 @@ count(A::AbstractArrayOrBroadcasted; dims=:) = count(identity, A, dims=dims) count(f, A::AbstractArrayOrBroadcasted; dims=:) = mapreduce(_bool(f), add_sum, A, dims=dims, init=0) """ - count!([f=identity,] r, A; init=true) + count!([f=identity,] r, A) Count the number of elements in `A` for which `f` returns `true` over the singleton dimensions of `r`, writing the result into `r` in-place. -If `init` is `true`, values in `r` are initialized to zero. !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. From 6cdfcf9b48c0c4d4cd4022539ec7e5083df9e8e3 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 16 Jun 2020 19:47:24 +0200 Subject: [PATCH 175/232] deleteat! : check bounds for the first passed index (#36231) * deleteat! : check bounds for the first passed index All other indices are bound-checked. Currently, the behavior looks like: ```julia julia> deleteat!([1:1000;], [-100]) signal (11): Segmentation fault [...] julia> deleteat!(BigInt[0], [0]) free(): invalid next size (normal) signal (6): Aborted [...] julia> deleteat!(BigInt[0], [-100]) ERROR: UndefRefError: access to undefined reference [...] julia> deleteat!([0], [0]) Int64[] julia> deleteat!([0], [2]) 1-element Array{Int64,1}: 0 julia> deleteat!([0], [3]) ERROR: InexactError: check_top_bit(UInt64, -1) [...] ``` With this commit, all these expressions throw a `BoundsError`. * Update base/array.jl Co-authored-by: Simeon Schaub Co-authored-by: Simeon Schaub --- base/array.jl | 4 ++-- test/arrayops.jl | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/base/array.jl b/base/array.jl index 8954f3e5b9d23..bd6807853c93e 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1352,9 +1352,9 @@ function _deleteat!(a::Vector, inds, dltd=Nowhere()) n = length(a) y = iterate(inds) y === nothing && return a - n == 0 && throw(BoundsError(a, inds)) (p, s) = y - p <= n && push!(dltd, @inbounds a[p]) + checkbounds(a, p) + push!(dltd, @inbounds a[p]) q = p+1 while true y = iterate(inds, s) diff --git a/test/arrayops.jl b/test/arrayops.jl index 059a5ab690e08..1c176f01b9887 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1448,11 +1448,15 @@ end @test deleteat!(a, [1,3,5,7:10...]) == [2,4,6] @test_throws BoundsError deleteat!(a, 13) @test_throws BoundsError deleteat!(a, [1,13]) - @test_throws ArgumentError deleteat!(a, [5,3]) + @test_throws ArgumentError deleteat!(a, [3,2]) # not sorted @test_throws BoundsError deleteat!(a, 5:20) @test_throws BoundsError deleteat!(a, Bool[]) @test_throws BoundsError deleteat!(a, [true]) @test_throws BoundsError deleteat!(a, falses(11)) + @test_throws BoundsError deleteat!(a, [0]) + @test_throws BoundsError deleteat!(a, [4]) + @test_throws BoundsError deleteat!(a, [5]) + @test_throws BoundsError deleteat!(a, [5, 3]) @test_throws BoundsError deleteat!([], 1) @test_throws BoundsError deleteat!([], [1]) From c54a6ea77b8475598744c7a134a04318bdbe5ba6 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 17 Jun 2020 00:51:36 +0200 Subject: [PATCH 176/232] Update to GMP 6.2.0 (#36309) This fixes a few bugs and brings performance improvements; for the full release notes, see . Two patches were removed as they are no longer necessary. --- contrib/refresh_bb_tarballs.sh | 4 +- deps/Versions.make | 4 +- .../GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 | 1 - .../GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 | 1 - .../GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 | 1 - .../sha512 | 1 - .../GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 | 1 - .../sha512 | 1 - .../GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/gmp.mk | 10 +- deps/patches/gmp-config-ldflags.patch | 381 ------------------ deps/patches/gmp-exception.patch | 35 -- 83 files changed, 57 insertions(+), 455 deletions(-) delete mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 delete mode 100644 deps/patches/gmp-config-ldflags.patch delete mode 100644 deps/patches/gmp-exception.patch diff --git a/contrib/refresh_bb_tarballs.sh b/contrib/refresh_bb_tarballs.sh index 5cac80d52fd78..c139b38f3557f 100755 --- a/contrib/refresh_bb_tarballs.sh +++ b/contrib/refresh_bb_tarballs.sh @@ -12,9 +12,9 @@ TRIPLETS="i686-linux-gnu x86_64-linux-gnu aarch64-linux-gnu armv7l-linux-gnueabihf powerpc64le-linux-gnu i686-linux-musl x86_64-linux-musl aarch64-linux-musl armv7l-linux-musleabihf x86_64-apple-darwin14 x86_64-unknown-freebsd11.1 i686-w64-mingw32 x86_64-w64-mingw32" # These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded: -BB_PROJECTS="gmp mbedtls libssh2 mpfr curl libgit2 pcre libuv unwind osxunwind dsfmt objconv p7zip zlib suitesparse openlibm" +BB_PROJECTS="mbedtls libssh2 mpfr curl libgit2 pcre libuv unwind osxunwind dsfmt objconv p7zip zlib suitesparse openlibm" BB_GCC_EXPANDED_PROJECTS="openblas" -BB_CXX_EXPANDED_PROJECTS="llvm" +BB_CXX_EXPANDED_PROJECTS="gmp llvm" # If we've been given a project name, filter down to that one: if [ -n "${1}" ]; then diff --git a/deps/Versions.make b/deps/Versions.make index a69bffca1b4e1..49ef420d889ca 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -15,8 +15,8 @@ UNWIND_VER = 1.3.1 UNWIND_BB_REL = 4 OSXUNWIND_VER = 0.0.5 OSXUNWIND_BB_REL = 0 -GMP_VER = 6.1.2 -GMP_BB_REL = 4 +GMP_VER = 6.2.0 +GMP_BB_REL = 0 MPFR_VER = 4.0.2 MPFR_BB_REL = 2 PATCHELF_VER = 0.9 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 deleted file mode 100644 index a33c13e8df4c4..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -fb8136c2a92d37edcd0ba4ae22352b5c diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 7a530e1181850..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9622464c9054371ee1621efe87801259b900e80457aa5cf39d4323e51dec56b7b3527c6fcf9ae74058f2b4db8034a204f71b9a28c02b3975239064261db5c361 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 deleted file mode 100644 index e0793c221b695..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f7d1f68ba15b3a5e9996cb349d4d5c58 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 deleted file mode 100644 index f7483c5f3b67f..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -921723c1e71cc80e1dd9f35f4142ca16be030ab1bd7a3c78c73a63be2d0ed80a342e28376bbc074d7203c97bec6e593b386f41ab25fbf22b8942d9d6818edbea diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 deleted file mode 100644 index 862c586220ed0..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b7210d1ffa5e517ef0bada8669303fab diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 deleted file mode 100644 index 86f9c96bd1a3e..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -77be741f35248a4c4fda80d5efb3b7b1b4ca3fd4f0fed9e305794b89bbb88c7baf5d1d57747bb8e0047377bf1ccbb00cba1a61df17d1c58df6a9901ca42eb593 diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 deleted file mode 100644 index fb8e567a332e7..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -cbd71c0e90e4c92381c79045d9310752 diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 deleted file mode 100644 index c6b1d6ece69da..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1a8a622746ad1bc7c6576476d7217e383c72771a8b2b3a3c74e5e9d3b0daec1920f85f556339c95857c16ebeb5d5c79669c900cb4b30f68969b9b57146f53b4c diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 deleted file mode 100644 index c044213f35214..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -96228c26a324e616715ff5116129efbf diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 0a98d76631389..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -dda1eed06658ee175868c80dac0dcc4848a2b179fa6226e5899de13492205e6d509180c65d533a132b46fdbcbcaa49a85df627a637336f5b14291ea747350d84 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 deleted file mode 100644 index d4061b698772b..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -bcb20e336ed586893a2bd1664bd4af96 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 deleted file mode 100644 index 06cdc0b869a8a..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e0cfabe7d2b331c3cc693ca50311319737785a087fc84b15f1862c6a60d036dfffed6f4e9a865d0b83fc374cba3b625face165505fd45de78f674bdafb34fbd2 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 37b7334e40f11..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -df89ac295cf9a17e16357e433fd917a9 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index 72d168d87424f..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -856cbaf79376e470ec5be0913a37f4ff06827bb6d06fceb462007375bd8d2b430ff8de938d7f3807a6612ba20d57cddfbb7fa52c94d828d62c678f75c1f5cb31 diff --git a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 deleted file mode 100644 index 7b2febc809adc..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5b232584c835a70ff5d43cfc57ec3438 diff --git a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 deleted file mode 100644 index ce283f5b9b001..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9ce5e5686bfcb14ca1ae7589cba6beec49215ffa2fe9b179c6afd5300d9da7b5c31133888a29cba923e4d399322b018ffb19d5a2bc096c1577a9e7bc7b8b33de diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 deleted file mode 100644 index 364fe0a283846..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a0d321bba6e59a449c92088fa693c969 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 deleted file mode 100644 index bb0ddb68ce569..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2b9ae682eecf5b5513bf7bc06872bd2bb79ac0d09d1a032217e8727854429e57bb415c5e6a6a04fd7da94cbcb45e3ad8a8578e7b9cf3ba1949db82dd8a9eb6ff diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 deleted file mode 100644 index bb7bbc3e41a82..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -a6fabb3bc108b0eef63c5543d1f13b7d diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index c6cb48ddae0c9..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -cf1fd3a1072f4a5ed3f71db9bf6cab2fa54fab8ae55fcc7bb2d6496e106a6a9a1844f9d8b8d7fdacfcd51e811e168fb99605ebbe1e87e61f978449ad6a280e24 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 deleted file mode 100644 index 2164b3144c461..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -ba1b090802d2636f460ee71253825a4c diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 deleted file mode 100644 index 9f14c65b65f5e..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6320d81bc7d27848606aa7063cf29e1c088f22272db28600a27238a0f933c6695b917ecb54670c93473dbdb7141c7ee33e65b8d717edd0f400966d5a03d5bd86 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 deleted file mode 100644 index 65452b89573ef..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -deb35cf6f3aa221c814a9cb9b77a99d9 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 deleted file mode 100644 index e92eb52b76acb..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d80954af671b6c24d8f0988c8cca8e89499dbb7206c8d0cd5c655688df9cc3a6b2e6ca8dbafb86ffaa42707be3e6da44e82c04d8173f7543514dae8dab4d5162 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 9d25f75ef5be6..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -51fb1b1a43cec0ec216c61d17cd637cd diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index 7521ace512391..0000000000000 --- a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9a702da6de85f3a81262858a2299222664417d424d89b1704eca702661f77e27cd07b7fca8fbd77c58521ef9caa83287c05956fbe97d9047272a4cb6912fc80d diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..2a1eab0c936ac --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +20672d3ba36a77d359172cc095f78805 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..4e546d4640aa3 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +b2f456fa97f4a68b512c286e89b5cc599f77f7e84956d761a93e6365f3ca425624e1c675f7c8c3132746d2ffcbc785537144401fca8f744869c606a579654512 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..8aece55be2b87 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +460394e5250cc11ba294645d2f1ffeac diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..e5e419c1020c0 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +6691625b2941ee9ef8fcdcbccd53e161a45cb37cbb3b87e4f40f2ae919189e2b7dcf5c10ffbd271be7d9f9bf60fb4a05e9532e965cac66ccd57836c364e288a2 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..92901a53a42bd --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +dc00a630947a489351d67903f5c12aae diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..e1c8ac10fa62a --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +8434a751bb6c0e610dda33e37a09a78331f41376df7362192e215d3c328c6730bb138fe8fd773dd907a6bb3b31b5e408eaf0a706f858c6a498b69db0315925c6 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..81c75fd01a9d1 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +d36cbb7473f9ce430df233b82b4c14f4 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..b6b9d88f57942 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +8fcda8c37131f0b44682548eaa4420fe74819fd9ab2ac6448464947c1f4b8328a5856ccd2ef3b8a400dc5e5d861d332354370ce36127bbea9eb6569705095b5b diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..faa2bae259fcb --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +f540d3dbe282d10469a6358b55d28939 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..fd764fcd495f3 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +04a66ec0d96bc3ed35fcaa0830a1592068677b71f0c7c1e1d34e8fa5626f17ee202562510d36ccda1ccc710d85fa1799bbf795d4cd46841e160e2a7920403310 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..30a72c0af3815 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +e11d981d3fbcdc0dec57d9e6683096c0 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..154d2fdf4915c --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +ddf3d98681ba2d0d886dc0c26a7d916e34ec16e3f1ef75ebdc3ec87a984ea874076af0d20220d85185de0e878f0819d8bb1a7f1ddb1a5f3b53362552f104e5e8 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..061d79e52f80c --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +2d8041c9fdafc830a89a5cc2a2864283 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..8fa17cbe17741 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +e366bb276ae4c6ad03ddacfb74c78793826e200465c7abcf8e158d05b16794e296ba8abe1780fbb586e0cadf28ed267ac981a408fb9dc3231547d30310962d29 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..c8df501c2aa37 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +1cb79462479e95afb33de29b26c9c60f diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..b2aa513df365e --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +56b1b8adbb9b660a19b10095c489fa67787c5a383fc409062c76afe659684ee44e31086ed683d3e058621302f69dd6547186901950ad2a57dc52c3f11df85e1d diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..45946d227f975 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +98dbed7361d07bbcbace591767a394b0 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..d52fab671d358 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +26a3ad2641b51fe8aa4b18383adc35a2093abae4f8bd483bf730428159f79d8a155e180ac1b71fe77a558d3071e3cf6701c45b2b54c6bdce9710c9dd8ae6ee05 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..884be1df6672c --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +f4311e1c856d88ef56b2e1a317486b0a diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..dbc5d0f14924f --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +80a61943871a1c78c9483acd96e5821a2e0a684a6fca9283639abd29a6c523dc5f191dbd8001e8c45501c99d2ee6a2a1aa34293e5ee15670481f2e64f8cd8001 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..893aa492a92c0 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +7de233cd7941bd4d4fe463cb390b9b76 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..e2f3b9da29a67 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +3005c252e9fa3f42fa1eb7a42b6e239630df2cbb9641e278b1fe0e24c0aafaf086eade41c1a68660b0e60cc3a9eb61883835df265373e5476e90f701b452d042 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..529c0ad49feb5 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +bbbc8ac72aba716263c39cf621cd7028 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..3c3f0a34bdd5f --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +7ce94e3a5ac55ed27ed380c17931f51a5f6eeba9b06c7d23396fa27a1e198839fbd6dfc1f90374e2c8434608e160adff758da85d4e6560a56b3ae1bed39e832e diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..16ec4a5f0f49c --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +56a84b9057036f41d69847e8f4127fea diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..40be98fb1ed7f --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +d1cfdec4d462eddf3969a6c3b581c73cd90d70adf2afd07e1fa7bfce04a32630305e7eec22ff465e9ecedc0b12a337f0820f4f91e92f76ec9e161e9d0749ebca diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..cd4a2fec244f2 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +23c5bf724a267de767ac633c94aedd79 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..859600955afd5 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +4e00c86635c09576200def2de7e4f5c6697a98cfce134c617f4c21dd7874a0a22665908f3530801a854b2867955885bf51cfbffd8f370be733c219448e212e6c diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..455cab5ec1b28 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +3046fbc73ae52f4105c7083f9fe62c4a diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..093f519d4bccb --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +9f6c01bedfe38c332255e8a4a2c4e03390db081c963f82ba9ad50117c5c5f810f387d5247466769d22c19bce8e2914305d0cb5f3d5214c6315aa764665e57e00 diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..deef9ab89aeb5 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +8d337e5b138241591cb2d1baec2485db diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..5e2e1bcd821fa --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +2ff8a58d78a80cf1f9629dccedece0e4bdf4af4a3a5b56dcb2664cf42c849764760ee823a62f2722d54405ff49a0f222af1c5ccf50cc3dd34ccbafdee2bc44be diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..a5a0d8985a643 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +d4e319df84721a019b033e538e0bbd97 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..e8972c7deb4fb --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +791ddf702c8751d7756fe2dc1ef3aa0d67fa5b4773064cc3e3012447f9b09516108dfa86b9b9326f19045bcd1807377b920efc2de6e90250f42a08d9ae4e99cb diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..989a8cb6e6361 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +2eb46789ca2af60a9fae1a0b92bec9c3 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..19f64a4f0bb36 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +8ce0f8c6a43f748885d3b044792e995c1729fb3576db52b136de5f8a1054b3d83d9c33ad38e907bf442d40b13b6eb5d2f539058d8a642d85405e2cccc09137e2 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..a6b35b3fd29c3 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +bd4b77b4ad10d932352b93cc79583963 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..6f4cfdecd622f --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +ae87d70aaeae01027cdb1f4fe76ee44abf8a68361f2086ecb93cfc54b7974a749508dc1ea60d7bb1806bdb1f0d58296d66d731c33d6f98a93bc6254374162de8 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..bbe8f53cbd4cf --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +42a7bfaa427ad3bd20df3b94652ee16b diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..d0e103093876a --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +0fdd19cb13a81efec2b5c36287f97659000c0bc2a812e11cfa2053cbb42aa7aeeb5558ea531a6f2c8378467f03073fe87b33898230197bdfd1c5b168e2f87c41 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..389e67d548feb --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +fdf152c6473d303c58624497984547d5 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..aa42a96b36b95 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +f68204bbadece63ed2980ef1d51567fbaa9689f2c6b68306636eaf44fa80001748cfacf41a16c16cd441acc049a139436964cd9f56220832f4789b14edc5a815 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..bd1603c893110 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +959a97e62c016409a722f35e92940109 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..cf03c140d1da1 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +6364c770cdf039c52a27f946ec0cf4229d0a3ef90c7c4beb9c8ccd0f915787436be11c340106742dd223cc50bf1f98a7cb228fee10bda151d166c646f326e14c diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..a4a5f02e76620 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +484d6a7beed5248b8611f458cbfc311f diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..7c3e641a62207 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +368bb6415b913fc4494fffc7bedb1ac48fd8d5c3ca697f790b59c1f8edce1780877b91acb20c627426de5bc024a9a3238b25d57150e2b483e1bc3d19da2c5460 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..a9a5eb3bb7dbb --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +83db3a1e6d1faeffaf241faf906d6659 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..956d3632e2a2c --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +4e3afafda2355463f3c301a112b23ddebeb5018e6b1271339ecc9a2e76297215c25228f6eb08a34a3fd8be93378040b1172457c714bec1af702b803baa92937d diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 new file mode 100644 index 0000000000000..102acd525d14d --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +96a5f00e789fc2fb31b42f67a44ff3b0 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 new file mode 100644 index 0000000000000..7887d60e0ae43 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +9b3982200a1e034964908b019e288dcc8b192524d5ecfc23bf9e3e367dd5ef3b57d6c0aec8bf3ef6e365e5ecee15d4590634342e681c1896c0e992b7edcc6607 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 new file mode 100644 index 0000000000000..a2f589d52c2ef --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +1ec5c4395753cae4c741abcfb1be7a11 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 new file mode 100644 index 0000000000000..2ab3c64c5d7b1 --- /dev/null +++ b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +486ddc1d1e5ca320a2cfc329486ebf7e5ebb5cfb3388cab2ce0221ed615634dcaafd003bfa53191e510f790d3854afc26e7a45dc7d35610ed467d34126b19f9b diff --git a/deps/gmp.mk b/deps/gmp.mk index dbc21c7ebf439..6757f20208b4b 100644 --- a/deps/gmp.mk +++ b/deps/gmp.mk @@ -20,17 +20,9 @@ $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/build-patched: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted - cp $(SRCDIR)/patches/config.sub $(SRCCACHE)/gmp-$(GMP_VER)/configfsf.sub - cd $(dir $@) && patch < $(SRCDIR)/patches/gmp-exception.patch cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch echo 1 > $@ -$(SRCCACHE)/gmp-$(GMP_VER)/gmp-config-ldflags.patch-applied: | $(SRCCACHE)/gmp-$(GMP_VER)/build-patched - cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp-config-ldflags.patch - echo 1 > $@ - -$(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-config-ldflags.patch-applied - $(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ @@ -81,5 +73,5 @@ else # USE_BINARYBUILDER_GMP GMP_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/GMP_jll.jl/releases/download/GMP-v$(GMP_VER)+$(GMP_BB_REL) GMP_BB_NAME := GMP.v$(GMP_VER) -$(eval $(call bb-install,gmp,GMP,false)) +$(eval $(call bb-install,gmp,GMP,false,true)) endif diff --git a/deps/patches/gmp-config-ldflags.patch b/deps/patches/gmp-config-ldflags.patch deleted file mode 100644 index fb89fa66b8da5..0000000000000 --- a/deps/patches/gmp-config-ldflags.patch +++ /dev/null @@ -1,381 +0,0 @@ ---- gmp-6.1.2/configure 2019-03-25 17:58:41.928471374 -0400 -+++ gmp-6.1.2-LDFLAGS/configure 2019-03-26 13:08:07.756316866 -0400 -@@ -5880,7 +5880,7 @@ if test "$gmp_prog_cc_works" = yes; then - int main () { return 0; } - EOF - echo "Test compile: " >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -5934,7 +5934,7 @@ void *f() { return g(); } - int main () { return 0; } - EOF - echo "Test compile: function pointer return" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -5990,7 +5990,7 @@ int cmov () { return (n >= 0 ? n : 0); } - int main () { return 0; } - EOF - echo "Test compile: cmov instruction" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6047,7 +6047,7 @@ unsigned long gcc303 () { return (unsign - int main () { return 0; } - EOF - echo "Test compile: double -> ulong conversion" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6102,7 +6102,7 @@ unsigned long fneg () { return -fneg_dat - int main () { return 0; } - EOF - echo "Test compile: double negation" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6158,7 +6158,7 @@ float ftod () { return (float) ftod_data - int main () { return 0; } - EOF - echo "Test compile: double -> float conversion" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6243,7 +6243,7 @@ param_init () - int main () { return 0; } - EOF - echo "Test compile: gnupro alpha ev6 char spilling" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6294,7 +6294,7 @@ if test "$gmp_prog_cc_works" = yes; then - int k; int foo () { __builtin_alloca (k); } - EOF - echo "Test compile: __builtin_alloca availability" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6340,7 +6340,7 @@ int foo () - int main () { return 0; } - EOF - echo "Test compile: alloca array" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6418,7 +6418,7 @@ int f () - int main () { return 0; } - EOF - echo "Test compile: abs int -> double conversion" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6483,7 +6483,7 @@ int dummy; - int main () { return 0; } - EOF - echo "Test compile: long long reliability test 1" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6544,7 +6544,7 @@ int dummy; - int main () { return 0; } - EOF - echo "Test compile: long long reliability test 2" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6605,7 +6605,7 @@ int dummy; - int main () { return 0; } - EOF - echo "Test compile: freebsd hacked gcc" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6704,7 +6704,7 @@ main () - - EOF - echo "Test compile: mpn_lshift_com optimization" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -6813,7 +6813,7 @@ main () - - EOF - echo "Test compile: mpn_lshift_com optimization 2" >&5 -- gmp_compile="$cc $cflags $cppflags conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7325,7 +7325,7 @@ _main: - xorl %eax, %eax - ret - EOF -- gmp_compile="$cc $cflags $cppflags conftest.s -o conftest >&5" -+ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.s -o conftest >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7390,7 +7390,7 @@ $as_echo_n "checking compiler $cc $cflag - cat >conftest.c <&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7498,7 +7498,7 @@ if test "$gmp_prog_cc_works" = yes; then - int main () { return 0; } - EOF - echo "Test compile: " >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7552,7 +7552,7 @@ void *f() { return g(); } - int main () { return 0; } - EOF - echo "Test compile: function pointer return" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7608,7 +7608,7 @@ int cmov () { return (n >= 0 ? n : 0); } - int main () { return 0; } - EOF - echo "Test compile: cmov instruction" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7665,7 +7665,7 @@ unsigned long gcc303 () { return (unsign - int main () { return 0; } - EOF - echo "Test compile: double -> ulong conversion" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7720,7 +7720,7 @@ unsigned long fneg () { return -fneg_dat - int main () { return 0; } - EOF - echo "Test compile: double negation" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7776,7 +7776,7 @@ float ftod () { return (float) ftod_data - int main () { return 0; } - EOF - echo "Test compile: double -> float conversion" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7861,7 +7861,7 @@ param_init () - int main () { return 0; } - EOF - echo "Test compile: gnupro alpha ev6 char spilling" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7912,7 +7912,7 @@ if test "$gmp_prog_cc_works" = yes; then - int k; int foo () { __builtin_alloca (k); } - EOF - echo "Test compile: __builtin_alloca availability" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -7958,7 +7958,7 @@ int foo () - int main () { return 0; } - EOF - echo "Test compile: alloca array" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -8036,7 +8036,7 @@ int f () - int main () { return 0; } - EOF - echo "Test compile: abs int -> double conversion" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -8101,7 +8101,7 @@ int dummy; - int main () { return 0; } - EOF - echo "Test compile: long long reliability test 1" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -8162,7 +8162,7 @@ int dummy; - int main () { return 0; } - EOF - echo "Test compile: long long reliability test 2" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -8223,7 +8223,7 @@ int dummy; - int main () { return 0; } - EOF - echo "Test compile: freebsd hacked gcc" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -8322,7 +8322,7 @@ main () - - EOF - echo "Test compile: mpn_lshift_com optimization" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -8431,7 +8431,7 @@ main () - - EOF - echo "Test compile: mpn_lshift_com optimization 2" >&5 -- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" -+ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -9987,7 +9987,7 @@ main () - return 0; - } - EOF --gmp_compile="$CC_FOR_BUILD conftest.c" -+gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c" - cc_for_build_works=no - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 -@@ -10019,7 +10019,7 @@ main () - return 0; - } - EOF --gmp_compile="$HOST_CC conftest.c" -+gmp_compile="$HOST_CC $LDFLAGS conftest.c" - cc_for_build_works=no - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 -@@ -10052,7 +10052,7 @@ main () - return 0; - } - EOF --gmp_compile="$i conftest.c" -+gmp_compile="$i $LDFLAGS conftest.c" - cc_for_build_works=no - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 -@@ -10132,7 +10132,7 @@ main () - } - EOF - for i in .exe ,ff8 ""; do -- gmp_compile="$CC_FOR_BUILD conftest.c -o conftest$i" -+ gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c -o conftest$i" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -10168,7 +10168,7 @@ main (int argc, char **argv) - return 0; - } - EOF --gmp_compile="$CC_FOR_BUILD conftest.c" -+gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -10210,7 +10210,7 @@ foo () - return log (d); - } - EOF --gmp_compile="$CC_FOR_BUILD conftest.c -lm" -+gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c -lm" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? -@@ -10543,7 +10543,7 @@ if test "$gmp_prog_cxx_works" = yes; the - int main (void) { return 0; } - EOF - echo "Test compile: " >&5 -- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" -+ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 - (eval $gmp_cxxcompile) 2>&5 - ac_status=$? -@@ -10583,7 +10583,7 @@ using namespace foo; - int main (void) { return 0; } - EOF - echo "Test compile: namespace" >&5 -- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" -+ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 - (eval $gmp_cxxcompile) 2>&5 - ac_status=$? -@@ -10629,7 +10629,7 @@ void someoutput (void) { std::cout << 12 - int main (void) { return 0; } - EOF - echo "Test compile: std iostream" >&5 -- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" -+ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 - (eval $gmp_cxxcompile) 2>&5 - ac_status=$? -@@ -27095,7 +27095,7 @@ for tmp_underscore in "" "_"; do - ${tmp_gsym_prefix}main$gmp_cv_asm_label_suffix - addl $ ${tmp_underscore}_GLOBAL_OFFSET_TABLE_, %ebx - EOF -- gmp_compile="$CCAS $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.s >&5 && $CC $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.$OBJEXT >&5" -+ gmp_compile="$CCAS $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.s >&5 && $CC $CFLAGS $CPPFLAGS $LDFLAGS $lt_prog_compiler_pic conftest.$OBJEXT >&5" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 - (eval $gmp_compile) 2>&5 - ac_status=$? - diff --git a/deps/patches/gmp-exception.patch b/deps/patches/gmp-exception.patch deleted file mode 100644 index e4672be10244b..0000000000000 --- a/deps/patches/gmp-exception.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff -r 842c2ba359bf errno.c ---- a/errno.c Sun Jan 24 22:06:51 2016 +0100 -+++ b/errno.c Thu Jan 28 13:37:54 2016 -0500 -@@ -33,24 +33,24 @@ - see https://www.gnu.org/licenses/. */ - - #include -+ -+#include -+ - #include "gmp.h" - #include "gmp-impl.h" - - int gmp_errno = 0; - - --/* The deliberate divide by zero triggers an exception on most systems. On -- those where it doesn't, for example power and powerpc, use abort instead. -- -- Enhancement: Perhaps raise(SIGFPE) (or the same with kill()) would be -- better than abort. Perhaps it'd be possible to get the BSD style -- FPE_INTDIV_TRAP parameter in there too. */ -- -+/* Use SIGFPE on systems which have it. Otherwise, deliberate divide -+ by zero, which triggers an exception on most systems. On those -+ where it doesn't, for example power and powerpc, use abort instead. */ - void - __gmp_exception (int error_bit) - { - gmp_errno |= error_bit; - __gmp_junk = 10 / __gmp_0; -+ raise (SIGFPE); - abort (); - } - From 0a2644ac8469a77d42424f08e4c65d3bb7bb1f59 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 16 Jun 2020 18:59:56 -0400 Subject: [PATCH 177/232] optimizer: add Instruction/InstructionStream abstraction (#34306) --- base/compiler/compiler.jl | 4 +- base/compiler/optimize.jl | 5 +- base/compiler/ssair/driver.jl | 6 +- base/compiler/ssair/inlining.jl | 58 ++-- base/compiler/ssair/ir.jl | 489 +++++++++++++++++--------------- base/compiler/ssair/legacy.jl | 32 +-- base/compiler/ssair/passes.jl | 167 +++++------ base/compiler/ssair/show.jl | 101 ++++--- base/compiler/ssair/slot2ssa.jl | 157 +++++----- base/compiler/ssair/verify.jl | 13 +- base/show.jl | 4 + test/compiler/irpasses.jl | 7 +- test/compiler/ssair.jl | 15 +- test/show.jl | 6 +- 14 files changed, 580 insertions(+), 484 deletions(-) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 4ac2e3ca9a4fd..48a2d1e30adf3 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -7,8 +7,8 @@ using Core.Intrinsics, Core.IR import Core: print, println, show, write, unsafe_write, stdout, stderr, _apply, _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance, CodeInstance -const getproperty = getfield -const setproperty! = setfield! +const getproperty = Core.getfield +const setproperty! = Core.setfield! ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Compiler, false) diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index b0aa0423f030b..120d010937ff4 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -186,8 +186,9 @@ function optimize(opt::OptimizationState, params::OptimizationParams, @nospecial if length(ir.stmts) < 10 proven_pure = true for i in 1:length(ir.stmts) - stmt = ir.stmts[i] - if stmt_affects_purity(stmt, ir) && !stmt_effect_free(stmt, ir.types[i], ir, ir.sptypes) + node = ir.stmts[i] + stmt = node[:inst] + if stmt_affects_purity(stmt, ir) && !stmt_effect_free(stmt, node[:type], ir, ir.sptypes) proven_pure = false break end diff --git a/base/compiler/ssair/driver.jl b/base/compiler/ssair/driver.jl index 3353d574b9f8f..4fd1bdee5d658 100644 --- a/base/compiler/ssair/driver.jl +++ b/base/compiler/ssair/driver.jl @@ -116,14 +116,16 @@ function convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, narg end strip_trailing_junk!(ci, code, flags) cfg = compute_basic_blocks(code) - ir = IRCode(code, Any[], ci.codelocs, flags, cfg, collect(LineInfoNode, ci.linetable), sv.slottypes, meta, sv.sptypes) + types = Any[] + stmts = InstructionStream(code, types, ci.codelocs, flags) + ir = IRCode(stmts, cfg, collect(LineInfoNode, ci.linetable), sv.slottypes, meta, sv.sptypes) return ir end function slot2reg(ir::IRCode, ci::CodeInfo, nargs::Int, sv::OptimizationState) # need `ci` for the slot metadata, IR for the code @timeit "domtree 1" domtree = construct_domtree(ir.cfg) - defuse_insts = scan_slot_def_use(nargs, ci, ir.stmts) + defuse_insts = scan_slot_def_use(nargs, ci, ir.stmts.inst) @timeit "construct_ssa" ir = construct_ssa!(ci, ir, domtree, defuse_insts, nargs, sv.sptypes, sv.slottypes) # consumes `ir` return ir end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index ddd7cfe7a1937..eb7222496a5f4 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -303,19 +303,19 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) # Ok, do the inlining here inline_cfg = item.ir.cfg - stmt = compact.result[idx] - linetable_offset = length(linetable) + stmt = compact.result[idx][:inst] + linetable_offset::Int32 = length(linetable) # Append the linetable of the inlined function to our line table - inlined_at = Int(compact.result_lines[idx]) + inlined_at = Int(compact.result[idx][:line]) for entry in item.linetable push!(linetable, LineInfoNode(entry.method, entry.file, entry.line, (entry.inlined_at > 0 ? entry.inlined_at + linetable_offset : inlined_at))) end if item.isva - vararg = mk_tuplecall!(compact, argexprs[item.na:end], compact.result_lines[idx]) + vararg = mk_tuplecall!(compact, argexprs[item.na:end], compact.result[idx][:line]) argexprs = Any[argexprs[1:(item.na - 1)]..., vararg] end - flag = compact.result_flags[idx] + flag = compact.result[idx][:flag] boundscheck_idx = boundscheck if boundscheck_idx === :default || boundscheck_idx === :propagate if (flag & IR_FLAG_INBOUNDS) != 0 @@ -341,7 +341,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector return_value = SSAValue(idx′) inline_compact[idx′] = stmt′.val val = stmt′.val - inline_compact.result_types[idx′] = (isa(val, Argument) || isa(val, Expr)) ? + inline_compact.result[idx′][:type] = (isa(val, Argument) || isa(val, Expr)) ? compact_exprtype(compact, stmt′.val) : compact_exprtype(inline_compact, stmt′.val) break @@ -371,11 +371,11 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector push!(pn.edges, inline_compact.active_result_bb-1) if isa(val, GlobalRef) || isa(val, Expr) stmt′ = val - inline_compact.result_types[idx′] = (isa(val, Argument) || isa(val, Expr)) ? + inline_compact.result[idx′][:type] = (isa(val, Argument) || isa(val, Expr)) ? compact_exprtype(compact, val) : compact_exprtype(inline_compact, val) insert_node_here!(inline_compact, GotoNode(post_bb_id), - Any, compact.result_lines[idx′], + Any, compact.result[idx′][:line], true) push!(pn.values, SSAValue(idx′)) else @@ -407,7 +407,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector if length(pn.edges) == 1 return_value = pn.values[1] else - return_value = insert_node_here!(compact, pn, compact_exprtype(compact, SSAValue(idx)), compact.result_lines[idx]) + return_value = insert_node_here!(compact, pn, compact_exprtype(compact, SSAValue(idx)), compact.result[idx][:line]) end end return_value @@ -418,7 +418,7 @@ const fatal_type_bound_error = ErrorException("fatal error in type inference (ty function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any}, linetable::Vector{LineInfoNode}, item::UnionSplit, boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) - stmt, typ, line = compact.result[idx], compact.result_types[idx], compact.result_lines[idx] + stmt, typ, line = compact.result[idx][:inst], compact.result[idx][:type], compact.result[idx][:line] atype = item.atype generic_bb = item.bbs[end-1] join_bb = item.bbs[end] @@ -541,12 +541,13 @@ function batch_inline!(todo::Vector{Any}, ir::IRCode, linetable::Vector{LineInfo for aidx in 1:length(argexprs) aexpr = argexprs[aidx] if isa(aexpr, GlobalRef) || isa(aexpr, Expr) - argexprs[aidx] = insert_node_here!(compact, aexpr, compact_exprtype(compact, aexpr), compact.result_lines[idx]) + argexprs[aidx] = insert_node_here!(compact, aexpr, compact_exprtype(compact, aexpr), compact.result[idx][:line]) end end if isinvoke(item) - argexprs = rewrite_invoke_exprargs!((node, typ)->insert_node_here!(compact, node, typ, compact.result_lines[idx]), - argexprs) + argexprs = rewrite_invoke_exprargs!(argexprs) do node, typ + insert_node_here!(compact, node, typ, compact.result[idx][:line]) + end end if isa(item, InliningTodo) compact.ssa_rename[compact.idx-1] = ir_inline_item!(compact, idx, argexprs, linetable, item, boundscheck, state.todo_bbs) @@ -841,8 +842,8 @@ function is_valid_type_for_apply_rewrite(@nospecialize(typ), params::Optimizatio end function inline_splatnew!(ir::IRCode, idx::Int) - stmt = ir.stmts[idx] - ty = ir.types[idx] + stmt = ir.stmts[idx][:inst] + ty = ir.stmts[idx][:type] nf = nfields_tfunc(ty) if nf isa Const eargs = stmt.args @@ -874,7 +875,6 @@ function call_sig(ir::IRCode, stmt::Expr) f = singleton_type(ft) f === Core.Intrinsics.llvmcall && return nothing f === Core.Intrinsics.cglobal && return nothing - atypes = Vector{Any}(undef, length(stmt.args)) atypes[1] = ft ok = true @@ -888,7 +888,7 @@ function call_sig(ir::IRCode, stmt::Expr) end function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::OptimizationParams) - stmt = ir.stmts[idx] + stmt = ir.stmts[idx][:inst] while sig.f === Core._apply || sig.f === Core._apply_iterate arg_start = sig.f === Core._apply ? 2 : 3 atypes = sig.atypes @@ -911,7 +911,7 @@ function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::Optimizatio break end if nonempty_idx != 0 - ir.stmts[idx] = stmt.args[nonempty_idx] + ir.stmts[idx][:inst] = stmt.args[nonempty_idx] return nothing end end @@ -941,8 +941,8 @@ is_builtin(s::Signature) = s.ft ⊑ Builtin function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, invoke_data::InvokeData, sv::OptimizationState, todo::Vector{Any}) - stmt = ir.stmts[idx] - calltype = ir.types[idx] + stmt = ir.stmts[idx][:inst] + calltype = ir.stmts[idx][:type] method = invoke_data.entry.func (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), sig.atype, method.sig)::SimpleVector @@ -958,7 +958,7 @@ end # this method does not access the method table or otherwise process generic # functions. function process_simple!(ir::IRCode, idx::Int, params::OptimizationParams, world::UInt) - stmt = ir.stmts[idx] + stmt = ir.stmts[idx][:inst] stmt isa Expr || return nothing if stmt.head === :splatnew inline_splatnew!(ir, idx) @@ -975,10 +975,10 @@ function process_simple!(ir::IRCode, idx::Int, params::OptimizationParams, world sig === nothing && return nothing # Check if we match any of the early inliners - calltype = ir.types[idx] + calltype = ir.stmts[idx][:type] res = early_inline_special_case(ir, sig, stmt, params, calltype) if res !== nothing - ir.stmts[idx] = res + ir.stmts[idx][:inst] = res return nothing end @@ -1013,8 +1013,8 @@ function assemble_inline_todo!(ir::IRCode, sv::OptimizationState) r = process_simple!(ir, idx, sv.params, sv.world) r === nothing && continue - stmt = ir.stmts[idx] - calltype = ir.types[idx] + stmt = ir.stmts[idx][:inst] + calltype = ir.stmts[idx][:type] (sig, invoke_data) = r # Ok, now figure out what method to call @@ -1213,8 +1213,8 @@ function early_inline_special_case(ir::IRCode, s::Signature, e::Expr, params::Op end function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::Expr, params::OptimizationParams) - typ = ir.types[idx] f, ft, atypes = sig.f, sig.ft, sig.atypes + typ = ir.stmts[idx][:type] if params.inlining && length(atypes) == 3 && istopfunction(f, :!==) # special-case inliner for !== that precedes _methods_by_ftype union splitting # and that works, even though inference generally avoids inferring the `!==` Method @@ -1251,9 +1251,9 @@ end function ssa_substitute!(idx::Int, @nospecialize(val), arg_replacements::Vector{Any}, @nospecialize(spsig), spvals::Vector{Any}, - linetable_offset::Int, boundscheck::Symbol, compact::IncrementalCompact) - compact.result_flags[idx] &= ~IR_FLAG_INBOUNDS - compact.result_lines[idx] += linetable_offset + linetable_offset::Int32, boundscheck::Symbol, compact::IncrementalCompact) + compact.result[idx][:flag] &= ~IR_FLAG_INBOUNDS + compact.result[idx][:line] += linetable_offset return ssa_substitute_op!(val, arg_replacements, spsig, spvals, boundscheck) end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 1fa189d1fb509..6bef646790d0c 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -53,7 +53,6 @@ struct CFG index::Vector{Int} # map from instruction => basic-block number # TODO: make this O(1) instead of O(log(n_blocks))? end -copy(c::CFG) = CFG(BasicBlock[copy(b) for b in c.blocks], copy(c.index)) function block_for_inst(index::Vector{Int}, inst::Int) return searchsortedfirst(index, inst, lt=(<=)) @@ -191,59 +190,127 @@ function first_insert_for_bb(code, cfg::CFG, block::Int) end end -struct NewNode +# SSA-indexed nodes +struct InstructionStream + inst::Vector{Any} + type::Vector{Any} + line::Vector{Int32} + flag::Vector{UInt8} +end +function InstructionStream(len::Int) + insts = Array{Any}(undef, len) + types = Array{Any}(undef, len) + lines = fill(Int32(0), len) + flags = fill(0x00, len) + return InstructionStream(insts, types, lines, flags) +end +InstructionStream() = InstructionStream(0) +length(is::InstructionStream) = length(is.inst) +isempty(is::InstructionStream) = isempty(is.inst) +function add!(is::InstructionStream) + ninst = length(is) + 1 + resize!(is.inst, ninst) + resize!(is.type, ninst) + resize!(is.line, ninst) + resize!(is.flag, ninst) + return ninst +end +#function copy(is::InstructionStream) # unused +# return InstructionStream( +# copy_exprargs(is.insts), +# copy(is.types), +# copy(is.lines), +# copy(is.flags)) +#end +function resize!(stmts::InstructionStream, len) + old_length = length(stmts) + resize!(stmts.inst, len) + resize!(stmts.type, len) + resize!(stmts.line, len) + resize!(stmts.flag, len) + for i in (old_length + 1):len + stmts.line[i] = 0 + stmts.flag[i] = 0x00 + end + return stmts +end + + +struct Instruction + data::InstructionStream + idx::Int +end +Instruction(is::InstructionStream) = Instruction(is, add!(is)) + +@inline function getindex(node::Instruction, fld::Symbol) + isdefined(node, fld) && return getfield(node, fld) + return getfield(getfield(node, :data), fld)[getfield(node, :idx)] +end +@inline function setindex!(node::Instruction, @nospecialize(val), fld::Symbol) + getfield(getfield(node, :data), fld)[getfield(node, :idx)] = val + return node +end + +@inline getindex(is::InstructionStream, idx::Int) = Instruction(is, idx) +function setindex!(is::InstructionStream, newval::Instruction, idx::Int) + is.inst[idx] = newval[:inst] + is.type[idx] = newval[:type] + is.line[idx] = newval[:line] + is.flag[idx] = newval[:flag] + return is +end +function setindex!(node::Instruction, newval::Instruction) + node.data[node.idx] = newval + return node +end + +struct NewNodeInfo # Insertion position (interpretation depends on which array this is in) pos::Int # Place the new instruction after this instruction (but in the same BB if this is an implicit terminator) attach_after::Bool - # The type of the instruction to insert - typ::Any - # The node itself - node::Any - # The index into the line number table of this entry - line::Int32 - - NewNode(pos::Int, attach_after::Bool, @nospecialize(typ), @nospecialize(node), line::Int32) = - new(pos, attach_after, typ, node, line) +end +struct NewNodeStream + stmts::InstructionStream + info::Vector{NewNodeInfo} +end +NewNodeStream(len::Int=0) = NewNodeStream(InstructionStream(len), fill(NewNodeInfo(0, false), len)) +length(new::NewNodeStream) = length(new.stmts) +isempty(new::NewNodeStream) = isempty(new.stmts) +function add!(new::NewNodeStream, pos::Int, attach_after::Bool) + push!(new.info, NewNodeInfo(pos, attach_after)) + return Instruction(new.stmts) end struct IRCode - stmts::Vector{Any} - types::Vector{Any} - lines::Vector{Int32} - flags::Vector{UInt8} + stmts::InstructionStream argtypes::Vector{Any} sptypes::Vector{Any} linetable::Vector{LineInfoNode} cfg::CFG - new_nodes::Vector{NewNode} + new_nodes::NewNodeStream meta::Vector{Any} - function IRCode(stmts::Vector{Any}, types::Vector{Any}, lines::Vector{Int32}, flags::Vector{UInt8}, - cfg::CFG, linetable::Vector{LineInfoNode}, argtypes::Vector{Any}, meta::Vector{Any}, - sptypes::Vector{Any}) - return new(stmts, types, lines, flags, argtypes, sptypes, linetable, cfg, NewNode[], meta) + function IRCode(stmts::InstructionStream, cfg::CFG, linetable::Vector{LineInfoNode}, argtypes::Vector{Any}, meta::Vector{Any}, sptypes::Vector{Any}) + return new(stmts, argtypes, sptypes, linetable, cfg, NewNodeStream(), meta) end - function IRCode(ir::IRCode, stmts::Vector{Any}, types::Vector{Any}, lines::Vector{Int32}, flags::Vector{UInt8}, - cfg::CFG, new_nodes::Vector{NewNode}) - return new(stmts, types, lines, flags, ir.argtypes, ir.sptypes, ir.linetable, cfg, new_nodes, ir.meta) + function IRCode(ir::IRCode, stmts::InstructionStream, cfg::CFG, new_nodes::NewNodeStream) + return new(stmts, ir.argtypes, ir.sptypes, ir.linetable, cfg, new_nodes, ir.meta) end end -copy(code::IRCode) = IRCode(code, copy_exprargs(code.stmts), copy(code.types), - copy(code.lines), copy(code.flags), copy(code.cfg), copy(code.new_nodes)) function getindex(x::IRCode, s::SSAValue) if s.id <= length(x.stmts) - return x.stmts[s.id] + return x.stmts[s.id][:inst] else - return x.new_nodes[s.id - length(x.stmts)].node + return x.new_nodes.stmts[s.id - length(x.stmts)][:inst] end end function setindex!(x::IRCode, @nospecialize(repl), s::SSAValue) @assert s.id <= length(x.stmts) - x.stmts[s.id] = repl - nothing + x.stmts[s.id][:inst] = repl + return x end @@ -442,9 +509,10 @@ function foreachssa(f, @nospecialize(stmt)) end function insert_node!(ir::IRCode, pos::Int, @nospecialize(typ), @nospecialize(val), attach_after::Bool=false) - line = ir.lines[pos] - push!(ir.new_nodes, NewNode(pos, attach_after, typ, val, line)) - return SSAValue(length(ir.stmts) + length(ir.new_nodes)) + line = ir.stmts[pos][:line] + node = add!(ir.new_nodes, pos, attach_after) + node[:inst], node[:type], node[:line], node[:flag] = val, typ, line, 0x00 + return SSAValue(length(ir.stmts) + node.idx) end # For bootstrapping @@ -459,23 +527,19 @@ end mutable struct IncrementalCompact ir::IRCode - result::Vector{Any} - result_types::Vector{Any} - result_lines::Vector{Int32} - result_flags::Vector{UInt8} + result::InstructionStream result_bbs::Vector{BasicBlock} ssa_rename::Vector{Any} bb_rename_pred::Vector{Int} bb_rename_succ::Vector{Int} used_ssas::Vector{Int} late_fixup::Vector{Int} - # This could be Stateful, but bootstrapping doesn't like that perm::Vector{Int} new_nodes_idx::Int # This supports insertion while compacting - new_new_nodes::Vector{NewNode} # New nodes that were before the compaction point at insertion time + new_new_nodes::NewNodeStream # New nodes that were before the compaction point at insertion time # TODO: Switch these two to a min-heap of some sort - pending_nodes::Vector{NewNode} # New nodes that were after the compaction point at insertion time + pending_nodes::NewNodeStream # New nodes that were after the compaction point at insertion time pending_perm::Vector{Int} # State idx::Int @@ -485,13 +549,12 @@ mutable struct IncrementalCompact cfg_transforms_enabled::Bool fold_constant_branches::Bool function IncrementalCompact(code::IRCode, allow_cfg_transforms::Bool=false) - # Sort by position with attach after nodes affter regular ones - perm = my_sortperm(Int[(code.new_nodes[i].pos*2 + Int(code.new_nodes[i].attach_after)) for i in 1:length(code.new_nodes)]) + # Sort by position with attach after nodes after regular ones + perm = my_sortperm(Int[let new_node = code.new_nodes.info[i] + (new_node.pos * 2 + Int(new_node.attach_after)) + end for i in 1:length(code.new_nodes)]) new_len = length(code.stmts) + length(code.new_nodes) - result = Array{Any}(undef, new_len) - result_types = Array{Any}(undef, new_len) - result_lines = fill(Int32(0), new_len) - result_flags = fill(0x00, new_len) + result = InstructionStream(new_len) used_ssas = fill(0, new_len) blocks = code.cfg.blocks if allow_cfg_transforms @@ -517,9 +580,11 @@ mutable struct IncrementalCompact # Dead blocks get removed from the predecessor list filter!(x->x !== -1, preds) # Rename succs - for j = 1:length(succs); succs[j] = bb_rename[succs[j]]; end + for j = 1:length(succs) + succs[j] = bb_rename[succs[j]] + end end - let blocks=blocks + let blocks = blocks result_bbs = BasicBlock[blocks[i] for i = 1:length(blocks) if bb_rename[i] != -1] end else @@ -528,26 +593,26 @@ mutable struct IncrementalCompact end ssa_rename = Any[SSAValue(i) for i = 1:new_len] late_fixup = Vector{Int}() - new_new_nodes = NewNode[] - pending_nodes = NewNode[] + new_new_nodes = NewNodeStream() + pending_nodes = NewNodeStream() pending_perm = Int[] - return new(code, result, result_types, result_lines, result_flags, result_bbs, ssa_rename, bb_rename, bb_rename, used_ssas, late_fixup, perm, 1, + return new(code, result, result_bbs, ssa_rename, bb_rename, bb_rename, used_ssas, late_fixup, perm, 1, new_new_nodes, pending_nodes, pending_perm, 1, 1, 1, false, allow_cfg_transforms, allow_cfg_transforms) end # For inlining function IncrementalCompact(parent::IncrementalCompact, code::IRCode, result_offset) - perm = my_sortperm(Int[code.new_nodes[i].pos for i in 1:length(code.new_nodes)]) + perm = my_sortperm(Int[code.new_nodes.info[i].pos for i in 1:length(code.new_nodes)]) new_len = length(code.stmts) + length(code.new_nodes) ssa_rename = Any[SSAValue(i) for i = 1:new_len] used_ssas = fill(0, new_len) late_fixup = Vector{Int}() bb_rename = Vector{Int}() - new_new_nodes = NewNode[] - pending_nodes = NewNode[] + new_new_nodes = NewNodeStream() + pending_nodes = NewNodeStream() pending_perm = Int[] - return new(code, parent.result, parent.result_types, parent.result_lines, parent.result_flags, + return new(code, parent.result, parent.result_bbs, ssa_rename, bb_rename, bb_rename, parent.used_ssas, late_fixup, perm, 1, new_new_nodes, pending_nodes, pending_perm, @@ -555,39 +620,39 @@ mutable struct IncrementalCompact end end -struct TypesView - ir::Union{IRCode, IncrementalCompact} +struct TypesView{T} + ir::T # ::Union{IRCode, IncrementalCompact} end types(ir::Union{IRCode, IncrementalCompact}) = TypesView(ir) function getindex(compact::IncrementalCompact, idx::Int) if idx < compact.result_idx - return compact.result[idx] + return compact.result[idx][:inst] else - return compact.ir.stmts[idx] + return compact.ir.stmts[idx][:inst] end end function getindex(compact::IncrementalCompact, ssa::SSAValue) @assert ssa.id < compact.result_idx - return compact.result[ssa.id] + return compact.result[ssa.id][:inst] end function getindex(compact::IncrementalCompact, ssa::OldSSAValue) id = ssa.id if id <= length(compact.ir.stmts) - return compact.ir.stmts[id] + return compact.ir.stmts[id][:inst] end id -= length(compact.ir.stmts) if id <= length(compact.ir.new_nodes) - return compact.ir.new_nodes[id].node + return compact.ir.new_nodes.stmts[id][:inst] end id -= length(compact.ir.new_nodes) - return compact.pending_nodes[id].node + return compact.pending_nodes.stmts[id][:inst] end function getindex(compact::IncrementalCompact, ssa::NewSSAValue) - return compact.new_new_nodes[ssa.id].node + return compact.new_new_nodes.stmts[ssa.id][:inst] end function count_added_node!(compact::IncrementalCompact, @nospecialize(v)) @@ -607,22 +672,26 @@ function count_added_node!(compact::IncrementalCompact, @nospecialize(v)) needs_late_fixup end -function resort_pending!(compact) - sort!(compact.pending_perm, DEFAULT_STABLE, Order.By(x->compact.pending_nodes[x].pos, Order.Forward)) +function add_pending!(compact::IncrementalCompact, pos::Int, attach_after::Bool) + node = add!(compact.pending_nodes, pos, attach_after) + # TODO: switch this to `l = length(pending_nodes); splice!(pending_perm, searchsorted(pending_perm, l), l)` + push!(compact.pending_perm, length(compact.pending_nodes)) + sort!(compact.pending_perm, DEFAULT_STABLE, Order.By(x->compact.pending_nodes.info[x].pos, Order.Forward)) + return node end function insert_node!(compact::IncrementalCompact, before, @nospecialize(typ), @nospecialize(val), attach_after::Bool=false) if isa(before, SSAValue) if before.id < compact.result_idx count_added_node!(compact, val) - line = compact.result_lines[before.id] - push!(compact.new_new_nodes, NewNode(before.id, attach_after, typ, val, line)) - return NewSSAValue(length(compact.new_new_nodes)) + line = compact.result[before.id][:line] + node = add!(compact.new_new_nodes, before.id, attach_after) + node[:inst], node[:type], node[:line] = val, typ, line + return NewSSAValue(node.idx) else - line = compact.ir.lines[before.id] - push!(compact.pending_nodes, NewNode(before.id, attach_after, typ, val, line)) - push!(compact.pending_perm, length(compact.pending_nodes)) - resort_pending!(compact) + line = compact.ir.stmts[before.id][:line] + node = add_pending!(compact, before.id, attach_after) + node[:inst], node[:type], node[:line] = val, typ, line os = OldSSAValue(length(compact.ir.stmts) + length(compact.ir.new_nodes) + length(compact.pending_nodes)) push!(compact.ssa_rename, os) push!(compact.used_ssas, 0) @@ -632,127 +701,117 @@ function insert_node!(compact::IncrementalCompact, before, @nospecialize(typ), @ pos = before.id if pos > length(compact.ir.stmts) #@assert attach_after - entry = compact.pending_nodes[pos - length(compact.ir.stmts) - length(compact.ir.new_nodes)] - pos, attach_after = entry.pos, entry.attach_after + info = compact.pending_nodes.info[pos - length(compact.ir.stmts) - length(compact.ir.new_nodes)] + pos, attach_after = info.pos, info.attach_after end - line = compact.ir.lines[pos] - push!(compact.pending_nodes, NewNode(pos, attach_after, typ, val, line)) - push!(compact.pending_perm, length(compact.pending_nodes)) - resort_pending!(compact) + line = compact.ir.stmts[pos][:line] + node = add_pending!(compact, pos, attach_after) + node[:inst], node[:type], node[:line] = val, typ, line os = OldSSAValue(length(compact.ir.stmts) + length(compact.ir.new_nodes) + length(compact.pending_nodes)) push!(compact.ssa_rename, os) push!(compact.used_ssas, 0) return os elseif isa(before, NewSSAValue) - before_entry = compact.new_new_nodes[before.id] - push!(compact.new_new_nodes, NewNode(before_entry.pos, attach_after, typ, val, before_entry.line)) - return NewSSAValue(length(compact.new_new_nodes)) + before_entry = compact.new_new_nodes.info[before.id] + line = compact.new_new_nodes.stmts[before.id][:line] + new_entry = add!(compact.new_new_nodes, before_entry.pos, attach_after) + new_entry[:inst], new_entry[:type], new_entry[:line] = val, typ, line + return NewSSAValue(new_entry.idx) else error("Unsupported") end end -function append_node!(ir, @nospecialize(typ), @nospecialize(node), line) - push!(ir.stmts, node) - push!(ir.types, typ) - push!(ir.lines, line) - push!(ir.flags, 0) - last_bb = ir.cfg.blocks[end] - ir.cfg.blocks[end] = BasicBlock(first(last_bb.stmts):length(ir.stmts), - last_bb.preds, - last_bb.succs) - return SSAValue(length(ir.stmts)) -end - function insert_node_here!(compact::IncrementalCompact, @nospecialize(val), @nospecialize(typ), ltable_idx::Int32, reverse_affinity::Bool=false) - if compact.result_idx > length(compact.result) - @assert compact.result_idx == length(compact.result) + 1 - resize!(compact, compact.result_idx) - end refinish = false - if compact.result_idx == first(compact.result_bbs[compact.active_result_bb].stmts) && reverse_affinity + result_idx = compact.result_idx + if result_idx == first(compact.result_bbs[compact.active_result_bb].stmts) && reverse_affinity compact.active_result_bb -= 1 refinish = true end - compact.result[compact.result_idx] = val - compact.result_types[compact.result_idx] = typ - compact.result_lines[compact.result_idx] = ltable_idx - compact.result_flags[compact.result_idx] = 0x00 + if result_idx > length(compact.result) + @assert result_idx == length(compact.result) + 1 + resize!(compact, result_idx) + end + node = compact.result[result_idx] + node[:inst], node[:type], node[:line], node[:flag] = val, typ, ltable_idx, 0x00 if count_added_node!(compact, val) - push!(compact.late_fixup, compact.result_idx) + push!(compact.late_fixup, result_idx) end - ret = SSAValue(compact.result_idx) - compact.result_idx += 1 + compact.result_idx = result_idx + 1 + inst = SSAValue(result_idx) refinish && finish_current_bb!(compact, 0) - ret + return inst end function getindex(view::TypesView, v::OldSSAValue) id = v.id - if id <= length(view.ir.ir.types) - return view.ir.ir.types[id] + ir = view.ir.ir + stmts = ir.stmts + if id <= length(stmts) + return stmts[id][:type] end - id -= length(view.ir.ir.types) - if id <= length(view.ir.ir.new_nodes) - return view.ir.ir.new_nodes[id].typ + id -= length(stmts) + if id <= length(ir.new_nodes) + return ir.new_nodes.stmts[id][:type] end - id -= length(view.ir.ir.new_nodes) - return view.ir.pending_nodes[id].typ + id -= length(ir.new_nodes) + return view.ir.pending_nodes.stmts[id][:type] end function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::SSAValue) @assert idx.id < compact.result_idx - (compact.result[idx.id] === v) && return + (compact.result[idx.id][:inst] === v) && return # Kill count for current uses - for ops in userefs(compact.result[idx.id]) + for ops in userefs(compact.result[idx.id][:inst]) val = ops[] if isa(val, SSAValue) @assert compact.used_ssas[val.id] >= 1 compact.used_ssas[val.id] -= 1 end end - compact.result[idx.id] = v + compact.result[idx.id][:inst] = v # Add count for new use if count_added_node!(compact, v) push!(compact.late_fixup, idx.id) end + return compact end function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::Int) if idx < compact.result_idx compact[SSAValue(idx)] = v else - compact.ir.stmts[idx] = v + compact.ir.stmts[idx][:inst] = v end - return nothing + return compact end -function getindex(view::TypesView, idx) - isa(idx, SSAValue) && (idx = idx.id) +getindex(view::TypesView, idx::SSAValue) = getindex(view, idx.id) +function getindex(view::TypesView, idx::Int) if isa(view.ir, IncrementalCompact) && idx < view.ir.result_idx - return view.ir.result_types[idx] + return view.ir.result[idx][:type] elseif isa(view.ir, IncrementalCompact) && view.ir.renamed_new_nodes - if idx <= length(view.ir.result_types) - return view.ir.result_types[idx] + if idx <= length(view.ir.result) + return view.ir.result[idx][:type] else - return view.ir.new_new_nodes[idx - length(view.ir.result_types)].typ + return view.ir.new_new_nodes.stmts[idx - length(view.ir.result)][:type] end else ir = isa(view.ir, IncrementalCompact) ? view.ir.ir : view.ir - if idx <= length(ir.types) - return ir.types[idx] + if idx <= length(ir.stmts) + return ir.stmts[idx][:type] else - return ir.new_nodes[idx - length(ir.types)].typ + return ir.new_nodes.stmts[idx - length(ir.stmts)][:type] end end end function getindex(view::TypesView, idx::NewSSAValue) if isa(view.ir, IncrementalCompact) - compact = view.ir - compact.new_new_nodes[idx.id].typ + return view.ir.new_new_nodes.stmts[idx.id][:type] else - view.ir.new_nodes[idx.id].typ + return view.ir.new_nodes.stmts[idx.id][:type] end end @@ -853,16 +912,16 @@ function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to:: # Kill all statements in the block stmts = compact.result_bbs[compact.bb_rename_succ[to]].stmts for stmt in stmts - compact.result[stmt] = nothing + compact.result[stmt][:inst] = nothing end - compact.result[last(stmts)] = ReturnNode() + compact.result[last(stmts)][:inst] = ReturnNode() end else # We need to remove this edge from any phi nodes if to < active_bb idx = first(compact.result_bbs[compact.bb_rename_succ[to]].stmts) while idx < length(compact.result) - stmt = compact.result[idx] + stmt = compact.result[idx][:inst] stmt === nothing && continue isa(stmt, PhiNode) || break i = findfirst(x-> x === compact.bb_rename_pred[from], stmt.edges) @@ -888,37 +947,39 @@ function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to:: nothing end -function process_node!(compact::IncrementalCompact, result::Vector{Any}, - result_idx::Int, ssa_rename::Vector{Any}, - late_fixup::Vector{Int}, used_ssas::Vector{Int}, @nospecialize(stmt), - idx::Int, processed_idx::Int, active_bb::Int, do_rename_ssa::Bool) +function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instruction, idx::Int, processed_idx::Int, active_bb::Int, do_rename_ssa::Bool) + stmt = inst[:inst] + result = compact.result + ssa_rename = compact.ssa_rename + late_fixup = compact.late_fixup + used_ssas = compact.used_ssas ssa_rename[idx] = SSAValue(result_idx) if stmt === nothing ssa_rename[idx] = stmt elseif isa(stmt, OldSSAValue) ssa_rename[idx] = ssa_rename[stmt.id] elseif isa(stmt, GotoNode) && compact.cfg_transforms_enabled - result[result_idx] = GotoNode(compact.bb_rename_succ[stmt.label]) + result[result_idx][:inst] = GotoNode(compact.bb_rename_succ[stmt.label]) result_idx += 1 elseif isa(stmt, GlobalRef) || isa(stmt, GotoNode) - result[result_idx] = stmt + result[result_idx][:inst] = stmt result_idx += 1 elseif isa(stmt, GotoIfNot) && compact.cfg_transforms_enabled stmt = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa)::GotoIfNot - result[result_idx] = stmt + result[result_idx][:inst] = stmt cond = stmt.cond if isa(cond, Bool) && compact.fold_constant_branches if cond - result[result_idx] = nothing + result[result_idx][:inst] = nothing kill_edge!(compact, active_bb, active_bb, stmt.dest) # Don't increment result_idx => Drop this statement else - result[result_idx] = GotoNode(compact.bb_rename_succ[stmt.dest]) + result[result_idx][:inst] = GotoNode(compact.bb_rename_succ[stmt.dest]) kill_edge!(compact, active_bb, active_bb, active_bb+1) result_idx += 1 end else - result[result_idx] = GotoIfNot(cond, compact.bb_rename_succ[stmt.dest]) + result[result_idx][:inst] = GotoIfNot(cond, compact.bb_rename_succ[stmt.dest]) result_idx += 1 end elseif isa(stmt, Expr) @@ -926,7 +987,7 @@ function process_node!(compact::IncrementalCompact, result::Vector{Any}, if compact.cfg_transforms_enabled && isexpr(stmt, :enter) stmt.args[1] = compact.bb_rename_succ[stmt.args[1]::Int] end - result[result_idx] = stmt + result[result_idx][:inst] = stmt result_idx += 1 elseif isa(stmt, PiNode) # As an optimization, we eliminate any trivial pinodes. For performance, we use === @@ -935,7 +996,7 @@ function process_node!(compact::IncrementalCompact, result::Vector{Any}, stmt = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa)::PiNode pi_val = stmt.val if isa(pi_val, SSAValue) - if stmt.typ === compact.result_types[pi_val.id] + if stmt.typ === compact.result[pi_val.id][:type] ssa_rename[idx] = pi_val return result_idx end @@ -946,10 +1007,10 @@ function process_node!(compact::IncrementalCompact, result::Vector{Any}, return result_idx end end - result[result_idx] = stmt + result[result_idx][:inst] = stmt result_idx += 1 elseif isa(stmt, ReturnNode) || isa(stmt, UpsilonNode) || isa(stmt, GotoIfNot) - result[result_idx] = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa) + result[result_idx][:inst] = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa) result_idx += 1 elseif isa(stmt, PhiNode) values = process_phinode_values(stmt.values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, do_rename_ssa) @@ -961,11 +1022,11 @@ function process_node!(compact::IncrementalCompact, result::Vector{Any}, ssa_rename[idx] = values[1] else edges = compact.cfg_transforms_enabled ? map!(i->compact.bb_rename_pred[i], stmt.edges, stmt.edges) : stmt.edges - result[result_idx] = PhiNode(edges, values) + result[result_idx][:inst] = PhiNode(edges, values) result_idx += 1 end elseif isa(stmt, PhiCNode) - result[result_idx] = PhiCNode(process_phinode_values(stmt.values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, do_rename_ssa)) + result[result_idx][:inst] = PhiCNode(process_phinode_values(stmt.values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, do_rename_ssa)) result_idx += 1 elseif isa(stmt, SSAValue) # identity assign, replace uses of this ssa value with its result @@ -980,26 +1041,17 @@ function process_node!(compact::IncrementalCompact, result::Vector{Any}, return result_idx end -function process_node!(compact::IncrementalCompact, result_idx::Int, @nospecialize(stmt), idx::Int, processed_idx::Int, active_bb::Int, do_rename_ssa::Bool) - return process_node!(compact, compact.result, result_idx, compact.ssa_rename, - compact.late_fixup, compact.used_ssas, stmt, idx, processed_idx, active_bb, - do_rename_ssa) -end - function resize!(compact::IncrementalCompact, nnewnodes) old_length = length(compact.result) resize!(compact.result, nnewnodes) - resize!(compact.result_types, nnewnodes) - resize!(compact.result_lines, nnewnodes) - resize!(compact.result_flags, nnewnodes) resize!(compact.used_ssas, nnewnodes) - for i in (old_length+1):nnewnodes + for i in (old_length + 1):nnewnodes compact.used_ssas[i] = 0 end - nothing + return compact end -function finish_current_bb!(compact, active_bb, old_result_idx=compact.result_idx, unreachable=false) +function finish_current_bb!(compact::IncrementalCompact, active_bb, old_result_idx=compact.result_idx, unreachable=false) if compact.active_result_bb > length(compact.result_bbs) #@assert compact.bb_rename[active_bb] == -1 return true @@ -1011,15 +1063,12 @@ function finish_current_bb!(compact, active_bb, old_result_idx=compact.result_id if !compact.cfg_transforms_enabled || active_bb == 0 || active_bb > length(compact.bb_rename_succ) || compact.bb_rename_succ[active_bb] != -1 if compact.result_idx == first(bb.stmts) length(compact.result) < old_result_idx && resize!(compact, old_result_idx) + node = compact.result[old_result_idx] if unreachable - compact.result[old_result_idx] = ReturnNode() - compact.result_types[old_result_idx] = Union{} + node[:inst], node[:type], node[:line] = ReturnNode(), Union{}, 0 else - compact.result[old_result_idx] = nothing - compact.result_types[old_result_idx] = Nothing + node[:inst], node[:type], node[:line] = nothing, Nothing, 0 end - compact.result_lines[old_result_idx] = 0 - compact.result_flags[old_result_idx] = 0x00 compact.result_idx = old_result_idx + 1 elseif compact.cfg_transforms_enabled && compact.result_idx - 1 == first(bb.stmts) # Optimization: If this BB consists of only a branch, eliminate this bb @@ -1039,25 +1088,25 @@ end function attach_after_stmt_after(compact::IncrementalCompact, idx::Int) compact.new_nodes_idx > length(compact.perm) && return false - entry = compact.ir.new_nodes[compact.perm[compact.new_nodes_idx]] - entry.pos == idx && entry.attach_after + entry = compact.ir.new_nodes.info[compact.perm[compact.new_nodes_idx]] + return entry.pos == idx && entry.attach_after end -function process_newnode!(compact, new_idx, new_node_entry, idx, active_bb, do_rename_ssa) +function process_newnode!(compact::IncrementalCompact, new_idx::Int, new_node_entry::Instruction, new_node_info::NewNodeInfo, idx::Int, active_bb::Int, do_rename_ssa::Bool) old_result_idx = compact.result_idx bb = compact.ir.cfg.blocks[active_bb] - compact.result_types[old_result_idx] = new_node_entry.typ - compact.result_lines[old_result_idx] = new_node_entry.line - result_idx = process_node!(compact, old_result_idx, new_node_entry.node, new_idx, idx - 1, active_bb, do_rename_ssa) + node = compact.result[old_result_idx] + node[] = new_node_entry + result_idx = process_node!(compact, old_result_idx, node, new_idx, idx - 1, active_bb, do_rename_ssa) compact.result_idx = result_idx # If this instruction has reverse affinity and we were at the end of a basic block, # finish it now. - if new_node_entry.attach_after && idx == last(bb.stmts)+1 && !attach_after_stmt_after(compact, idx-1) + if new_node_info.attach_after && idx == last(bb.stmts)+1 && !attach_after_stmt_after(compact, idx-1) active_bb += 1 finish_current_bb!(compact, active_bb, old_result_idx) end (old_result_idx == result_idx) && return iterate(compact, (idx, active_bb)) - return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx]), (idx, active_bb) + return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx][:inst]), (idx, active_bb) end struct CompactPeekIterator @@ -1065,28 +1114,30 @@ struct CompactPeekIterator start_idx::Int end -entry_at_idx(entry, idx) = entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx -function iterate(it::CompactPeekIterator, (idx, aidx, bidx)::NTuple{3, Int}=(it.start_idx,it.compact.new_nodes_idx,1)) +entry_at_idx(entry::NewNodeInfo, idx::Int) = entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx +function iterate(it::CompactPeekIterator, (idx, aidx, bidx)::NTuple{3, Int}=(it.start_idx, it.compact.new_nodes_idx, 1)) # TODO: Take advantage of the fact that these arrays are sorted + # TODO: this return value design is horrible compact = it.compact if compact.new_nodes_idx <= length(compact.perm) + new_nodes = compact.ir.new_nodes for eidx in aidx:length(compact.perm) - if entry_at_idx(compact.ir.new_nodes[compact.perm[eidx]], idx) - entry = compact.ir.new_nodes[compact.perm[eidx]] - return (entry.node, (idx, eidx+1, bidx)) + if entry_at_idx(new_nodes.info[compact.perm[eidx]], idx) + entry = new_nodes.stmts[compact.perm[eidx]] + return (entry[:inst], (idx, eidx+1, bidx)) end end end if !isempty(compact.pending_perm) for eidx in bidx:length(compact.pending_perm) - if entry_at_idx(compact.pending_nodes[compact.pending_perm[eidx]], idx) - entry = compact.pending_nodes[compact.compact.pending_perm[eidx]] - return (entry.node, (idx, aidx, eidx+1)) + if entry_at_idx(compact.pending_nodes.info[compact.pending_perm[eidx]], idx) + entry = compact.pending_nodes.stmts[compact.pending_perm[eidx]] + return (entry[:inst], (idx, aidx, eidx+1)) end end end idx > length(compact.ir.stmts) && return nothing - return (compact.ir.stmts[idx], (idx + 1, aidx, bidx)) + return (compact.ir.stmts[idx][:inst], (idx + 1, aidx, bidx)) end function iterate(compact::IncrementalCompact, (idx, active_bb)::Tuple{Int, Int}=(compact.idx, 1)) @@ -1106,15 +1157,15 @@ function iterate(compact::IncrementalCompact, (idx, active_bb)::Tuple{Int, Int}= compact.idx = last(bb.stmts) # Pop any remaining insertion nodes while compact.new_nodes_idx <= length(compact.perm) - entry = compact.ir.new_nodes[compact.perm[compact.new_nodes_idx]] + entry = compact.ir.new_nodes.info[compact.perm[compact.new_nodes_idx]] if !(entry.attach_after ? entry.pos <= compact.idx - 1 : entry.pos <= compact.idx) break end compact.new_nodes_idx += 1 end while !isempty(compact.pending_perm) - entry = compact.pending_nodes[compact.pending_perm[1]]; - if !(entry.attach_after ? entry.pos <= compact.idx - 1 : entry.pos <= compact.idx) + info = compact.pending_nodes.info[compact.pending_perm[1]]; + if !(info.attach_after ? info.pos <= compact.idx - 1 : info.pos <= compact.idx) break end popfirst!(compact.pending_perm) @@ -1124,32 +1175,32 @@ function iterate(compact::IncrementalCompact, (idx, active_bb)::Tuple{Int, Int}= if finish_current_bb!(compact, active_bb, old_result_idx, true) return iterate(compact, (compact.idx, active_bb + 1)) else - return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx]), (compact.idx, active_bb + 1) + return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx][:inst]), (compact.idx, active_bb + 1) end end if compact.new_nodes_idx <= length(compact.perm) && - (entry = compact.ir.new_nodes[compact.perm[compact.new_nodes_idx]]; - entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx) + (info = compact.ir.new_nodes.info[compact.perm[compact.new_nodes_idx]]; + info.attach_after ? info.pos == idx - 1 : info.pos == idx) new_idx = compact.perm[compact.new_nodes_idx] compact.new_nodes_idx += 1 - new_node_entry = compact.ir.new_nodes[new_idx] + new_node_entry = compact.ir.new_nodes.stmts[new_idx] + new_node_info = compact.ir.new_nodes.info[new_idx] new_idx += length(compact.ir.stmts) - return process_newnode!(compact, new_idx, new_node_entry, idx, active_bb, true) + return process_newnode!(compact, new_idx, new_node_entry, new_node_info, idx, active_bb, true) elseif !isempty(compact.pending_perm) && - (entry = compact.pending_nodes[compact.pending_perm[1]]; - entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx) + (info = compact.pending_nodes.info[compact.pending_perm[1]]; + info.attach_after ? info.pos == idx - 1 : info.pos == idx) new_idx = popfirst!(compact.pending_perm) - new_node_entry = compact.pending_nodes[new_idx] + new_node_entry = compact.pending_nodes.stmts[new_idx] + new_node_info = compact.pending_nodes.info[new_idx] new_idx += length(compact.ir.stmts) + length(compact.ir.new_nodes) - return process_newnode!(compact, new_idx, new_node_entry, idx, active_bb, false) + return process_newnode!(compact, new_idx, new_node_entry, new_node_info, idx, active_bb, false) end # This will get overwritten in future iterations if # result_idx is not, incremented, but that's ok and expected - compact.result_types[old_result_idx] = compact.ir.types[idx] - compact.result_lines[old_result_idx] = compact.ir.lines[idx] - compact.result_flags[old_result_idx] = compact.ir.flags[idx] + compact.result[old_result_idx] = compact.ir.stmts[idx] result_idx = process_node!(compact, old_result_idx, compact.ir.stmts[idx], idx, idx, active_bb, true) - stmt_if_any = old_result_idx == result_idx ? nothing : compact.result[old_result_idx] + stmt_if_any = old_result_idx == result_idx ? nothing : compact.result[old_result_idx][:inst] compact.result_idx = result_idx if idx == last(bb.stmts) && !attach_after_stmt_after(compact, idx) finish_current_bb!(compact, active_bb, old_result_idx) @@ -1160,19 +1211,17 @@ function iterate(compact::IncrementalCompact, (idx, active_bb)::Tuple{Int, Int}= idx += 1 @goto restart end - if !isassigned(compact.result, old_result_idx) - @assert false - end - return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx]), (compact.idx, active_bb) + @assert isassigned(compact.result.inst, old_result_idx) + return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx][:inst]), (compact.idx, active_bb) end function maybe_erase_unused!(extra_worklist, compact, idx, callback = x->nothing) - stmt = compact.result[idx] + stmt = compact.result[idx][:inst] stmt === nothing && return false if compact_exprtype(compact, SSAValue(idx)) === Bottom effect_free = false else - effect_free = stmt_effect_free(stmt, compact.result_types[idx], compact, compact.ir.sptypes) + effect_free = stmt_effect_free(stmt, compact.result[idx][:type], compact, compact.ir.sptypes) end if effect_free for ops in userefs(stmt) @@ -1189,7 +1238,7 @@ function maybe_erase_unused!(extra_worklist, compact, idx, callback = x->nothing callback(val) end end - compact.result[idx] = nothing + compact.result[idx][:inst] = nothing return true end return false @@ -1238,17 +1287,16 @@ end function just_fixup!(compact::IncrementalCompact) for idx in compact.late_fixup - stmt = compact.result[idx] + stmt = compact.result[idx][:inst] new_stmt = fixup_node(compact, stmt) - (stmt !== new_stmt) && (compact.result[idx] = new_stmt) + (stmt === new_stmt) || (compact.result[idx][:inst] = new_stmt) end for idx in 1:length(compact.new_new_nodes) - node = compact.new_new_nodes[idx] - new_stmt = fixup_node(compact, node.node) - if node.node !== new_stmt - compact.new_new_nodes[idx] = NewNode( - node.pos, node.attach_after, node.typ, - new_stmt, node.line) + node = compact.new_new_nodes.stmts[idx] + stmt = node[:inst] + new_stmt = fixup_node(compact, stmt) + if new_stmt !== stmt + node[:inst] = new_stmt end end end @@ -1268,10 +1316,7 @@ end function non_dce_finish!(compact::IncrementalCompact) result_idx = compact.result_idx - resize!(compact.result, result_idx-1) - resize!(compact.result_types, result_idx-1) - resize!(compact.result_lines, result_idx-1) - resize!(compact.result_flags, result_idx-1) + resize!(compact.result, result_idx - 1) just_fixup!(compact) bb = compact.result_bbs[end] compact.result_bbs[end] = BasicBlock(bb, @@ -1289,13 +1334,13 @@ end function complete(compact::IncrementalCompact) result_bbs = resize!(compact.result_bbs, compact.active_result_bb-1) cfg = CFG(result_bbs, Int[first(result_bbs[i].stmts) for i in 2:length(result_bbs)]) - return IRCode(compact.ir, compact.result, compact.result_types, compact.result_lines, compact.result_flags, cfg, compact.new_new_nodes) + return IRCode(compact.ir, compact.result, cfg, compact.new_new_nodes) end -function compact!(code::IRCode, allow_cfg_transforms=false) +function compact!(code::IRCode, allow_cfg_transforms::Bool=false) compact = IncrementalCompact(code, allow_cfg_transforms) # Just run through the iterator without any processing - foreach(x -> nothing, compact) # x isa Pair{Int, Any} + for _ in compact; end # _ isa Pair{Int, Any} return finish(compact) end diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 66e830046eaf3..14283e789312e 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -42,21 +42,21 @@ function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) code[i] = stmt end end + ssavaluetypes = ci.ssavaluetypes + nstmts = length(code) ssavaluetypes = ci.ssavaluetypes isa Vector{Any} ? copy(ci.ssavaluetypes) : Any[ Any for i = 1:(ci.ssavaluetypes::Int) ] - ir = IRCode(code, ssavaluetypes, copy(ci.codelocs), copy(ci.ssaflags), cfg, collect(LineInfoNode, ci.linetable), - argtypes, Any[], sptypes) + stmts = InstructionStream(code, ssavaluetypes, copy(ci.codelocs), copy(ci.ssaflags)) + ir = IRCode(stmts, cfg, collect(LineInfoNode, ci.linetable), argtypes, Any[], sptypes) return ir end function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) @assert isempty(ir.new_nodes) # All but the first `nargs` slots will now be unused - resize!(ci.slotflags, nargs+1) - ci.code = ir.stmts - ci.codelocs = ir.lines - ci.linetable = ir.linetable - ci.ssavaluetypes = ir.types - ci.ssaflags = ir.flags + resize!(ci.slotflags, nargs + 1) + stmts = ir.stmts + ci.code, ci.ssavaluetypes, ci.codelocs, ci.ssaflags, ci.linetable = + stmts.inst, stmts.type, stmts.line, stmts.flag, ir.linetable for metanode in ir.meta push!(ci.code, metanode) push!(ci.codelocs, 1) @@ -76,23 +76,21 @@ function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) end stmt = urs[] if isa(stmt, GotoNode) - ci.code[i] = GotoNode(first(ir.cfg.blocks[stmt.label].stmts)) + stmt = GotoNode(first(ir.cfg.blocks[stmt.label].stmts)) elseif isa(stmt, GotoIfNot) - ci.code[i] = Expr(:gotoifnot, stmt.cond, first(ir.cfg.blocks[stmt.dest].stmts)) + stmt = Expr(:gotoifnot, stmt.cond, first(ir.cfg.blocks[stmt.dest].stmts)) elseif isa(stmt, PhiNode) - ci.code[i] = PhiNode(Any[last(ir.cfg.blocks[edge].stmts) for edge in stmt.edges], stmt.values) + stmt = PhiNode(Any[last(ir.cfg.blocks[edge::Int].stmts) for edge in stmt.edges], stmt.values) elseif isa(stmt, ReturnNode) if isdefined(stmt, :val) - ci.code[i] = Expr(:return, stmt.val) + stmt = Expr(:return, stmt.val) else - ci.code[i] = Expr(:unreachable) + stmt = Expr(:unreachable) end elseif isa(stmt, Expr) && stmt.head === :enter - stmt.args[1] = first(ir.cfg.blocks[stmt.args[1]].stmts) - ci.code[i] = stmt - else - ci.code[i] = stmt + stmt.args[1] = first(ir.cfg.blocks[stmt.args[1]::Int].stmts) end + ci.code[i] = stmt end end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 2e6a55db16e25..3895e220eed3a 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -116,35 +116,34 @@ end function simple_walk(compact::IncrementalCompact, @nospecialize(defssa#=::AnySSAValue=#), pi_callback=(pi, idx)->false) while true - if isa(defssa, OldSSAValue) && already_inserted(compact, defssa) - rename = compact.ssa_rename[defssa.id] - if isa(rename, AnySSAValue) - defssa = rename - continue + if isa(defssa, OldSSAValue) + if already_inserted(compact, defssa) + rename = compact.ssa_rename[defssa.id] + if isa(rename, AnySSAValue) + defssa = rename + continue + end + return rename end - return rename end def = compact[defssa] if isa(def, PiNode) if pi_callback(def, defssa) return defssa end - if isa(def.val, SSAValue) - if is_old(compact, defssa) - defssa = OldSSAValue(def.val.id) - else - defssa = def.val - end + def = def.val + if isa(def, SSAValue) + is_old(compact, defssa) && (def = OldSSAValue(def.id)) else - return def.val + return def end + defssa = def elseif isa(def, AnySSAValue) pi_callback(def, defssa) - if isa(def, SSAValue) && is_old(compact, defssa) - defssa = OldSSAValue(def.id) - else - defssa = def + if isa(def, SSAValue) + is_old(compact, defssa) && (def = OldSSAValue(def.id)) end + defssa = def elseif isa(def, Union{PhiNode, PhiCNode, Expr, GlobalRef}) return defssa else @@ -565,8 +564,9 @@ function getfield_elim_pass!(ir::IRCode, domtree::DomTree) # that's defined not to return its value to make life easier # for the backend. pi = insert_node_here!(compact, - PiNode(stmt.args[2], compact.result_types[idx]), compact.result_types[idx], - compact.result_lines[idx], true) + PiNode(stmt.args[2], compact.result[idx][:type]), + compact.result[idx][:type], + compact.result[idx][:line], true) compact.ssa_rename[compact.idx-1] = pi continue elseif is_known_call(stmt, (===), compact) @@ -738,7 +738,7 @@ function getfield_elim_pass!(ir::IRCode, domtree::DomTree) # Find the type for this allocation defexpr = ir[SSAValue(idx)] isexpr(defexpr, :new) || continue - typ = ir.types[idx] + typ = ir.stmts[idx][:type] if isa(typ, UnionAll) typ = unwrap_unionall(typ) end @@ -834,11 +834,12 @@ function getfield_elim_pass!(ir::IRCode, domtree::DomTree) ir end -function adce_erase!(phi_uses, extra_worklist, compact, idx) - if isa(compact.result[idx], PhiNode) - maybe_erase_unused!(extra_worklist, compact, idx, val->phi_uses[val.id]-=1) +function adce_erase!(phi_uses::Vector{Int}, extra_worklist::Vector{Int}, compact::IncrementalCompact, idx::Int) + # return whether this made a change + if isa(compact.result[idx][:inst], PhiNode) + return maybe_erase_unused!(extra_worklist, compact, idx, val -> phi_uses[val.id] -= 1) else - maybe_erase_unused!(extra_worklist, compact, idx) + return maybe_erase_unused!(extra_worklist, compact, idx) end end @@ -857,7 +858,7 @@ function mark_phi_cycles(compact::IncrementalCompact, safe_phis::BitSet, phi::In while !isempty(worklist) phi = pop!(worklist) push!(safe_phis, phi) - for ur in userefs(compact.result[phi]) + for ur in userefs(compact.result[phi][:inst]) val = ur[] isa(val, SSAValue) || continue isa(compact[val], PhiNode) || continue @@ -878,7 +879,7 @@ function adce_pass!(ir::IRCode) end non_dce_finish!(compact) for phi in all_phis - count_uses(compact.result[phi]::PhiNode, phi_uses) + count_uses(compact.result[phi][:inst]::PhiNode, phi_uses) end # Perform simple DCE for unused values extra_worklist = Int[] @@ -919,7 +920,9 @@ function type_lift_pass!(ir::IRCode) type_ctx_uses = Vector{Vector{Int}}[] has_non_type_ctx_uses = IdSet{Int}() lifted_undef = IdDict{Int, Any}() - for (idx, stmt) in pairs(ir.stmts) + insts = ir.stmts + for idx in 1:length(insts) + 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] @@ -927,26 +930,26 @@ function type_lift_pass!(ir::IRCode) # node (or an UpsilonNode() argument to a PhiC node), # so lift all these nodes that have maybe undef values processed = IdDict{Int, Union{SSAValue, Bool}}() - while isa(val, SSAValue) && isa(ir.stmts[val.id], PiNode) - val = ir.stmts[val.id].val + while isa(val, SSAValue) && isa(insts[val.id][:inst], PiNode) + val = (insts[val.id][:inst]::PiNode).val end - if !isa(val, SSAValue) || (!isa(ir.stmts[val.id], PhiNode) && !isa(ir.stmts[val.id], PhiCNode)) + 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 stmt.head === :undefcheck - ir.stmts[idx] = nothing + insts[idx][:inst] = nothing else - ir.stmts[idx] = true + insts[idx][:inst] = true end continue end stmt_id = val.id worklist = Tuple{Int, Int, SSAValue, Int}[(stmt_id, 0, SSAValue(0), 0)] - def = ir.stmts[stmt_id] + def = insts[stmt_id][:inst] if !haskey(lifted_undef, stmt_id) first = true while !isempty(worklist) item, w_up_id, which, use = pop!(worklist) - def = ir.stmts[item] + def = insts[item][:inst] if isa(def, PhiNode) edges = copy(def.edges) values = Vector{Any}(undef, length(edges)) @@ -969,24 +972,25 @@ function type_lift_pass!(ir::IRCode) else up_id = id = def.values[i].id @label restart - if !isa(ir.types[id], MaybeUndef) + if !isa(ir.stmts[id][:type], MaybeUndef) val = true else - if isa(ir.stmts[id], UpsilonNode) - up = ir.stmts[id] - if !isdefined(up, :val) + node = insts[id][:inst] + if isa(node, UpsilonNode) + if !isdefined(node, :val) val = false - elseif !isa(up.val, SSAValue) + elseif !isa(node.val, SSAValue) val = true else - id = up.val.id + id = node.val.id @goto restart end else - while isa(ir.stmts[id], PiNode) - id = ir.stmts[id].val.id + while isa(node, PiNode) + id = node.val.id + node = insts[id][:inst] end - if isa(ir.stmts[id], Union{PhiNode, PhiCNode}) + if isa(node, Union{PhiNode, PhiCNode}) if haskey(processed, id) val = processed[id] else @@ -1011,15 +1015,15 @@ function type_lift_pass!(ir::IRCode) phi.values[use] = new_phi else phi = phi::PhiCNode - ir[which].values[use] = insert_node!(ir, w_up_id, Bool, UpsilonNode(new_phi)) + phi.values[use] = insert_node!(ir, w_up_id, Bool, UpsilonNode(new_phi)) end end end end if stmt.head === :isdefined - ir.stmts[idx] = lifted_undef[stmt_id] + insts[idx][:inst] = lifted_undef[stmt_id] else - ir.stmts[idx] = Expr(:throw_undef_if_not, stmt.args[1], lifted_undef[stmt_id]) + insts[idx][:inst] = Expr(:throw_undef_if_not, stmt.args[1], lifted_undef[stmt_id]) end end end @@ -1064,7 +1068,7 @@ function cfg_simplify!(ir::IRCode) while merged_succ[curr] != 0 curr = merged_succ[curr] end - terminator = ir.stmts[ir.cfg.blocks[curr].stmts[end]] + terminator = ir.stmts[ir.cfg.blocks[curr].stmts[end]][:inst] if isa(terminator, GotoNode) || isa(terminator, ReturnNode) break end @@ -1097,50 +1101,55 @@ function cfg_simplify!(ir::IRCode) for i = 1:length(result_bbs_lengths) bb_starts[i+1] = bb_starts[i] + result_bbs_lengths[i] end - # Look at the original successor - function compute_succs(i) - orig_bb = result_bbs[i] - while merged_succ[orig_bb] != 0 - orig_bb = merged_succ[orig_bb] - end - map(i->bb_rename_succ[i], bbs[orig_bb].succs) - end - - function compute_preds(i) - orig_bb = result_bbs[i] - preds = bbs[orig_bb].preds - map(preds) do pred - while merge_into[pred] != 0 - pred = merge_into[pred] + # Figure out the pred and succ lists for each basic block in our merged result + cresult_bbs = let result_bbs = result_bbs, + merged_succ = merged_succ, + merge_into = merge_into, + bbs = bbs, + bb_rename_succ = bb_rename_succ + function compute_succs(i) + # Look at the original successor + orig_bb = result_bbs[i] + while merged_succ[orig_bb] != 0 + orig_bb = merged_succ[orig_bb] + end + newsuccs = map(i->bb_rename_succ[i], bbs[orig_bb].succs) + return newsuccs end - bb_rename_succ[pred] + function compute_preds(i) + orig_bb = result_bbs[i] + preds = bbs[orig_bb].preds + newpreds = map(preds) do pred + while merge_into[pred] != 0 + pred = merge_into[pred] + end + bb_rename_succ[pred] + end + return newpreds + end + BasicBlock[BasicBlock( + StmtRange(bb_starts[i], i + 1 > length(bb_starts) ? length(compact.result) : bb_starts[i + 1] - 1), + compute_preds(i), compute_succs(i)) for i = 1:length(result_bbs)] end - end - cresult_bbs = BasicBlock[BasicBlock( - StmtRange(bb_starts[i], i+1 > length(bb_starts) ? length(compact.result) : bb_starts[i+1]-1), - compute_preds(i), compute_succs(i)) for i = 1:length(result_bbs)] compact = IncrementalCompact(ir, true) - # We're messing with the CFG. We don't want compaction to do - # so independently + # Run instruction compaction to produce the result, + # but we're messing with the CFG + # so we don't want compaction to do so independently compact.fold_constant_branches = false compact.bb_rename_succ = bb_rename_succ compact.bb_rename_pred = bb_rename_pred compact.result_bbs = cresult_bbs - result_idx = 1 for (idx, orig_bb) in enumerate(result_bbs) ms = orig_bb while ms != 0 for i in bbs[ms].stmts - stmt = ir.stmts[i] - compact.result[compact.result_idx] = nothing - compact.result_types[compact.result_idx] = ir.types[i] - compact.result_lines[compact.result_idx] = ir.lines[i] - compact.result_flags[compact.result_idx] = ir.flags[i] - # If we merged a basic block, we need remove the trailing GotoNode (if any) - if isa(stmt, GotoNode) && merged_succ[ms] != 0 - # Do nothing + node = ir.stmts[i] + compact.result[compact.result_idx] = node + if isa(node[:inst], GotoNode) && merged_succ[ms] != 0 + # If we merged a basic block, we need remove the trailing GotoNode (if any) + compact.result[compact.result_idx][:inst] = nothing else - process_node!(compact, compact.result_idx, stmt, i, i, ms, true) + process_node!(compact, compact.result_idx, node, i, i, ms, true) end # We always increase the result index to ensure a predicatable # placement of the resulting nodes. diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 4f159478d9671..bc77095b45d0a 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -234,7 +234,7 @@ to catch up and print the intermediate scopes. Which scope is printed is indicat by the indentation of the method name and by an increased thickness of the appropriate line for the scope. """ -function compute_ir_line_annotations(code::Union{IRCode, CodeInfo}) +function compute_ir_line_annotations(code::IRCode) loc_annotations = String[] loc_methods = String[] loc_lineno = String[] @@ -243,20 +243,12 @@ function compute_ir_line_annotations(code::Union{IRCode, CodeInfo}) last_lineno = 0 last_stack = [] last_printed_depth = 0 - stmts = (code isa IRCode ? code.stmts : code.code) linetable = code.linetable - lines = (code isa IRCode ? code.lines : code.codelocs) - for idx in eachindex(stmts) + lines = code.stmts.line + for idx in 1:length(lines) buf = IOBuffer() - # N.B.: The line array length not matching is invalid, - # but let's be robust here - if idx > length(lines) - line = Int32(0) - print(buf, "!") - else - line = lines[idx] - print(buf, "│") - end + line = lines[idx] + print(buf, "│") depth = compute_inlining_depth(linetable, line) iline = line lineno = 0 @@ -343,7 +335,7 @@ function DILineInfoPrinter(linetable::Vector, showtypes::Bool=false) collapse = showtypes ? false : true indent_all = true # convert lineidx to a vector - if lineidx < 0 + if lineidx == typemin(Int32) # sentinel value: reset internal (and external) state pops = indent("└") if !isempty(pops) @@ -508,23 +500,26 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print cols = displaysize(io)[2] used = BitSet() stmts = code.stmts - types = code.types + isempty(stmts) && return # unlikely, but avoid errors from reducing over empty sets cfg = code.cfg max_bb_idx_size = length(string(length(cfg.blocks))) + new_nodes = code.new_nodes.stmts + new_nodes_info = code.new_nodes.info + bb_idx = 1 for stmt in stmts - scan_ssa_use!(push!, used, stmt) + scan_ssa_use!(push!, used, stmt[:inst]) end - bb_idx = 1 - new_nodes = code.new_nodes - if any(i -> !isassigned(code.new_nodes, i), 1:length(code.new_nodes)) + if any(i -> !isassigned(new_nodes.inst, i), 1:length(new_nodes)) printstyled(io, "ERROR: New node array has unset entry\n", color=:red) - new_nodes = new_nodes[filter(i -> isassigned(code.new_nodes, i), 1:length(code.new_nodes))] + new_nodes_perm = filter(i -> isassigned(new_nodes.inst, i), 1:length(new_nodes)) + else + new_nodes_perm = collect(1:length(new_nodes)) end - for nn in new_nodes - scan_ssa_use!(push!, used, nn.node) + for nn in new_nodes_perm + scan_ssa_use!(push!, used, new_nodes[nn][:inst]) end - perm = sortperm(new_nodes, by = x->x.pos) - new_nodes_perm = Iterators.Stateful(perm) + sort!(new_nodes_perm, by = x -> (x = new_nodes_info[x]; (x.pos, x.attach_after))) + perm_idx = 1 if isempty(used) maxlength_idx = 0 @@ -538,10 +533,10 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print max_lineno_width = maximum(length(str) for str in loc_lineno) max_method_width = maximum(length(str) for str in loc_methods) end - max_depth = maximum(compute_inlining_depth(code.linetable, line) for line in code.lines) + max_depth = maximum(compute_inlining_depth(code.linetable, stmts[i][:line]) for i in 1:length(stmts.line)) last_stack = [] - for idx in eachindex(stmts) - if !isassigned(stmts, idx) + for idx in 1:length(stmts) + if !isassigned(stmts.inst, idx) # This is invalid, but do something useful rather # than erroring, to make debugging easier printstyled(io, "#UNDEF\n", color=:red) @@ -570,7 +565,7 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print end # Print linetable information if verbose_linetable - stack = compute_loc_stack(code.linetable, code.lines[idx]) + stack = compute_loc_stack(code.linetable, stmt[:line]) # We need to print any stack frames that did not exist in the last stack ndepth = max(1, length(stack)) rail = string(" "^(max_depth+1-ndepth), "│"^ndepth) @@ -581,7 +576,7 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print printstyled(io, "\e[$(start_column)G$(rail)\e[1G", color = :light_black) print(io, bb_guard_rail) ssa_guard = " "^(maxlength_idx + 4 + (i - 1)) - entry_label = "$(ssa_guard)$(method_name(entry)) at $(entry.file):$(entry.line) " + entry_label = "$(ssa_guard)$(method_name(entry)) at $(entry.file):$(entry[:line]) " hline = string("─"^(start_column-length(entry_label)-length(bb_guard_rail)+max_depth-i), "┐") printstyled(io, string(entry_label, hline), "\n"; color=:light_black) bb_guard_rail = bb_guard_rail_cont @@ -590,9 +585,17 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print printstyled(io, "\e[$(start_column)G$(rail)\e[1G", color = :light_black) last_stack = stack else - annotation = loc_annotations[idx] - loc_method = loc_methods[idx] - lineno = loc_lineno[idx] + if idx <= length(loc_annotations) + # N.B.: The line array length not matching is invalid, + # but let's be robust here + annotation = loc_annotations[idx] + loc_method = loc_methods[idx] + lineno = loc_lineno[idx] + else + annotation = "!" + loc_method = "" + lineno = "" + end # Print location information right aligned. If the line below is too long, it'll overwrite this, # but that's what we want. if get(io, :color, false) @@ -609,10 +612,12 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print end floop = true # print new nodes first in the right position - while !isempty(new_nodes_perm) && new_nodes[Iterators.peek(new_nodes_perm)].pos == idx - node_idx = popfirst!(new_nodes_perm) - new_node = new_nodes[node_idx] - node_idx += length(stmts) + while perm_idx <= length(new_nodes_perm) + node_idx = new_nodes_perm[perm_idx] + if new_nodes_info[node_idx].pos != idx + break + end + perm_idx += 1 if !floop && !verbose_linetable print(io, " "^(max_lineno_width + 1)) end @@ -625,12 +630,16 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print end print_sep = true floop = false - show_type = should_print_ssa_type(new_node.node) + new_node = new_nodes[node_idx] + node_idx += length(stmts) + show_type = should_print_ssa_type(new_node[:inst]) with_output_color(:green, io) do io′ - print_stmt(io′, node_idx, new_node.node, used, maxlength_idx, false, show_type) + print_stmt(io′, node_idx, new_node[:inst], used, maxlength_idx, false, show_type) end - if show_type - expr_type_printer(io, new_node.typ, node_idx in used) + if !isassigned(stmts.type, idx) # try to be robust against errors + printstyled(io, "::#UNDEF", color=:red) + elseif show_type + expr_type_printer(io, new_node[:type], node_idx in used) end println(io) end @@ -649,14 +658,12 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print if idx == last(bbrange) bb_idx += 1 end - show_type = should_print_ssa_type(stmt) - print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) - if !isassigned(types, idx) - # This is an error, but can happen if passes don't update their type information + show_type = should_print_ssa_type(stmt[:inst]) + print_stmt(io, idx, stmt[:inst], used, maxlength_idx, true, show_type) + if !isassigned(stmts.type, idx) # try to be robust against errors printstyled(io, "::#UNDEF", color=:red) elseif show_type - typ = types[idx] - expr_type_printer(io, typ, idx in used) + expr_type_printer(io, stmt[:type], idx in used) end println(io) end @@ -753,7 +760,7 @@ function show_ir(io::IO, code::CodeInfo, line_info_preprinter=DILineInfoPrinter( end bb_idx = 1 - for idx in eachindex(code.code) + for idx in 1:length(stmts) bb_idx = show_ir_stmt(ioctx, code, idx, line_info_preprinter, line_info_postprinter, used, cfg, bb_idx) end diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 8f54b4034e716..0255dab82ce5a 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -117,6 +117,7 @@ function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, @nospecializ elseif isa(stmt, TypedSlot) return NewSSAValue(insert_node!(ir, idx, stmt.typ, PiNode(ssa, stmt.typ)).id - length(ir.stmts)) end + @assert false # unreachable end function fixemup!(cond, rename, ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt)) @@ -173,7 +174,7 @@ function fixemup!(cond, rename, ir::IRCode, ci::CodeInfo, idx::Int, @nospecializ return urs[] end -function fixup_uses!(ir::IRCode, ci::CodeInfo, code, uses::Vector{Int}, slot, @nospecialize(ssa)) +function fixup_uses!(ir::IRCode, ci::CodeInfo, code::Vector{Any}, uses::Vector{Int}, slot::Int, @nospecialize(ssa)) for use in uses code[use] = fixemup!(stmt->slot_id(stmt)==slot, stmt->ssa, ir, ci, use, code[use]) end @@ -384,7 +385,7 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) while node !== -1 push!(result_order, node) cs = domtree.nodes[node].children - terminator = ir.stmts[last(ir.cfg.blocks[node].stmts)] + terminator = ir.stmts[last(ir.cfg.blocks[node].stmts)][:inst] iscondbr = isa(terminator, GotoIfNot) let old_node = node + 1 if length(cs) >= 1 @@ -426,21 +427,20 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) nstmts += length(ir.cfg.blocks[i].stmts) end end - result_stmts = Vector{Any}(undef, nstmts + ncritbreaks + nnewfallthroughs) - result_types = Any[Any for i = 1:length(result_stmts)] - result_ltable = fill(Int32(0), length(result_stmts)) - result_flags = fill(0x00, length(result_stmts)) - inst_rename = Vector{Any}(undef, length(ir.stmts)) + result = InstructionStream(nstmts + ncritbreaks + nnewfallthroughs) + inst_rename = Vector{Any}(undef, length(ir.stmts) + length(ir.new_nodes)) for i = 1:length(ir.new_nodes) - push!(inst_rename, SSAValue(nstmts + i + ncritbreaks + nnewfallthroughs)) + inst_rename[i + length(ir.stmts)] = SSAValue(i + length(result)) end bb_start_off = 0 crit_edge_breaks_fixup = Tuple{Int, Int}[] for (new_bb, bb) in pairs(result_order) if bb == 0 - @assert isa(result_stmts[bb_start_off+1], GotoNode) + nidx = bb_start_off + 1 + inst = result[nidx][:inst] + @assert isa(inst, GotoNode) # N.B.: The .label has already been renamed when it was created. - new_bbs[new_bb] = BasicBlock((bb_start_off+1):(bb_start_off+1), [new_bb-1], [result_stmts[bb_start_off+1].label]) + new_bbs[new_bb] = BasicBlock(nidx:nidx, [new_bb - 1], [inst.label]) bb_start_off += 1 continue end @@ -448,41 +448,45 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) inst_range = (bb_start_off+1):(bb_start_off+length(old_inst_range)) for (nidx, idx) in zip(inst_range, old_inst_range) inst_rename[idx] = SSAValue(nidx) - stmt = ir.stmts[idx] - if isa(stmt, PhiNode) - result_stmts[nidx] = rename_phinode_edges(stmt, bb, result_order, bb_rename) - else - result_stmts[nidx] = stmt + @assert !isassigned(result.inst, nidx) + node = result[nidx] + node[] = ir.stmts[idx] + inst = node[:inst] + if isa(inst, PhiNode) + node[:inst] = rename_phinode_edges(inst, bb, result_order, bb_rename) end - result_types[nidx] = ir.types[idx] - result_ltable[nidx] = ir.lines[idx] - result_flags[nidx] = ir.flags[idx] end # Now fix up the terminator - terminator = result_stmts[inst_range[end]] + terminator = result[inst_range[end]][:inst] if isa(terminator, GotoNode) # Convert to implicit fall through if bb_rename[terminator.label] == new_bb + 1 - result_stmts[inst_range[end]] = nothing + result[inst_range[end]][:inst] = nothing else - result_stmts[inst_range[end]] = GotoNode(bb_rename[terminator.label]) + result[inst_range[end]][:inst] = GotoNode(bb_rename[terminator.label]) end elseif isa(terminator, GotoIfNot) # Check if we need to break the critical edge if bb_rename[bb + 1] != new_bb + 1 @assert result_order[new_bb + 1] == 0 # Add an explicit goto node in the next basic block (we accounted for this above) - result_stmts[inst_range[end]+1] = GotoNode(bb_rename[bb+1]) + nidx = inst_range[end] + 1 + node = result[nidx] + node[:inst], node[:type], node[:line] = GotoNode(bb_rename[bb + 1]), Any, 0 end - result_stmts[inst_range[end]] = GotoIfNot(terminator.cond, bb_rename[terminator.dest]) + result[inst_range[end]][:inst] = GotoIfNot(terminator.cond, bb_rename[terminator.dest]) elseif !isa(terminator, ReturnNode) - if isa(terminator, Expr) && terminator.head === :enter - terminator.args[1] = bb_rename[terminator.args[1]] + if isa(terminator, Expr) + if terminator.head == :enter + terminator.args[1] = bb_rename[terminator.args[1]] + end end if bb_rename[bb + 1] != new_bb + 1 # Add an explicit goto node - result_stmts[inst_range[end]+1] = GotoNode(bb_rename[bb+1]) - inst_range = first(inst_range):(last(inst_range)+1) + nidx = inst_range[end] + 1 + node = result[nidx] + node[:inst], node[:type], node[:line] = GotoNode(bb_rename[bb + 1]), Any, 0 + inst_range = first(inst_range):(last(inst_range) + 1) end end bb_start_off += length(inst_range) @@ -493,18 +497,24 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) end new_bbs[new_bb] = BasicBlock(inst_range, new_preds, new_succs) end - result_stmts = Any[renumber_ssa!(result_stmts[i], inst_rename, true) for i in 1:length(result_stmts)] + for i in 1:length(result) + result[i][:inst] = renumber_ssa!(result[i][:inst], inst_rename, true) + end cfg = CFG(new_bbs, Int[first(bb.stmts) for bb in new_bbs[2:end]]) - new_new_nodes = Vector{NewNode}(undef, length(ir.new_nodes)) + new_new_nodes = NewNodeStream(length(ir.new_nodes)) for i = 1:length(ir.new_nodes) - entry = ir.new_nodes[i] - new_new_nodes[i] = NewNode(inst_rename[entry.pos].id, entry.attach_after, entry.typ, - renumber_ssa!(isa(entry.node, PhiNode) ? - rename_phinode_edges(entry.node, block_for_inst(ir.cfg, entry.pos), result_order, bb_rename) : entry.node, - inst_rename, true), - entry.line) - end - new_ir = IRCode(ir, result_stmts, result_types, result_ltable, result_flags, cfg, new_new_nodes) + new_info = ir.new_nodes.info[i] + new_new_info = NewNodeInfo(inst_rename[new_info.pos].id, new_info.attach_after) + new_new_nodes.info[i] = new_new_info + new_node = new_new_nodes.stmts[i] + new_node[] = ir.new_nodes.stmts[i] + new_node_inst = new_node[:inst] + if isa(new_node_inst, PhiNode) + new_node_inst = rename_phinode_edges(new_node_inst, block_for_inst(ir.cfg, new_info.pos), result_order, bb_rename) + end + new_node[:inst] = renumber_ssa!(new_node_inst, inst_rename, true) + end + new_ir = IRCode(ir, result, cfg, new_new_nodes) return new_ir end @@ -572,11 +582,12 @@ end function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, nargs::Int, sptypes::Vector{Any}, slottypes::Vector{Any}) - code = ir.stmts + code = ir.stmts.inst cfg = ir.cfg left = Int[] catch_entry_blocks = Tuple{Int, Int}[] - for (idx, stmt) in pairs(code) + for idx in 1:length(code) + stmt = code[idx] if isexpr(stmt, :enter) push!(catch_entry_blocks, (block_for_inst(cfg, idx), block_for_inst(cfg, stmt.args[1]))) end @@ -661,6 +672,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg worklist = Tuple{Int, Int, Vector{Any}}[(1, 0, initial_incoming_vals)] visited = BitSet() type_refine_phi = BitSet() + new_nodes = ir.new_nodes @timeit "SSA Rename" while !isempty(worklist) (item::Int, pred, incoming_vals) = pop!(worklist) # Rename existing phi nodes first, because their uses occur on the edge @@ -700,12 +712,13 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg push!(type_refine_phi, ssaval.id) end typ = incoming_val == undef_token ? MaybeUndef(Union{}) : typ_for_val(incoming_val, ci, sptypes, -1, slottypes) - old_entry = ir.new_nodes[ssaval.id] + old_entry = new_nodes.stmts[ssaval.id] if isa(typ, DelayedTyp) push!(type_refine_phi, ssaval.id) end - new_typ = isa(typ, DelayedTyp) ? Union{} : tmerge(old_entry.typ, typ) - ir.new_nodes[ssaval.id] = NewNode(old_entry.pos, old_entry.attach_after, new_typ, node, old_entry.line) + new_typ = isa(typ, DelayedTyp) ? Union{} : tmerge(old_entry[:type], typ) + old_entry[:type] = new_typ + old_entry[:inst] = node incoming_vals[slot] = ssaval end (item in visited) && continue @@ -787,9 +800,10 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg end end # Convert into IRCode form - new_code = ir.stmts - ssavalmap = Any[SSAValue(-1) for _ in 1:(length(ci.ssavaluetypes)+1)] - result_types = Any[Any for _ in 1:length(new_code)] + nstmts = length(ir.stmts) + new_code = Vector{Any}(undef, nstmts) + ssavalmap = Any[SSAValue(-1) for _ in 0:length(ci.ssavaluetypes)] + result_types = Any[Any for _ in 1:nstmts] # Detect statement positions for assignments and construct array for (bb, idx) in bbidxiter(ir) stmt = code[idx] @@ -827,16 +841,17 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg # TODO: This could just be the ones that depend on other phis push!(type_refine_phi, ssa.id) new_idx = ssa.id - node = ir.new_nodes[new_idx] - for i = 1:length(node.node.values) - orig_typ = typ = typ_for_val(node.node.values[i], ci, sptypes, -1, slottypes) + node = new_nodes.stmts[new_idx] + phic_values = (node[:inst]::PhiCNode).values + for i = 1:length(phic_values) + orig_typ = typ = typ_for_val(phic_values[i], ci, sptypes, -1, slottypes) @assert !isa(typ, MaybeUndef) while isa(typ, DelayedTyp) typ = types(ir)[typ.phi::NewSSAValue] end new_typ = tmerge(new_typ, typ) end - ir.new_nodes[new_idx] = NewNode(node.pos, node.attach_after, new_typ, node.node, node.line) + node[:type] = new_typ end end # This is a bit awkward, because it basically duplicates what type @@ -846,27 +861,39 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg while changed changed = false for new_idx in type_refine_phi - node = ir.new_nodes[new_idx] - new_typ = recompute_type(node.node, ci, ir, sptypes, slottypes) - if !(node.typ ⊑ new_typ) || !(new_typ ⊑ node.typ) - ir.new_nodes[new_idx] = NewNode(node.pos, node.attach_after, new_typ, node.node, node.line) + node = new_nodes.stmts[new_idx] + new_typ = recompute_type(node[:inst], ci, ir, sptypes, slottypes) + if !(node[:type] ⊑ new_typ) || !(new_typ ⊑ node[:type]) + node[:type] = new_typ changed = true end end end - result_types = Any[isa(result_types[i], DelayedTyp) ? types(ir)[result_types[i].phi::NewSSAValue] : result_types[i] for i in 1:length(result_types)] - new_nodes = NewNode[let node = ir.new_nodes[i] - typ = isa(node.typ, DelayedTyp) ? types(ir)[node.typ.phi::NewSSAValue] : node.typ - NewNode(node.pos, node.attach_after, typ, node.node, node.line) - end for i in 1:length(ir.new_nodes)] + for i in 1:length(result_types) + rt_i = result_types[i] + if rt_i isa DelayedTyp + result_types[i] = types(ir)[rt_i.phi::NewSSAValue] + end + end + for i = 1:length(new_nodes) + local node = new_nodes.stmts[i] + local typ = node[:type] + if isa(typ, DelayedTyp) + node[:type] = types(ir)[typ.phi::NewSSAValue] + end + end # Renumber SSA values - new_code = Any[new_to_regular(renumber_ssa!(new_code[i], ssavalmap), length(ir.stmts)) for i in 1:length(new_code)] - new_nodes = NewNode[let node = new_nodes[i] - NewNode(node.pos, node.attach_after, node.typ, - new_to_regular(renumber_ssa!(node.node, ssavalmap), length(ir.stmts)), - node.line) - end for i in 1:length(new_nodes)] - ir = IRCode(ir, new_code, result_types, ir.lines, ir.flags, ir.cfg, new_nodes) + @assert isempty(ir.stmts.type) + resize!(ir.stmts.type, nstmts) + for i in 1:nstmts + local node = ir.stmts[i] + node[:inst] = new_to_regular(renumber_ssa!(new_code[i], ssavalmap), nstmts) + node[:type] = result_types[i] + end + for i = 1:length(new_nodes) + local node = new_nodes.stmts[i] + node[:inst] = new_to_regular(renumber_ssa!(node[:inst], ssavalmap), nstmts) + end @timeit "domsort" ir = domsort_ssa!(ir, domtree) return ir end diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index 7363e70a72725..3ba32a85c5aa7 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -69,7 +69,7 @@ function verify_ir(ir::IRCode) error() end last_end = last(block.stmts) - terminator = ir.stmts[last_end] + terminator = ir.stmts[last_end][:inst] bb_unreachable(domtree, idx) && continue for p in block.preds @@ -114,7 +114,8 @@ function verify_ir(ir::IRCode) if length(block.succs) != 1 || block.succs[1] != idx + 1 # As a special case, we allow extra statements in the BB of an :enter # statement, until we can do proper CFG manipulations during compaction. - for stmt in ir.stmts[first(block.stmts):last(block.stmts)] + for idx in first(block.stmts):last(block.stmts) + stmt = ir.stmts[idx][:inst] if isexpr(stmt, :enter) terminator = stmt @goto enter_check @@ -139,7 +140,7 @@ function verify_ir(ir::IRCode) # We allow invalid IR in dead code to avoid passes having to detect when # they're generating dead code. bb_unreachable(domtree, bb) && continue - stmt = ir.stmts[idx] + stmt = ir.stmts[idx][:inst] stmt === nothing && continue if isa(stmt, PhiNode) @assert length(stmt.edges) == length(stmt.values) @@ -154,13 +155,13 @@ function verify_ir(ir::IRCode) edge == 0 && continue isassigned(stmt.values, i) || continue val = stmt.values[i] - phiT = ir.types[idx] + phiT = ir.stmts[idx][:type] if isa(val, SSAValue) if !(types(ir)[val] ⊑ phiT) #@verify_error """ # PhiNode $idx, has operand $(val.id), whose type is not a sub lattice element. # PhiNode type was $phiT - # Value type was $(ir.types[val.id]) + # Value type was $(ir.stmts[val.id][:type]) #""" #error() end @@ -185,7 +186,7 @@ function verify_ir(ir::IRCode) else if isa(stmt, Expr) || isa(stmt, ReturnNode) # TODO: make sure everything has line info if !(stmt isa ReturnNode && !isdefined(stmt, :val)) # not actually a return node, but an unreachable marker - if ir.lines[idx] <= 0 + if ir.stmts[idx][:line] <= 0 #@verify_error "Missing line number information for statement $idx of $ir" end end diff --git a/base/show.jl b/base/show.jl index e5aae45fcab4c..300acd186e14b 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1894,6 +1894,10 @@ module IRShow Base.size(r::Compiler.StmtRange) = Compiler.size(r) Base.first(r::Compiler.StmtRange) = Compiler.first(r) Base.last(r::Compiler.StmtRange) = Compiler.last(r) + Base.length(is::Compiler.InstructionStream) = Compiler.length(is) + Base.iterate(is::Compiler.InstructionStream, st::Int=1) = (st <= Compiler.length(is)) ? (is[st], st + 1) : nothing + Base.getindex(is::Compiler.InstructionStream, idx::Int) = Compiler.getindex(is, idx) + Base.getindex(node::Compiler.Instruction, fld::Symbol) = Compiler.getindex(node, fld) include("compiler/ssair/show.jl") const __debuginfo = Dict{Symbol, Any}( diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 042d91d2ff700..9e252ad0230c8 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -37,7 +37,8 @@ let m = Meta.@lower 1 + 1 domtree = Core.Compiler.construct_domtree(ir.cfg) ir = Core.Compiler.domsort_ssa!(ir, domtree) Core.Compiler.verify_ir(ir) - @test isa(ir.stmts[3], Core.PhiNode) && length(ir.stmts[3].edges) == 1 + phi = ir.stmts.inst[3] + @test isa(phi, Core.PhiNode) && length(phi.edges) == 1 end # test that we don't stack-overflow in SNCA with large functions. @@ -224,7 +225,7 @@ let m = Meta.@lower 1 + 1 ir = Core.Compiler.cfg_simplify!(ir) Core.Compiler.verify_ir(ir) ir = Core.Compiler.compact!(ir) - @test length(ir.cfg.blocks) == 1 && length(ir.stmts) == 1 + @test length(ir.cfg.blocks) == 1 && Core.Compiler.length(ir.stmts) == 1 end let m = Meta.@lower 1 + 1 @@ -252,7 +253,7 @@ let m = Meta.@lower 1 + 1 ir = Core.Compiler.cfg_simplify!(ir) Core.Compiler.verify_ir(ir) @test length(ir.cfg.blocks) == 5 - ret_2 = ir.stmts[ir.cfg.blocks[3].stmts[end]] + ret_2 = ir.stmts.inst[ir.cfg.blocks[3].stmts[end]] @test isa(ret_2, Core.Compiler.ReturnNode) && ret_2.val == 2 end diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 116e099398b7b..ca104f1d81f0a 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -63,11 +63,12 @@ let cfg = CFG(BasicBlock[ # the answer doesn't change (it does change the which node is chosen # as the semi-dominator, since it changes the DFS numbering). for (a, b, c, d) in Iterators.product(((true, false) for _ = 1:4)...) - let cfg′ = Compiler.copy(cfg) - a && reverse!(cfg′.blocks[1].succs) - b && reverse!(cfg′.blocks[2].succs) - c && reverse!(cfg′.blocks[4].preds) - d && reverse!(cfg′.blocks[5].preds) + let blocks = copy(cfg.blocks) + a && (blocks[1] = make_bb(blocks[1].preds, reverse(blocks[1].succs))) + b && (blocks[2] = make_bb(blocks[2].preds, reverse(blocks[2].succs))) + c && (blocks[4] = make_bb(reverse(blocks[4].preds), blocks[4].succs)) + d && (blocks[5] = make_bb(reverse(blocks[5].preds), blocks[5].succs)) + cfg′ = CFG(blocks, cfg.index) @test Compiler.SNCA(cfg′) == correct_idoms end end @@ -107,8 +108,8 @@ let cfg = CFG(BasicBlock[ make_bb([0, 1, 2] , [5] ), # 0 predecessor should be preserved make_bb([2, 3] , [] ), ], Int[]) - code = Compiler.IRCode( - [], [], Int32[], UInt8[], cfg, LineInfoNode[], [], [], []) + insts = Compiler.InstructionStream([], [], Int32[], UInt8[]) + code = Compiler.IRCode(insts, cfg, LineInfoNode[], [], [], []) compact = Compiler.IncrementalCompact(code, true) @test length(compact.result_bbs) == 4 && 0 in compact.result_bbs[3].preds end diff --git a/test/show.jl b/test/show.jl index 4acf2e1d47458..fc186ec5081e5 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1778,8 +1778,8 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1] @test isempty(pop!(lines1)) Core.Compiler.insert_node!(ir, 1, Val{1}, QuoteNode(1), false) Core.Compiler.insert_node!(ir, 1, Val{2}, QuoteNode(2), true) - Core.Compiler.insert_node!(ir, length(ir.stmts), Val{3}, QuoteNode(3), false) - Core.Compiler.insert_node!(ir, length(ir.stmts), Val{4}, QuoteNode(4), true) + Core.Compiler.insert_node!(ir, length(ir.stmts.inst), Val{3}, QuoteNode(3), false) + Core.Compiler.insert_node!(ir, length(ir.stmts.inst), Val{4}, QuoteNode(4), true) lines2 = split(repr(ir), '\n') @test isempty(pop!(lines2)) @test popfirst!(lines2) == "2 1 ── $(QuoteNode(1))" @@ -1806,7 +1806,7 @@ end # with as unnamed "!" BB. let src = code_typed(gcd, (Int, Int), debuginfo=:source)[1][1] ir = Core.Compiler.inflate_ir(src) - push!(ir.stmts, Core.Compiler.ReturnNode()) + push!(ir.stmts.inst, Core.Compiler.ReturnNode()) lines = split(sprint(show, ir), '\n') @test isempty(pop!(lines)) @test pop!(lines) == " ! ── unreachable::#UNDEF" From bf3ed5788eb9993db71798372128d47855d86715 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 16 Jun 2020 22:10:26 -0400 Subject: [PATCH 178/232] pty_{master,slave} -> pt{m,s} (#36315) We decided to get rid of the master/slave terminology in Julia in #30058. However, we decided to keep it for PTYs, where the terminology is imposed upon us by POSIX (and thus changing it would be confusing for people who might need to read up on the POSIX definitions of these concepts). It was recently suggested to revisit this, by renaming to ptm and pts instead, which also fit well (as the devies that the fds refer to are `/dev/ptmx` and `/dev/pts` respectively), are googleable (`pty pts` will yield the correct man page) and will probably need to remain even if POSIX adjusts their terminology, since they're part of the ABI (presumably they'll be backronymed to whatever terminology ends up winning). This patch does this rename. --- contrib/generate_precompile.jl | 24 +++++++++---------- stdlib/LibGit2/test/libgit2.jl | 18 +++++++-------- stdlib/REPL/test/repl.jl | 26 ++++++++++----------- test/testhelpers/FakePTYs.jl | 42 +++++++++++++++++----------------- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 36b1b567f15a7..001b7972d9b97 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -86,7 +86,7 @@ function generate_precompile_statements() mktemp() do precompile_file, precompile_file_h # Run a repl process and replay our script - pty_slave, pty_master = open_fake_pty() + pts, ptm = open_fake_pty() blackhole = Sys.isunix() ? "/dev/null" : "nul" if have_repl cmdargs = ```--color=yes @@ -104,25 +104,25 @@ function generate_precompile_statements() --cpu-target=native --startup-file=no --color=yes -e 'import REPL; REPL.Terminals.is_precompiling[] = true' -i $cmdargs```, - pty_slave, pty_slave, pty_slave; wait=false) + pts, pts, pts; wait=false) end - Base.close_stdio(pty_slave) - # Prepare a background process to copy output from process until `pty_slave` is closed + Base.close_stdio(pts) + # Prepare a background process to copy output from process until `pts` is closed output_copy = Base.BufferStream() tee = @async try - while !eof(pty_master) - l = readavailable(pty_master) + while !eof(ptm) + l = readavailable(ptm) write(debug_output, l) Sys.iswindows() && (sleep(0.1); yield(); yield()) # workaround hang - probably a libuv issue? write(output_copy, l) end close(output_copy) - close(pty_master) + close(ptm) catch ex close(output_copy) - close(pty_master) + close(ptm) if !(ex isa Base.IOError && ex.code == Base.UV_EIO) - rethrow() # ignore EIO on pty_master after pty_slave dies + rethrow() # ignore EIO on ptm after pts dies end end # wait for the definitive prompt before start writing to the TTY @@ -141,7 +141,7 @@ function generate_precompile_statements() bytesavailable(output_copy) > 0 && readavailable(output_copy) # push our input write(debug_output, "\n#### inputting statement: ####\n$(repr(l))\n####\n") - write(pty_master, l, "\n") + write(ptm, l, "\n") readuntil(output_copy, "\n") # wait for the next prompt-like to appear # NOTE: this is rather innaccurate because the Pkg REPL mode is a special flower @@ -150,10 +150,10 @@ function generate_precompile_statements() end println() end - write(pty_master, "exit()\n") + write(ptm, "exit()\n") wait(tee) success(p) || Base.pipeline_error(p) - close(pty_master) + close(ptm) write(debug_output, "\n#### FINISHED ####\n") # Extract the precompile statements from the precompile file diff --git a/stdlib/LibGit2/test/libgit2.jl b/stdlib/LibGit2/test/libgit2.jl index 65b8f6b9e26c2..0078d18979745 100644 --- a/stdlib/LibGit2/test/libgit2.jl +++ b/stdlib/LibGit2/test/libgit2.jl @@ -47,9 +47,9 @@ function challenge_prompt(cmd::Cmd, challenges; timeout::Integer=60, debug::Bool return "Process output found:\n\"\"\"\n$str\n\"\"\"" end out = IOBuffer() - with_fake_pty() do pty_slave, pty_master - p = run(detach(cmd), pty_slave, pty_slave, pty_slave, wait=false) - Base.close_stdio(pty_slave) + with_fake_pty() do pts, ptm + p = run(detach(cmd), pts, pts, pts, wait=false) + Base.close_stdio(pts) # Kill the process if it takes too long. Typically occurs when process is waiting # for input. @@ -79,17 +79,17 @@ function challenge_prompt(cmd::Cmd, challenges; timeout::Integer=60, debug::Bool end for (challenge, response) in challenges - write(out, readuntil(pty_master, challenge, keep=true)) - if !isopen(pty_master) + write(out, readuntil(ptm, challenge, keep=true)) + if !isopen(ptm) error("Could not locate challenge: \"$challenge\". ", format_output(out)) end - write(pty_master, response) + write(ptm, response) end - # Capture output from process until `pty_slave` is closed + # Capture output from process until `pts` is closed try - write(out, pty_master) + write(out, ptm) catch ex if !(ex isa Base.IOError && ex.code == Base.UV_EIO) rethrow() # ignore EIO from master after slave dies @@ -97,7 +97,7 @@ function challenge_prompt(cmd::Cmd, challenges; timeout::Integer=60, debug::Bool end status = fetch(timer) - close(pty_master) + close(ptm) if status != :success if status == :timeout error("Process timed out possibly waiting for a response. ", diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 57b0be33c823f..d785a8f9c2428 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -768,43 +768,43 @@ Base.exit_on_sigint(true) let exename = Base.julia_cmd() # Test REPL in dumb mode - with_fake_pty() do pty_slave, pty_master + with_fake_pty() do pts, ptm nENV = copy(ENV) nENV["TERM"] = "dumb" - p = run(detach(setenv(`$exename --startup-file=no -q`, nENV)), pty_slave, pty_slave, pty_slave, wait=false) - Base.close_stdio(pty_slave) - output = readuntil(pty_master, "julia> ", keep=true) + p = run(detach(setenv(`$exename --startup-file=no -q`, nENV)), pts, pts, pts, wait=false) + Base.close_stdio(pts) + output = readuntil(ptm, "julia> ", keep=true) if ccall(:jl_running_on_valgrind, Cint,()) == 0 # If --trace-children=yes is passed to valgrind, we will get a # valgrind banner here, not just the prompt. @test output == "julia> " end - write(pty_master, "1\nexit()\n") + write(ptm, "1\nexit()\n") - output = readuntil(pty_master, ' ', keep=true) + output = readuntil(ptm, ' ', keep=true) if Sys.iswindows() # Our fake pty is actually a pipe, and thus lacks the input echo feature of posix @test output == "1\n\njulia> " else @test output == "1\r\nexit()\r\n1\r\n\r\njulia> " end - @test bytesavailable(pty_master) == 0 + @test bytesavailable(ptm) == 0 @test if Sys.iswindows() || Sys.isbsd() - eof(pty_master) + eof(ptm) else # Some platforms (such as linux) report EIO instead of EOF # possibly consume child-exited notification # for example, see discussion in https://bugs.python.org/issue5380 try - eof(pty_master) && !Sys.islinux() + eof(ptm) && !Sys.islinux() catch ex (ex isa Base.IOError && ex.code == Base.UV_EIO) || rethrow() - @test_throws ex eof(pty_master) # make sure the error is sticky - pty_master.readerror = nothing - eof(pty_master) + @test_throws ex eof(ptm) # make sure the error is sticky + ptm.readerror = nothing + eof(ptm) end end - @test read(pty_master, String) == "" + @test read(ptm, String) == "" wait(p) end diff --git a/test/testhelpers/FakePTYs.jl b/test/testhelpers/FakePTYs.jl index a79f64d25c2bc..f659c59ca22ca 100644 --- a/test/testhelpers/FakePTYs.jl +++ b/test/testhelpers/FakePTYs.jl @@ -16,25 +16,25 @@ function open_fake_pty() pid = string(getpid(), base=16, pad=16) pipename = """\\\\?\\pipe\\cygwin-$pid-pty10-abcdefg""" server = listen(pipename) - pty_slave = connect(pipename) - @assert ccall(:jl_ispty, Cint, (Ptr{Cvoid},), pty_slave.handle) == 1 - pty_master = accept(server) + pts = connect(pipename) + @assert ccall(:jl_ispty, Cint, (Ptr{Cvoid},), pts.handle) == 1 + ptm = accept(server) close(server) # extract just the file descriptor - fds = Libc.dup(Base._fd(pty_slave)) - close(pty_slave) - pty_slave = fds - # convert pty_slave handle to a TTY - #fds = pty_slave.handle - #pty_slave.status = Base.StatusClosed - #pty_slave.handle = C_NULL - #pty_slave = Base.TTY(fds, Base.StatusOpen) + fds = Libc.dup(Base._fd(pts)) + close(pts) + pts = fds + # convert pts handle to a TTY + #fds = pts.handle + #pts.status = Base.StatusClosed + #pts.handle = C_NULL + #pts = Base.TTY(fds, Base.StatusOpen) else O_RDWR = Base.Filesystem.JL_O_RDWR O_NOCTTY = Base.Filesystem.JL_O_NOCTTY fdm = ccall(:posix_openpt, Cint, (Cint,), O_RDWR | O_NOCTTY) - fdm == -1 && error("Failed to open pty_master") + fdm == -1 && error("Failed to open ptm") rc = ccall(:grantpt, Cint, (Cint,), fdm) rc != 0 && error("grantpt failed") rc = ccall(:unlockpt, Cint, (Cint,), fdm) @@ -43,21 +43,21 @@ function open_fake_pty() fds = ccall(:open, Cint, (Ptr{UInt8}, Cint), ccall(:ptsname, Ptr{UInt8}, (Cint,), fdm), O_RDWR | O_NOCTTY) - pty_slave = RawFD(fds) - # pty_slave = fdio(fds, true) - # pty_slave = Base.Filesystem.File(RawFD(fds)) - # pty_slave = Base.TTY(RawFD(fds); readable = false) - pty_master = Base.TTY(RawFD(fdm)) + pts = RawFD(fds) + # pts = fdio(fds, true) + # pts = Base.Filesystem.File(RawFD(fds)) + # pts = Base.TTY(RawFD(fds); readable = false) + ptm = Base.TTY(RawFD(fdm)) end - return pty_slave, pty_master + return pts, ptm end function with_fake_pty(f) - pty_slave, pty_master = open_fake_pty() + pts, ptm = open_fake_pty() try - f(pty_slave, pty_master) + f(pts, ptm) finally - close(pty_master) + close(ptm) end nothing end From 7d9bfb73361def90f7b3ec58d68e9fe366548abb Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 11 Jun 2020 03:30:08 -0500 Subject: [PATCH 179/232] Formal specification of field types for abstract IO types Most concrete IO types declare that a subset of their fields are other abstract IO types. As a result, if `obj1::IO`, and `obj2 = obj1.fieldwithabstracttype`, then inference has no way of knowing what type `obj2.status` returns. When `obj2.status` is used as an argument for `Base.somefunction`, this leaves such code vulnerable to invalidation via package specializations of `Base.somefunction` even when in practice there is no risk that any IO code will call the new package-supplied method. This provides a more formal interface for three non-exported abstract types: `LibuvStream`, `LibuvServer`, and `AbstractPipe`. --- base/io.jl | 21 +++++++++++++++++- base/stream.jl | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/base/io.jl b/base/io.jl index 61c686a38529b..476af15e58368 100644 --- a/base/io.jl +++ b/base/io.jl @@ -333,8 +333,27 @@ function open(f::Function, args...; kwargs...) end end -# Generic wrappers around other IO objects +""" + AbstractPipe + +`AbstractPipe` is the abstract supertype for IO pipes that provide for communication between processes. + +If `pipe isa AbstractPipe`, it must obey the following interface: + +- `pipe.in` or `pipe.in_stream`, if present, must be of type `IO` and be used to provide input to the pipe +- `pipe.out` or `pipe.out_stream`, if present, must be of type `IO` and be used for output from the pipe +- `pipe.err` or `pipe.err_stream`, if present, must be of type `IO` and be used for writing errors from the pipe +""" abstract type AbstractPipe <: IO end + +function getproperty(pipe::AbstractPipe, name::Symbol) + if name === :in || name === :in_stream || name === :out || name === :out_stream || + name === :err || name === :err_stream + return getfield(pipe, name)::IO + end + return getfield(pipe, name) +end + function pipe_reader end function pipe_writer end diff --git a/base/stream.jl b/base/stream.jl index d5f3b05fc0327..f824334fb1418 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -13,9 +13,67 @@ end ## types ## abstract type IOServer end +""" + LibuvServer + +An abstract type for IOServers handled by libuv. + +If `server isa LibuvServer`, it must obey the following interface: + +- `server.handle` must be a `Ptr{Cvoid}` +- `server.status` must be an `Int` +- `server.cond` must be a `GenericCondition` +""" abstract type LibuvServer <: IOServer end + +function getproperty(server::LibuvServer, name::Symbol) + if name === :handle + return getfield(server, :handle)::Ptr{Cvoid} + elseif name === :status + return getfield(server, :status)::Int + elseif name === :cond + return getfield(server, :cond)::GenericCondition + else + return getfield(server, name) + end +end + +""" + LibuvStream + +An abstract type for IO streams handled by libuv. + +If`stream isa LibuvStream`, it must obey the following interface: + +- `stream.handle`, if present, must be a `Ptr{Cvoid}` +- `stream.status`, if present, must be an `Int` +- `stream.buffer`, if present, must be an `IOBuffer` +- `stream.sendbuf`, if present, must be a `Union{Nothing,IOBuffer}` +- `stream.cond`, if present, must be a `GenericCondition` +- `stream.lock`, if present, must be an `AbstractLock` +- `stream.throttle`, if present, must be an `Int` +""" abstract type LibuvStream <: IO end +function getproperty(stream::LibuvStream, name::Symbol) + if name === :handle + return getfield(stream, :handle)::Ptr{Cvoid} + elseif name === :status + return getfield(stream, :status)::Int + elseif name === :buffer + return getfield(stream, :buffer)::IOBuffer + elseif name === :sendbuf + return getfield(stream, :sendbuf)::Union{Nothing,IOBuffer} + elseif name === :cond + return getfield(stream, :cond)::GenericCondition + elseif name === :lock + return getfield(stream, :lock)::AbstractLock + elseif name === :throttle + return getfield(stream, :throttle)::Int + else + return getfield(stream, name) + end +end # IO # +- GenericIOBuffer{T<:AbstractArray{UInt8,1}} (not exported) From 6a69baad9370f3ebe3eb88a5ad642b3631c34294 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 11 Jun 2020 03:35:53 -0500 Subject: [PATCH 180/232] Remove obsolete field type assertions --- base/stream.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/stream.jl b/base/stream.jl index f824334fb1418..6e9ef071b7f50 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -378,7 +378,7 @@ function isopen(x::Union{LibuvStream, LibuvServer}) if x.status == StatusUninit || x.status == StatusInit throw(ArgumentError("$x is not initialized")) end - return x.status::Int != StatusClosed && x.status::Int != StatusEOF + return x.status != StatusClosed && x.status != StatusEOF end function check_open(x::Union{LibuvStream, LibuvServer}) @@ -453,7 +453,7 @@ function close(stream::Union{LibuvStream, LibuvServer}) stream.status = StatusClosing elseif isopen(stream) || stream.status == StatusEOF should_wait = uv_handle_data(stream) != C_NULL - if stream.status::Int != StatusClosing + if stream.status != StatusClosing ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) stream.status = StatusClosing end @@ -468,7 +468,7 @@ function uvfinalize(uv::Union{LibuvStream, LibuvServer}) iolock_begin() if uv.handle != C_NULL disassociate_julia_struct(uv.handle) # not going to call the usual close hooks - if uv.status::Int != StatusUninit + if uv.status != StatusUninit close(uv) else Libc.free(uv.handle) @@ -582,7 +582,7 @@ function uv_alloc_buf(handle::Ptr{Cvoid}, size::Csize_t, buf::Ptr{Cvoid}) stream = unsafe_pointer_to_objref(hd)::LibuvStream local data::Ptr{Cvoid}, newsize::Csize_t - if stream.status::Int != StatusActive + if stream.status != StatusActive data = C_NULL newsize = 0 else @@ -615,7 +615,7 @@ function uv_readcb(handle::Ptr{Cvoid}, nread::Cssize_t, buf::Ptr{Cvoid}) if isa(stream, TTY) stream.status = StatusEOF # libuv called uv_stop_reading already notify(stream.cond) - elseif stream.status::Int != StatusClosing + elseif stream.status != StatusClosing # begin shutdown of the stream ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) stream.status = StatusClosing @@ -725,7 +725,7 @@ show(io::IO, stream::Pipe) = print(io, function open_pipe!(p::PipeEndpoint, handle::OS_HANDLE) iolock_begin() - if p.status::Int != StatusInit + if p.status != StatusInit error("pipe is already in use or has been closed") end err = ccall(:uv_pipe_open, Int32, (Ptr{Cvoid}, OS_HANDLE), p.handle, handle) @@ -1119,7 +1119,7 @@ _fd(x::Union{OS_HANDLE, RawFD}) = x function _fd(x::Union{LibuvStream, LibuvServer}) fd = Ref{OS_HANDLE}(INVALID_OS_HANDLE) - if x.status::Int != StatusUninit && x.status::Int != StatusClosed + if x.status != StatusUninit && x.status != StatusClosed err = ccall(:uv_fileno, Int32, (Ptr{Cvoid}, Ptr{OS_HANDLE}), x.handle, fd) # handle errors by returning INVALID_OS_HANDLE end From 44f88a87be34e928528bb9ba0ee6a3f33f4edc79 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 17 Jun 2020 03:44:39 -0500 Subject: [PATCH 181/232] Assert type for `displaysize` --- base/show.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/show.jl b/base/show.jl index 300acd186e14b..e77e2b9735317 100644 --- a/base/show.jl +++ b/base/show.jl @@ -340,7 +340,7 @@ getindex(io::IO, key) = throw(KeyError(key)) get(io::IOContext, key, default) = get(io.dict, key, default) get(io::IO, key, default) = default -displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displaysize(io.io) +displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io) show_circular(io::IO, @nospecialize(x)) = false function show_circular(io::IOContext, @nospecialize(x)) From b4c570d5d0003de6e0d1b4022f83303088926694 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 10 Jun 2020 10:30:14 -0400 Subject: [PATCH 182/232] codegen: remove the shared "shadow_module" state --- src/aotcompile.cpp | 19 +++---- src/ccall.cpp | 139 +++++++++------------------------------------ src/cgutils.cpp | 62 +++++++++----------- src/codegen.cpp | 49 ++++++++-------- src/jitlayers.cpp | 65 +++++++++++---------- src/jitlayers.h | 53 +++++++++++------ 6 files changed, 154 insertions(+), 233 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 1cfe0772856ec..65be12c6bbb3c 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -294,6 +294,7 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams, int _p JL_LOCK(&codegen_lock); CompilationPolicy policy = (CompilationPolicy) _policy; + std::unique_ptr clone(jl_create_llvm_module("text")); // compile all methods for the current world and type-inference world size_t compile_for[] = { jl_typeinf_world, jl_world_counter }; @@ -311,7 +312,7 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams, int _p jl_value_t *item = jl_array_ptr_ref(methods, i); if (jl_is_simplevector(item)) { if (worlds == 1) - jl_compile_extern_c(shadow_output, ¶ms, NULL, jl_svecref(item, 0), jl_svecref(item, 1)); + jl_compile_extern_c(clone.get(), ¶ms, NULL, jl_svecref(item, 0), jl_svecref(item, 1)); continue; } mi = (jl_method_instance_t*)item; @@ -348,8 +349,6 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams, int _p // clones the contents of the module `m` to the shadow_output collector // while examining and recording what kind of function pointer we have - ValueToValueMapTy VMap; - std::unique_ptr clone(CloneModule(*shadow_output, VMap)); for (auto &def : emitted) { jl_merge_module(clone.get(), std::move(std::get<0>(def.second))); jl_code_instance_t *this_code = def.first; @@ -374,6 +373,11 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams, int _p } data->jl_fvar_map[this_code] = std::make_tuple(func_id, cfunc_id); } + if (params._shared_module) { + std::unique_ptr shared(params._shared_module); + params._shared_module = NULL; + jl_merge_module(clone.get(), std::move(shared)); + } // now get references to the globals in the merged module // and set them to be internalized and initialized at startup @@ -598,15 +602,6 @@ void jl_dump_native(void *native_code, delete data; } -// clones the contents of the module `m` to the shadow_output collector -// TODO: this should be deprecated -void jl_add_to_shadow(Module *m) -{ - ValueToValueMapTy VMap; - std::unique_ptr clone(CloneModule(*m, VMap)); - jl_merge_module(shadow_output, std::move(clone)); -} - void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM) { diff --git a/src/ccall.cpp b/src/ccall.cpp index 31cfedb9bd03c..634c733bf4a4c 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -3,93 +3,42 @@ // --- the ccall, cglobal, and llvm intrinsics --- #include "llvm/Support/Path.h" // for llvm::sys::path -// Map from symbol name (in a certain library) to its GV in sysimg and the -// DL handle address in the current session. -typedef StringMap SymMapGV; -static StringMap> libMapGV; -#ifdef _OS_WINDOWS_ -static SymMapGV symMapExe; -static SymMapGV symMapDl; -#endif -static SymMapGV symMapDefault; - -template -struct LazyModule { - Func func; - Module *m; - template - LazyModule(Func2 &&func) - : func(std::forward(func)), - m(nullptr) - {} - Module *get() - { - if (!m) - m = func(); - return m; - } - Module &operator*() - { - return *get(); - } -}; - -template -static LazyModule::type> -lazyModule(Func &&func) -{ - return LazyModule::type>( - std::forward(func)); -} - - -void copy_to_shadow(GlobalVariable *gv) -{ - // hack: make a copy of all globals in the shadow_output - if (!imaging_mode) - return; - GlobalVariable *shadowvar = global_proto(gv, shadow_output); - shadowvar->setInitializer(gv->getInitializer()); - shadowvar->setLinkage(GlobalVariable::InternalLinkage); -} - // Find or create the GVs for the library and symbol lookup. // Return `runtime_lib` (whether the library name is a string) // The `lib` and `sym` GV returned may not be in the current module. -template -static bool runtime_sym_gvs(const char *f_lib, const char *f_name, MT &&M, +static bool runtime_sym_gvs(jl_codegen_params_t &emission_context, const char *f_lib, const char *f_name, GlobalVariable *&lib, GlobalVariable *&sym) { + Module *M = emission_context.shared_module(jl_LLVMContext); bool runtime_lib = false; GlobalVariable *libptrgv; - SymMapGV *symMap; + jl_codegen_params_t::SymMapGV *symMap; #ifdef _OS_WINDOWS_ if ((intptr_t)f_lib == 1) { libptrgv = jlexe_var; - symMap = &symMapExe; + symMap = &emission_context.symMapExe; } else if ((intptr_t)f_lib == 2) { libptrgv = jldll_var; - symMap = &symMapDl; + symMap = &emission_context.symMapDl; } else #endif if (f_lib == NULL) { libptrgv = jlRTLD_DEFAULT_var; - symMap = &symMapDefault; + symMap = &emission_context.symMapDefault; } else { std::string name = "ccalllib_"; name += llvm::sys::path::filename(f_lib); name += std::to_string(globalUnique++); runtime_lib = true; - auto &libgv = libMapGV[f_lib]; + auto &libgv = emission_context.libMapGV[f_lib]; if (libgv.first == NULL) { libptrgv = new GlobalVariable(*M, T_pint8, false, GlobalVariable::ExternalLinkage, Constant::getNullValue(T_pint8), name); - copy_to_shadow(libptrgv); - libgv.first = global_proto(libptrgv); + libgv.first = libptrgv; } else { libptrgv = libgv.first; @@ -108,8 +57,6 @@ static bool runtime_sym_gvs(const char *f_lib, const char *f_name, MT &&M, llvmgv = new GlobalVariable(*M, T_pvoidfunc, false, GlobalVariable::ExternalLinkage, Constant::getNullValue(T_pvoidfunc), name); - copy_to_shadow(llvmgv); - llvmgv = global_proto(llvmgv); } lib = libptrgv; @@ -118,6 +65,7 @@ static bool runtime_sym_gvs(const char *f_lib, const char *f_name, MT &&M, } static Value *runtime_sym_lookup( + jl_codegen_params_t &emission_context, IRBuilder<> &irbuilder, PointerType *funcptype, const char *f_lib, const char *f_name, Function *f, @@ -154,14 +102,14 @@ static Value *runtime_sym_lookup( irbuilder.SetInsertPoint(dlsym_lookup); Value *libname; if (runtime_lib) { - libname = stringConstPtr(irbuilder, f_lib); + libname = stringConstPtr(emission_context, irbuilder, f_lib); } else { // f_lib is actually one of the special sentinel values libname = ConstantExpr::getIntToPtr(ConstantInt::get(T_size, (uintptr_t)f_lib), T_pint8); } Value *llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlsym_func), - { libname, stringConstPtr(irbuilder, f_name), libptrgv }); + { libname, stringConstPtr(emission_context, irbuilder, f_name), libptrgv }); auto store = irbuilder.CreateAlignedStore(llvmf, llvmgv, sizeof(void*)); store->setAtomic(AtomicOrdering::Release); irbuilder.CreateBr(ccall_bb); @@ -181,35 +129,22 @@ static Value *runtime_sym_lookup( { GlobalVariable *libptrgv; GlobalVariable *llvmgv; - bool runtime_lib = runtime_sym_gvs(f_lib, f_name, f->getParent(), - libptrgv, llvmgv); - libptrgv = prepare_global(libptrgv); - llvmgv = prepare_global(llvmgv); - return runtime_sym_lookup(ctx.builder, funcptype, f_lib, f_name, f, libptrgv, llvmgv, - runtime_lib); + bool runtime_lib = runtime_sym_gvs(ctx.emission_context, f_lib, f_name, libptrgv, llvmgv); + libptrgv = prepare_global_in(jl_Module, libptrgv); + llvmgv = prepare_global_in(jl_Module, llvmgv); + return runtime_sym_lookup(ctx.emission_context, ctx.builder, funcptype, f_lib, f_name, f, libptrgv, llvmgv, runtime_lib); } -// Map from distinct callee's to its GOT entry. -// In principle the attribute, function type and calling convention -// don't need to be part of the key but it seems impossible to forward -// all the arguments without writing assembly directly. -// This doesn't matter too much in reality since a single function is usually -// not called with multiple signatures. -static DenseMap,GlobalVariable*>> allPltMap; - -void jl_add_to_shadow(Module *m); - // Emit a "PLT" entry that will be lazily initialized // when being called the first time. static GlobalVariable *emit_plt_thunk( - Module *M, FunctionType *functype, - const AttributeList &attrs, + jl_codegen_params_t &emission_context, + FunctionType *functype, const AttributeList &attrs, CallingConv::ID cc, const char *f_lib, const char *f_name, GlobalVariable *libptrgv, GlobalVariable *llvmgv, bool runtime_lib) { + Module *M = emission_context.shared_module(jl_LLVMContext); PointerType *funcptype = PointerType::get(functype, 0); libptrgv = prepare_global_in(M, libptrgv); llvmgv = prepare_global_in(M, llvmgv); @@ -229,7 +164,7 @@ static GlobalVariable *emit_plt_thunk( ConstantExpr::getBitCast(plt, T_pvoidfunc), gname); BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", plt); IRBuilder<> irbuilder(b0); - Value *ptr = runtime_sym_lookup(irbuilder, funcptype, f_lib, f_name, plt, libptrgv, + Value *ptr = runtime_sym_lookup(emission_context, irbuilder, funcptype, f_lib, f_name, plt, libptrgv, llvmgv, runtime_lib); auto store = irbuilder.CreateAlignedStore(irbuilder.CreateBitCast(ptr, T_pvoidfunc), got, sizeof(void*)); store->setAtomic(AtomicOrdering::Release); @@ -265,23 +200,14 @@ static GlobalVariable *emit_plt_thunk( } irbuilder.ClearInsertionPoint(); - got = global_proto(got); // exchange got for the permanent global before jl_finalize_module destroys it - jl_add_to_shadow(M); - jl_finalize_module(std::unique_ptr(M)); - - auto shadowgot = - cast(shadow_output->getNamedValue(gname)); - auto shadowplt = cast(shadow_output->getNamedValue(fname)); - shadowgot->setInitializer(ConstantExpr::getBitCast(shadowplt, - T_pvoidfunc)); return got; } static Value *emit_plt( jl_codectx_t &ctx, FunctionType *functype, - const AttributeList &attrs, - CallingConv::ID cc, const char *f_lib, const char *f_name) + const AttributeList &attrs, + CallingConv::ID cc, const char *f_lib, const char *f_name) { assert(imaging_mode); // Don't do this for vararg functions so that the `musttail` is only @@ -289,26 +215,17 @@ static Value *emit_plt( assert(!functype->isVarArg()); GlobalVariable *libptrgv; GlobalVariable *llvmgv; - auto LM = lazyModule([&] { - Module *m = new Module(f_name, jl_LLVMContext); - jl_setup_module(m); - return m; - }); - bool runtime_lib = runtime_sym_gvs(f_lib, f_name, LM, libptrgv, llvmgv); + bool runtime_lib = runtime_sym_gvs(ctx.emission_context, f_lib, f_name, libptrgv, llvmgv); PointerType *funcptype = PointerType::get(functype, 0); - auto &pltMap = allPltMap[attrs]; + auto &pltMap = ctx.emission_context.allPltMap[attrs]; auto key = std::make_tuple(llvmgv, functype, cc); - GlobalVariable *&shadowgot = pltMap[key]; - if (!shadowgot) { - shadowgot = emit_plt_thunk(LM.get(), functype, attrs, cc, f_lib, f_name, libptrgv, llvmgv, runtime_lib); - } - else { - // `runtime_sym_gvs` shouldn't have created anything in a new module - // if it returns a GV that already exists. - assert(!LM.m); + GlobalVariable *&sharedgot = pltMap[key]; + if (!sharedgot) { + sharedgot = emit_plt_thunk(ctx.emission_context, + functype, attrs, cc, f_lib, f_name, libptrgv, llvmgv, runtime_lib); } - GlobalVariable *got = prepare_global(shadowgot); + GlobalVariable *got = prepare_global_in(jl_Module, sharedgot); LoadInst *got_val = ctx.builder.CreateAlignedLoad(got, sizeof(void*)); // See comment in `runtime_sym_lookup` above. This in principle needs a // consume ordering too. This is even less likely to cause issues though diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 8cdaf9c5bd2f4..abcd5cbb40e20 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -137,40 +137,30 @@ static inline void _hook_call(jl_value_t *hook, std::array args) // --- string constants --- -static StringMap stringConstants; -static Value *stringConstPtr(IRBuilder<> &irbuilder, const std::string &txt) +static Value *stringConstPtr( + jl_codegen_params_t &emission_context, + IRBuilder<> &irbuilder, + const std::string &txt) { - StringRef ctxt(txt.c_str(), strlen(txt.c_str()) + 1); + StringRef ctxt(txt.c_str(), txt.size() + 1); StringMap::iterator pooledval = - stringConstants.insert(std::pair(ctxt, NULL)).first; - StringRef pooledtxt = pooledval->getKey(); - if (imaging_mode) { - if (pooledval->second == NULL) { - static int strno = 0; - std::stringstream ssno; - ssno << "_j_str" << strno++; - GlobalVariable *gv = get_pointer_to_constant( - ConstantDataArray::get(jl_LLVMContext, - ArrayRef( - (const unsigned char*)pooledtxt.data(), - pooledtxt.size())), - ssno.str(), - *shadow_output); - pooledval->second = gv; - jl_ExecutionEngine->addGlobalMapping(gv, (void*)(uintptr_t)pooledtxt.data()); - } - - GlobalVariable *v = prepare_global_in(jl_builderModule(irbuilder), pooledval->second); - Value *zero = ConstantInt::get(Type::getInt32Ty(jl_LLVMContext), 0); - Value *Args[] = { zero, zero }; - return irbuilder.CreateInBoundsGEP(v->getValueType(), v, Args); - } - else { - Value *v = ConstantExpr::getIntToPtr( - ConstantInt::get(T_size, (uintptr_t)pooledtxt.data()), - T_pint8); - return v; - } + emission_context.stringConstants.insert(std::pair(ctxt, NULL)).first; + Module *M = jl_builderModule(irbuilder); + if (pooledval->second == NULL) { + static int strno = 0; + std::stringstream ssno; + ssno << "_j_str" << strno++; + GlobalVariable *gv = get_pointer_to_constant( + ConstantDataArray::get(jl_LLVMContext, ArrayRef(ctxt.data(), ctxt.size())), + ssno.str(), *M); + pooledval->second = gv; + } + GlobalVariable *v = prepare_global_in(M, pooledval->second); + if (v != pooledval->second) + v->setInitializer(pooledval->second->getInitializer()); + Value *zero = ConstantInt::get(Type::getInt32Ty(jl_LLVMContext), 0); + Value *Args[] = { zero, zero }; + return irbuilder.CreateInBoundsGEP(v->getValueType(), v, Args); } // --- MDNode --- @@ -356,8 +346,8 @@ static Value *literal_pointer_val_slot(jl_codectx_t &ctx, jl_value_t *p) return gv; } if (GlobalVariable *gv = julia_const_gv(p)) { - // if this is a known object, use the existing GlobalValue - return prepare_global(gv); + // if this is a known special object, use the existing GlobalValue + return prepare_global_in(jl_Module, gv); } if (jl_is_datatype(p)) { jl_datatype_t *addr = (jl_datatype_t*)p; @@ -1091,7 +1081,7 @@ static Value *emit_datatype_name(jl_codectx_t &ctx, Value *dt) static void just_emit_error(jl_codectx_t &ctx, const std::string &txt) { - ctx.builder.CreateCall(prepare_call(jlerror_func), stringConstPtr(ctx.builder, txt)); + ctx.builder.CreateCall(prepare_call(jlerror_func), stringConstPtr(ctx.emission_context, ctx.builder, txt)); } static void emit_error(jl_codectx_t &ctx, const std::string &txt) @@ -1154,7 +1144,7 @@ static void null_pointer_check(jl_codectx_t &ctx, Value *v) static void emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, const std::string &msg) { - Value *msg_val = stringConstPtr(ctx.builder, msg); + Value *msg_val = stringConstPtr(ctx.emission_context, ctx.builder, msg); ctx.builder.CreateCall(prepare_call(jltypeerror_func), { msg_val, maybe_decay_untracked(type), mark_callee_rooted(boxed(ctx, x))}); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 58aee11558dc7..13e8db412cac5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -153,7 +153,6 @@ bool imaging_mode = false; // shared llvm state JL_DLLEXPORT LLVMContext &jl_LLVMContext = *(new LLVMContext()); TargetMachine *jl_TargetMachine; -Module *shadow_output; static DataLayout &jl_data_layout = *(new DataLayout("")); #define jl_Module ctx.f->getParent() #define jl_builderModule(builder) (builder).GetInsertBlock()->getParent()->getParent() @@ -606,7 +605,6 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); static GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G); -#define prepare_global(G) prepare_global_in(jl_Module, (G)) static Instruction *tbaa_decorate(MDNode *md, Instruction *load_or_store); // --- convenience functions for tagging llvm values with julia types --- @@ -617,7 +615,7 @@ static GlobalVariable *get_pointer_to_constant(Constant *val, StringRef name, Mo M, val->getType(), true, - GlobalVariable::PrivateLinkage, + GlobalVariable::ExternalLinkage, val, name); gv->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); @@ -1098,6 +1096,13 @@ static void jl_setup_module(Module *m, const jl_cgparams_t *params = &jl_default m->setTargetTriple(jl_TargetMachine->getTargetTriple().str()); } +Module *jl_create_llvm_module(StringRef name) +{ + Module *M = new Module(name, jl_LLVMContext); + jl_setup_module(M); + return M; +} + static void jl_init_function(Function *F) { // set any attributes that *must* be set on all functions @@ -3542,7 +3547,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) else { if (!jl_is_method(ctx.linfo->def.method)) { // TODO: inference is invalid if this has an effect - Value *world = ctx.builder.CreateLoad(prepare_global(jlgetworld_global)); + Value *world = ctx.builder.CreateLoad(prepare_global_in(jl_Module, jlgetworld_global)); ctx.builder.CreateStore(world, ctx.world_age_field); } assert(ssaval_result != -1); @@ -4024,6 +4029,7 @@ static Function* gen_cfun_wrapper( jl_unionall_t *unionall_env, jl_svec_t *sparam_vals, jl_array_t **closure_types) { // Generate a c-callable wrapper + assert(into); size_t nargs = sig.nccallargs; const char *name = "cfunction"; size_t world = jl_world_counter; @@ -4067,10 +4073,6 @@ static Function* gen_cfun_wrapper( funcName << "jlcapi_" << name << "_" << globalUnique++; Module *M = into; - if (!M) { - M = new Module(name, jl_LLVMContext); - jl_setup_module(M); - } AttributeList attributes = sig.attributes; FunctionType *functype; if (nest) { @@ -4089,7 +4091,6 @@ static Function* gen_cfun_wrapper( funcName.str(), M); cw->setAttributes(attributes); jl_init_function(cw); - Function *cw_proto = into ? cw : function_proto(cw); jl_codectx_t ctx(jl_LLVMContext, params); ctx.f = cw; @@ -4113,7 +4114,7 @@ static Function* gen_cfun_wrapper( Value *valid_tls = ctx.builder.CreateIsNotNull(last_age); have_tls = ctx.builder.CreateAnd(have_tls, valid_tls); ctx.world_age_field = ctx.builder.CreateSelect(valid_tls, ctx.world_age_field, dummy_world); - Value *world_v = ctx.builder.CreateLoad(prepare_global(jlgetworld_global)); + Value *world_v = ctx.builder.CreateLoad(prepare_global_in(jl_Module, jlgetworld_global)); Value *age_ok = NULL; if (calltype) { @@ -4482,6 +4483,11 @@ static Function* gen_cfun_wrapper( ctx.builder.SetCurrentDebugLocation(noDbg); ctx.builder.ClearInsertionPoint(); + if (aliasname) { + GlobalAlias::create(cw->getType()->getElementType(), cw->getType()->getAddressSpace(), + GlobalValue::ExternalLinkage, aliasname, cw, M); + } + if (nest) { funcName << "make"; Function *cw_make = Function::Create( @@ -4502,17 +4508,10 @@ static Function* gen_cfun_wrapper( cwbuilder.CreateBitCast(NVal, T_pint8) }); cwbuilder.CreateRet(cwbuilder.CreateCall(adjust_trampoline, { Tramp })); - cw_proto = into ? cw_make : function_proto(cw_make); - } - - if (aliasname) { - GlobalAlias::create(cw->getType()->getElementType(), cw->getType()->getAddressSpace(), - GlobalValue::ExternalLinkage, aliasname, cw, M); + cw = cw_make; } - if (!into) - jl_finalize_module(std::unique_ptr(M)); - return cw_proto; + return cw; } // Get the LLVM Function* for the C-callable entry point for a certain function @@ -4685,7 +4684,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con // do codegen to create a C-callable alias/wrapper, or if sysimg_handle is set, // restore one from a loaded system image. -Function *jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms) +void jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms) { jl_datatype_t *ft = (jl_datatype_t*)jl_tparam0(sigt); jl_value_t *ff = ft->instance; @@ -4715,24 +4714,23 @@ Function *jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t * size_t world = jl_world_counter; size_t min_valid = 0; size_t max_valid = ~(size_t)0; - Function *F = NULL; if (sysimg_handle) { // restore a ccallable from the system image void *addr; int found = jl_dlsym(sysimg_handle, name, &addr, 0); if (found) { FunctionType *ftype = sig.functype(); - F = Function::Create(ftype, GlobalVariable::ExternalLinkage, - name, shadow_output); + Function *F = Function::Create(ftype, GlobalVariable::ExternalLinkage, name); add_named_global(F, addr); + delete F; } } else { jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); - F = gen_cfun_wrapper((Module*)llvmmod, params, sig, ff, name, declrt, lam, NULL, NULL, NULL); + gen_cfun_wrapper((Module*)llvmmod, params, sig, ff, name, declrt, lam, NULL, NULL, NULL); } JL_GC_POP(); - return F; + return; } err = jl_get_exceptionf(jl_errorexception_type, "%s", sig.err_msg.c_str()); } @@ -7530,7 +7528,6 @@ extern "C" void jl_init_codegen(void) // Now that the execution engine exists, initialize all modules jl_init_jit(); Module *m = new Module("julia", jl_LLVMContext); - shadow_output = m; jl_setup_module(m); init_julia_llvm_env(m); diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 88ef1cc96e2d5..af8364af25844 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -42,7 +42,7 @@ void jl_dump_compiles(void *s) dump_compiles_stream = (JL_STREAM*)s; } -static void jl_add_to_ee(); +static void jl_add_to_ee(std::unique_ptr m); static void jl_add_to_ee(std::unique_ptr &M, StringMap*> &NewExports); static uint64_t getAddressForFunction(StringRef fname); @@ -62,6 +62,26 @@ void jl_jit_globals(std::map &globals) } } +// turn long strings into memoized copies, instead of making a copy per object file of output. +void jl_jit_strings(jl_codegen_params_t::SymMapGV &stringConstants) +{ + for (auto &it : stringConstants) { + GlobalVariable *GV = it.second; + Constant *CDA = GV->getInitializer(); + StringRef data = cast(CDA)->getRawDataValues(); + if (data.size() > 8) { // only for long strings: keep short ones as values + Type *T_size = Type::getIntNTy(GV->getContext(), sizeof(void*) * 8); + Constant *v = ConstantExpr::getIntToPtr( + ConstantInt::get(T_size, (uintptr_t)data.data()), + GV->getType()); + GV->replaceAllUsesWith(v); + GV->eraseFromParent(); + it.second = nullptr; + } + } +} + + // this generates llvm code for the lambda info // and adds the result to the jitlayers // (and the shadow module), @@ -99,7 +119,9 @@ static jl_callptr_t _jl_compile_codeinst( emitted[codeinst] = std::move(result); jl_compile_workqueue(emitted, params, CompilationPolicy::Default); - jl_add_to_ee(); + jl_jit_strings(params.stringConstants); + if (params._shared_module) + jl_add_to_ee(std::unique_ptr(params._shared_module)); StringMap*> NewExports; StringMap NewGlobals; for (auto &global : params.globals) { @@ -197,7 +219,7 @@ static jl_callptr_t _jl_compile_codeinst( return fptr; } -Function *jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); +void jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); // compile a C-callable alias extern "C" JL_DLLEXPORT @@ -208,14 +230,20 @@ void jl_compile_extern_c(void *llvmmod, void *p, void *sysimg, jl_value_t *declr jl_codegen_params_t *pparams = (jl_codegen_params_t*)p; if (pparams == NULL) pparams = ¶ms; - jl_generate_ccallable(llvmmod, sysimg, declrt, sigt, *pparams); + Module *into = (Module*)llvmmod; + if (into == NULL) + into = jl_create_llvm_module("cextern"); + jl_generate_ccallable(into, sysimg, declrt, sigt, *pparams); if (!sysimg) { if (p == NULL) { jl_jit_globals(params.globals); + jl_jit_strings(params.stringConstants); assert(params.workqueue.empty()); + if (params._shared_module) + jl_add_to_ee(std::unique_ptr(params._shared_module)); } if (llvmmod == NULL) - jl_add_to_ee(); + jl_add_to_ee(std::unique_ptr(into)); } JL_UNLOCK(&codegen_lock); } @@ -804,7 +832,8 @@ void jl_merge_module(Module *dest, std::unique_ptr src) continue; } else { - assert(dG->isDeclaration() || dG->getInitializer() == sG->getInitializer()); + assert(dG->isDeclaration() || (dG->getInitializer() == sG->getInitializer() && + dG->isConstant() && sG->isConstant())); dG->replaceAllUsesWith(sG); dG->eraseFromParent(); } @@ -870,11 +899,6 @@ void jl_merge_module(Module *dest, std::unique_ptr src) } } -// this is a unique_ptr, but we don't want -// C++ to attempt to run out finalizer on exit -// since it also owned by jl_LLVMContext -static Module *ready_to_emit; - static void jl_add_to_ee(std::unique_ptr m) { JL_TIMING(LLVM_EMIT); @@ -893,24 +917,6 @@ static void jl_add_to_ee(std::unique_ptr m) jl_ExecutionEngine->addModule(std::move(m)); } -static void jl_add_to_ee() -{ - std::unique_ptr m(ready_to_emit); - ready_to_emit = NULL; - if (m) - jl_add_to_ee(std::move(m)); -} - -// this passes ownership of a module to the JIT after code emission is complete -// TODO: this should be deprecated -void jl_finalize_module(std::unique_ptr m) -{ - if (ready_to_emit) - jl_merge_module(ready_to_emit, std::move(m)); - else - ready_to_emit = m.release(); -} - static int jl_add_to_ee( std::unique_ptr &M, StringMap*> &NewExports, @@ -989,7 +995,6 @@ void add_named_global(GlobalObject *gv, void *addr, bool dllimport) { #ifdef _OS_WINDOWS_ // setting JL_DLLEXPORT correctly only matters when building a binary - // (global_proto will strip this from the JIT) if (dllimport && imaging_mode) { assert(gv->getLinkage() == GlobalValue::ExternalLinkage); // add the __declspec(dllimport) attribute diff --git a/src/jitlayers.h b/src/jitlayers.h index c7f2dbbe90b26..ed957ad44b588 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -21,13 +21,13 @@ extern "C" { extern int globalUnique; } extern TargetMachine *jl_TargetMachine; -extern Module *shadow_output; extern bool imaging_mode; void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM); void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, bool lower_intrinsics=true, bool dump_native=false); void jl_finalize_module(std::unique_ptr m); void jl_merge_module(Module *dest, std::unique_ptr src); +Module *jl_create_llvm_module(StringRef name); typedef struct _jl_llvm_functions_t { std::string functionObject; // jlcall llvm Function name @@ -53,11 +53,36 @@ typedef std::vector, jl_llvm_functions_t> jl_compile_result_t; typedef struct { + typedef StringMap SymMapGV; // outputs jl_codegen_call_targets_t workqueue; std::map globals; std::map ditypes; std::map llvmtypes; + SymMapGV stringConstants; + // Map from symbol name (in a certain library) to its GV in sysimg and the + // DL handle address in the current session. + StringMap> libMapGV; +#ifdef _OS_WINDOWS_ + SymMapGV symMapExe; + SymMapGV symMapDl; +#endif + SymMapGV symMapDefault; + // Map from distinct callee's to its GOT entry. + // In principle the attribute, function type and calling convention + // don't need to be part of the key but it seems impossible to forward + // all the arguments without writing assembly directly. + // This doesn't matter too much in reality since a single function is usually + // not called with multiple signatures. + DenseMap, + GlobalVariable*>> allPltMap; + Module *_shared_module = NULL; + Module *shared_module(LLVMContext &context) { + if (!_shared_module) + _shared_module = jl_create_llvm_module("globals"); + return _shared_module; + } // inputs size_t world = 0; const jl_cgparams_t *params = &jl_default_cgparams; @@ -88,29 +113,21 @@ void jl_compile_workqueue( Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_tupletype_t *argt, jl_codegen_params_t ¶ms); -// Connect Modules via prototypes, each owned by module `M` -static inline GlobalVariable *global_proto(GlobalVariable *G, Module *M = NULL) -{ - // Copy the GlobalVariable, but without the initializer, so it becomes a declaration - GlobalVariable *proto = new GlobalVariable(G->getType()->getElementType(), - G->isConstant(), GlobalVariable::ExternalLinkage, - NULL, G->getName(), G->getThreadLocalMode()); - proto->copyAttributesFrom(G); - // DLLImport only needs to be set for the shadow module - // it just gets annoying in the JIT - proto->setDLLStorageClass(GlobalValue::DefaultStorageClass); - if (M) - M->getGlobalList().push_back(proto); - return proto; -} - static inline GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G) { if (G->getParent() == M) return G; GlobalValue *local = M->getNamedValue(G->getName()); if (!local) { - local = global_proto(G, M); + // Copy the GlobalVariable, but without the initializer, so it becomes a declaration + GlobalVariable *proto = new GlobalVariable(*M, G->getType()->getElementType(), + G->isConstant(), GlobalVariable::ExternalLinkage, + nullptr, G->getName(), nullptr, G->getThreadLocalMode()); + proto->copyAttributesFrom(G); + // DLLImport only needs to be set for the shadow module + // it just gets annoying in the JIT + proto->setDLLStorageClass(GlobalValue::DefaultStorageClass); + return proto; } return cast(local); } From 26de664edf16ea4587265ce71f4c30a995231d9a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 10 Jun 2020 17:35:17 -0400 Subject: [PATCH 183/232] codegen: use constant placeholder objects instead of realizing LLVM functions We already had prepare_global/prepare_call functions. This simply lets us use that to allocate these on-demand instead of keeping around a global copy of them to template from. --- src/anticodegen.c | 1 - src/aotcompile.cpp | 2 +- src/ccall.cpp | 64 ++- src/cgutils.cpp | 76 +-- src/codegen.cpp | 1307 +++++++++++++++++++++--------------------- src/intrinsics.cpp | 70 +-- src/jitlayers.cpp | 21 +- src/jitlayers.h | 30 +- src/julia_internal.h | 1 - src/staticdata.c | 1 - 10 files changed, 763 insertions(+), 810 deletions(-) diff --git a/src/anticodegen.c b/src/anticodegen.c index 1b157af267b1c..eb7f4be526175 100644 --- a/src/anticodegen.c +++ b/src/anticodegen.c @@ -27,7 +27,6 @@ JL_DLLEXPORT size_t jl_LLVMDisasmInstruction(void *DC, uint8_t *Bytes, uint64_t int32_t jl_assign_functionID(const char *fname) UNAVAILABLE void jl_init_codegen(void) { } -void jl_fptr_to_llvm(void *fptr, jl_method_instance_t *lam, int specsig) { } int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) { diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 65be12c6bbb3c..c2b0d64318b44 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -536,7 +536,7 @@ void jl_dump_native(void *native_code, // reflect the address of the jl_RTLD_DEFAULT_handle variable // back to the caller, so that we can check for consistency issues - GlobalValue *jlRTLD_DEFAULT_var = data->M->getNamedValue("jl_RTLD_DEFAULT_handle"); + GlobalValue *jlRTLD_DEFAULT_var = jl_emit_RTLD_DEFAULT_var(data->M.get()); addComdat(new GlobalVariable(*data->M, jlRTLD_DEFAULT_var->getType(), true, diff --git a/src/ccall.cpp b/src/ccall.cpp index 634c733bf4a4c..9170fcabfdb2d 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -3,6 +3,12 @@ // --- the ccall, cglobal, and llvm intrinsics --- #include "llvm/Support/Path.h" // for llvm::sys::path +// somewhat unusual variable, in that aotcompile wants to get the address of this for a sanity check +GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M) +{ + return prepare_global_in(M, jlRTLD_DEFAULT_var); +} + // Find or create the GVs for the library and symbol lookup. // Return `runtime_lib` (whether the library name is a string) // The `lib` and `sym` GV returned may not be in the current module. @@ -15,17 +21,17 @@ static bool runtime_sym_gvs(jl_codegen_params_t &emission_context, const char *f jl_codegen_params_t::SymMapGV *symMap; #ifdef _OS_WINDOWS_ if ((intptr_t)f_lib == 1) { - libptrgv = jlexe_var; + libptrgv = prepare_global_in(M, jlexe_var); symMap = &emission_context.symMapExe; } else if ((intptr_t)f_lib == 2) { - libptrgv = jldll_var; + libptrgv = prepare_global_in(M, jldll_var); symMap = &emission_context.symMapDl; } else #endif if (f_lib == NULL) { - libptrgv = jlRTLD_DEFAULT_var; + libptrgv = jl_emit_RTLD_DEFAULT_var(M); symMap = &emission_context.symMapDefault; } else { @@ -636,6 +642,39 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg return mark_julia_type(ctx, res, false, rt); } +static Function *llvmcall_proto(Function *F, Module *M = nullptr) +{ + // Copy the declaration characteristics of the Function (not the body) + Function *NewF = Function::Create(F->getFunctionType(), + Function::ExternalLinkage, + F->getName(), + M); + + // Declarations are not allowed to have personality routines, but + // copyAttributesFrom sets them anyway. Temporarily unset the personality + // routine from `F`, since copying it and then resetting is more expensive + // as well as introducing an extra use from this unowned function, which + // can cause crashes in the LLVMContext's global destructor. + llvm::Constant *OldPersonalityFn = nullptr; + if (F->hasPersonalityFn()) { + OldPersonalityFn = F->getPersonalityFn(); + F->setPersonalityFn(nullptr); + } + + // FunctionType does not include any attributes. Copy them over manually + // as codegen may make decisions based on the presence of certain attributes + NewF->copyAttributesFrom(F); + + if (OldPersonalityFn) + F->setPersonalityFn(OldPersonalityFn); + + // DLLImport only needs to be set for the shadow module + // it just gets annoying in the JIT + NewF->setDLLStorageClass(GlobalValue::DefaultStorageClass); + + return NewF; +} + class FunctionMover final : public ValueMaterializer { public: @@ -699,7 +738,7 @@ class FunctionMover final : public ValueMaterializer { Function *NewF = destModule->getFunction(F->getName()); if (!NewF) { - NewF = function_proto(F); + NewF = llvmcall_proto(F); NewF->setComdat(nullptr); destModule->getFunctionList().push_back(NewF); } @@ -776,6 +815,15 @@ class FunctionMover final : public ValueMaterializer }; }; +static Function *prepare_llvmcall(Module *M, Function *Callee) +{ + GlobalValue *local = M->getNamedValue(Callee->getName()); + if (!local) + local = llvmcall_proto(Callee, M); + return cast(local); +} + + // llvmcall(ir, (rettypes...), (argtypes...), args...) static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) { @@ -954,7 +1002,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar std::stringstream name; name << "jl_llvmcall" << llvmcallnumbering++; f->setName(name.str()); - f = cast(prepare_call(function_proto(f)).getCallee()); + f = prepare_llvmcall(jl_Module, llvmcall_proto(f)); } else { f->setLinkage(GlobalValue::LinkOnceODRLinkage); @@ -1862,9 +1910,9 @@ jl_cgval_t function_sig_t::emit_a_ccall( OperandBundleDef OpBundle("jl_roots", gc_uses); // the actual call - Value *ret = ctx.builder.CreateCall(prepare_call(llvmf), - ArrayRef(&argvals[0], nccallargs + sret), - ArrayRef(&OpBundle, gc_uses.empty() ? 0 : 1)); + CallInst *ret = ctx.builder.CreateCall(functype, llvmf, + ArrayRef(&argvals[0], nccallargs + sret), + ArrayRef(&OpBundle, gc_uses.empty() ? 0 : 1)); ((CallInst*)ret)->setAttributes(attributes); if (cc != CallingConv::C) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index abcd5cbb40e20..64c7056cb359a 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -10,54 +10,6 @@ static Instruction *tbaa_decorate(MDNode *md, Instruction *load_or_store) return load_or_store; } -static Function *function_proto(Function *F, Module *M = nullptr) -{ - // Copy the declaration characteristics of the Function (not the body) - Function *NewF = Function::Create(F->getFunctionType(), - Function::ExternalLinkage, - F->getName(), - M); - - // Declarations are not allowed to have personality routines, but - // copyAttributesFrom sets them anyway. Temporarily unset the personality - // routine from `F`, since copying it and then resetting is more expensive - // as well as introducing an extra use from this unowned function, which - // can cause crashes in the LLVMContext's global destructor. - llvm::Constant *OldPersonalityFn = nullptr; - if (F->hasPersonalityFn()) { - OldPersonalityFn = F->getPersonalityFn(); - F->setPersonalityFn(nullptr); - } - - // FunctionType does not include any attributes. Copy them over manually - // as codegen may make decisions based on the presence of certain attributes - NewF->copyAttributesFrom(F); - - if (OldPersonalityFn) - F->setPersonalityFn(OldPersonalityFn); - - // DLLImport only needs to be set for the shadow module - // it just gets annoying in the JIT - NewF->setDLLStorageClass(GlobalValue::DefaultStorageClass); - - return NewF; -} - -#define prepare_call(Callee) prepare_call_in(jl_Module, (Callee)) -static FunctionCallee prepare_call_in(Module *M, Value *Callee) -{ - if (Function *F = dyn_cast(Callee)) { - GlobalValue *local = M->getNamedValue(Callee->getName()); - if (!local) { - local = function_proto(F, M); - } - Callee = local; - } - FunctionType *FnTy = cast( - Callee->getType()->getPointerElementType()); - return {FnTy, Callee}; -} - // Take an arbitrary untracked value and make it gc-tracked static Value *maybe_decay_untracked(IRBuilder<> &irbuilder, Value *V) { @@ -256,8 +208,9 @@ static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V) Type *T = PointerType::get(T_jlvalue, AddressSpace::Derived); if (V->getType() != T) V = ctx.builder.CreateBitCast(V, T); - CallInst *Call = ctx.builder.CreateCall(prepare_call(pointer_from_objref_func), V); - Call->setAttributes(pointer_from_objref_func->getAttributes()); + Function *F = prepare_call(pointer_from_objref_func); + CallInst *Call = ctx.builder.CreateCall(F, V); + Call->setAttributes(F->getAttributes()); return Call; } @@ -332,7 +285,7 @@ static Value *julia_pgv(jl_codectx_t &ctx, const char *prefix, jl_sym_t *name, j return julia_pgv(ctx, fullname, addr); } -static GlobalVariable *julia_const_gv(jl_value_t *val); +static JuliaVariable *julia_const_gv(jl_value_t *val); static Value *literal_pointer_val_slot(jl_codectx_t &ctx, jl_value_t *p) { // emit a pointer to a jl_value_t* which will allow it to be valid across reloading code @@ -345,7 +298,7 @@ static Value *literal_pointer_val_slot(jl_codectx_t &ctx, jl_value_t *p) gv->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); return gv; } - if (GlobalVariable *gv = julia_const_gv(p)) { + if (JuliaVariable *gv = julia_const_gv(p)) { // if this is a known special object, use the existing GlobalValue return prepare_global_in(jl_Module, gv); } @@ -2225,10 +2178,11 @@ static jl_value_t *static_constant_instance(Constant *constant, jl_value_t *jt) return obj; } -static Value *call_with_attrs(jl_codectx_t &ctx, Function *func, Value *v) +static Value *call_with_attrs(jl_codectx_t &ctx, JuliaFunction *intr, Value *v) { - CallInst *Call = ctx.builder.CreateCall(prepare_call(func), v); - Call->setAttributes(func->getAttributes()); + Function *F = prepare_call(intr); + CallInst *Call = ctx.builder.CreateCall(F, v); + Call->setAttributes(F->getAttributes()); return Call; } @@ -2607,10 +2561,9 @@ static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt) { Value *ptls_ptr = emit_bitcast(ctx, ctx.ptlsStates, T_pint8); - auto call = ctx.builder.CreateCall(prepare_call(jl_alloc_obj_func), - {ptls_ptr, ConstantInt::get(T_size, static_size), - maybe_decay_untracked(jt)}); - call->setAttributes(jl_alloc_obj_func->getAttributes()); + Function *F = prepare_call(jl_alloc_obj_func); + auto call = ctx.builder.CreateCall(F, {ptls_ptr, ConstantInt::get(T_size, static_size), maybe_decay_untracked(jt)}); + call->setAttributes(F->getAttributes()); return call; } @@ -2618,8 +2571,9 @@ static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt) static Value *emit_new_bits(jl_codectx_t &ctx, Value *jt, Value *pval) { pval = ctx.builder.CreateBitCast(pval, T_pint8); - auto call = ctx.builder.CreateCall(prepare_call(jl_newbits_func), { jt, pval }); - call->setAttributes(jl_newbits_func->getAttributes()); + Function *F = prepare_call(jl_newbits_func); + auto call = ctx.builder.CreateCall(F, { jt, pval }); + call->setAttributes(F->getAttributes()); return call; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 13e8db412cac5..0dea4c8b35316 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -156,6 +156,7 @@ TargetMachine *jl_TargetMachine; static DataLayout &jl_data_layout = *(new DataLayout("")); #define jl_Module ctx.f->getParent() #define jl_builderModule(builder) (builder).GetInsertBlock()->getParent()->getParent() +#define prepare_call(Callee) prepare_call_in(jl_Module, (Callee)) // types static Type *T_jlvalue; @@ -272,88 +273,542 @@ static bool is_uniquerep_Type(jl_value_t *t) return jl_is_type_type(t) && type_has_unique_rep(jl_tparam0(t)); } +class jl_codectx_t; +struct JuliaVariable { +public: + StringLiteral name; + bool isconst; + Type *(*_type)(LLVMContext &C); + + JuliaVariable(const JuliaVariable&) = delete; + JuliaVariable(const JuliaVariable&&) = delete; + GlobalVariable *realize(Module *m) { + if (GlobalValue *V = m->getNamedValue(name)) + return cast(V); + return new GlobalVariable(*m, _type(m->getContext()), + isconst, GlobalVariable::ExternalLinkage, + NULL, name); + } + GlobalVariable *realize(jl_codectx_t &ctx); +}; +static inline void add_named_global(JuliaVariable *name, void *addr) +{ + add_named_global(name->name, addr); +} + +struct JuliaFunction { +public: + StringLiteral name; + FunctionType *(*_type)(LLVMContext &C); + AttributeList (*_attrs)(LLVMContext &C); + + JuliaFunction(const JuliaFunction&) = delete; + JuliaFunction(const JuliaFunction&&) = delete; + Function *realize(Module *m) { + if (GlobalValue *V = m->getNamedValue(name)) + return cast(V); + Function *F = Function::Create(_type(m->getContext()), + Function::ExternalLinkage, + name, m); + if (_attrs) + F->setAttributes(_attrs(m->getContext())); + return F; + } + Function *realize(jl_codectx_t &ctx); +}; +template +static inline void add_named_global(JuliaFunction *name, T *addr) +{ + // cast through integer to avoid c++ pedantic warning about casting between + // data and code pointers + add_named_global(name->name, (void*)(uintptr_t)addr); +} +template +static inline void add_named_global(StringRef name, T *addr) +{ + // cast through integer to avoid c++ pedantic warning about casting between + // data and code pointers + add_named_global(name, (void*)(uintptr_t)addr); +} + +AttributeSet Attributes(LLVMContext &C, std::initializer_list attrkinds) +{ + SmallVector attrs(attrkinds.size()); + for (size_t i = 0; i < attrkinds.size(); i++) + attrs[i] = Attribute::get(C, attrkinds.begin()[i]); + return AttributeSet::get(C, makeArrayRef(attrs)); +} + +static Type *get_pjlvalue(LLVMContext &C) { return T_pjlvalue; } + +static FunctionType *get_func_sig(LLVMContext &C) { return jl_func_sig; } + +static AttributeList get_func_attrs(LLVMContext &C) +{ + return AttributeList::get(C, + AttributeSet::get(C, makeArrayRef({Thunk})), + Attributes(C, {Attribute::NonNull}), + None); +} + +static AttributeList get_attrs_noreturn(LLVMContext &C) +{ + return AttributeList::get(C, + Attributes(C, {Attribute::NoReturn}), + AttributeSet(), + None); +} + +static AttributeList get_attrs_sext(LLVMContext &C) +{ + return AttributeList::get(C, + AttributeSet(), + AttributeSet(), + {Attributes(C, {Attribute::SExt})}); +} + +static AttributeList get_attrs_zext(LLVMContext &C) +{ + return AttributeList::get(C, + AttributeSet(), + AttributeSet(), + {Attributes(C, {Attribute::ZExt})}); +} + + // global vars -static GlobalVariable *jlRTLD_DEFAULT_var; +static const auto jlRTLD_DEFAULT_var = new JuliaVariable{ + "jl_RTLD_DEFAULT_handle", + true, + [](LLVMContext &C) { return T_pint8; }, +}; #ifdef _OS_WINDOWS_ -static GlobalVariable *jlexe_var; -static GlobalVariable *jldll_var; +static const auto jlexe_var = new JuliaVariable{ + "jl_exe_handle", + true, + [](LLVMContext &C) { return T_pint8; }, +}; +static const auto jldll_var = new JuliaVariable{ + "jl_dl_handle", + true, + [](LLVMContext &C) { return T_pint8; }, +}; #endif //_OS_WINDOWS_ -static Function *jltls_states_func; +static const auto jlstack_chk_guard_var = new JuliaVariable{ + "__stack_chk_guard", + true, + get_pjlvalue, +}; + +static const auto jlgetworld_global = new JuliaVariable{ + "jl_world_counter", + false, + [](LLVMContext &C) { return (Type*)T_size; }, +}; + +static const auto jltls_states_func = new JuliaFunction{ + "julia.ptls_states", + [](LLVMContext &C) { return FunctionType::get(PointerType::get(T_ppjlvalue, 0), false); }, +}; + // important functions -static Function *jlnew_func; -static Function *jlsplatnew_func; -static Function *jlthrow_func; -static Function *jlerror_func; -static Function *jltypeerror_func; -static Function *jlundefvarerror_func; -static Function *jlboundserror_func; -static Function *jluboundserror_func; -static Function *jlvboundserror_func; -static Function *jlboundserrorv_func; -static Function *jlcheckassign_func; -static Function *jldeclareconst_func; -static Function *jlgetbindingorerror_func; -static Function *jlboundp_func; -static Function *jltopeval_func; -static Function *jlcopyast_func; -static Function *jltuple_func; -static Function *jlnsvec_func; -static Function *jlapplygeneric_func; -static Function *jlinvoke_func; -static Function *jlgetfield_func; -static Function *jlmethod_func; -static Function *jlgenericfunction_func; -static Function *jlenter_func; -static Function *jl_current_exception_func; -static Function *jlleave_func; -static Function *jl_restore_excstack_func; -static Function *jl_excstack_state_func; -static Function *jlegal_func; -static Function *jl_alloc_obj_func; -static Function *jl_newbits_func; -static Function *jl_typeof_func; -static Function *jl_loopinfo_marker_func; -static Function *jl_write_barrier_func; -static Function *jlisa_func; -static Function *jlsubtype_func; -static Function *jlapplytype_func; -static Function *jl_object_id__func; -static Function *setjmp_func; -static Function *memcmp_func; -static Function *box_int8_func; -static Function *box_uint8_func; -static Function *box_int16_func; -static Function *box_uint16_func; -static Function *box_int32_func; -static Function *box_char_func; -static Function *box_uint32_func; -static Function *box_int64_func; -static Function *box_uint64_func; -static Function *box_float32_func; -static Function *box_float64_func; -static Function *box_ssavalue_func; -static Function *expect_func; -static Function *jldlsym_func; -static Function *jltypeassert_func; -//static Function *jlgetnthfield_func; -static Function *jlgetnthfieldchecked_func; -//static Function *jlsetnthfield_func; -static Function *jlgetcfunctiontrampoline_func; -static Function *diff_gc_total_bytes_func; -static Function *sync_gc_total_bytes_func; -static Function *jlarray_data_owner_func; -static GlobalVariable *jlgetworld_global; +// Symbols are not gc-tracked, but we'll treat them as callee rooted anyway, +// because they may come from a gc-rooted location +static const auto jlnew_func = new JuliaFunction{ + "jl_new_structv", + get_func_sig, + get_func_attrs, +}; +static const auto jlsplatnew_func = new JuliaFunction{ + "jl_new_structt", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue, T_prjlvalue}, false); }, + get_func_attrs, +}; +static const auto jlthrow_func = new JuliaFunction{ + "jl_throw", + [](LLVMContext &C) { return FunctionType::get(T_void, + {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, + get_attrs_noreturn, +}; +static const auto jlerror_func = new JuliaFunction{ + "jl_error", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_pint8}, false); }, + get_attrs_noreturn, +}; +static const auto jltypeerror_func = new JuliaFunction{ + "jl_type_error", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_pint8, T_prjlvalue, PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, + get_attrs_noreturn, +}; +static const auto jlundefvarerror_func = new JuliaFunction{ + "jl_undefined_var_error", + [](LLVMContext &C) { return FunctionType::get(T_void, + {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, + get_attrs_noreturn, +}; +static const auto jlboundserrorv_func = new JuliaFunction{ + "jl_bounds_error_ints", + [](LLVMContext &C) { return FunctionType::get(T_void, + {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted), T_psize, T_size}, false); }, + get_attrs_noreturn, +}; +static const auto jlboundserror_func = new JuliaFunction{ + "jl_bounds_error_int", + [](LLVMContext &C) { return FunctionType::get(T_void, + {PointerType::get(T_jlvalue, AddressSpace::CalleeRooted), T_size}, false); }, + get_attrs_noreturn, +}; +static const auto jlvboundserror_func = new JuliaFunction{ + "jl_bounds_error_tuple_int", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_pprjlvalue, T_size, T_size}, false); }, + get_attrs_noreturn, +}; +static const auto jluboundserror_func = new JuliaFunction{ + "jl_bounds_error_unboxed_int", + [](LLVMContext &C) { return FunctionType::get(T_void, + {PointerType::get(T_int8, AddressSpace::Derived), T_pjlvalue, T_size}, false); }, + get_attrs_noreturn, +}; +static const auto jlcheckassign_func = new JuliaFunction{ + "jl_checked_assignment", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_pjlvalue, PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)}, false); }, +}; +static const auto jldeclareconst_func = new JuliaFunction{ + "jl_declare_constant", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_pjlvalue}, false); }, +}; +static const auto jlgetbindingorerror_func = new JuliaFunction{ + "jl_get_binding_or_error", + [](LLVMContext &C) { return FunctionType::get(T_pjlvalue, + {T_pjlvalue, T_pjlvalue}, false); }, +}; +static const auto jlboundp_func = new JuliaFunction{ + "jl_boundp", + [](LLVMContext &C) { return FunctionType::get(T_int32, + {T_pjlvalue, T_pjlvalue}, false); }, +}; +static const auto jltopeval_func = new JuliaFunction{ + "jl_toplevel_eval", + [](LLVMContext &C) { return FunctionType::get(T_pjlvalue, + {T_pjlvalue, T_pjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +static const auto jlcopyast_func = new JuliaFunction{ + "jl_copy_ast", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +//static const auto jlnsvec_func = new JuliaFunction{ +// "jl_svec", +// [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, +// {T_size}, true); }, +// [](LLVMContext &C) { return AttributeList::get(C, +// AttributeSet(), +// Attributes(C, {Attribute::NonNull}), +// None); }, +//}; +static const auto jlapplygeneric_func = new JuliaFunction{ + "jl_apply_generic", + get_func_sig, + get_func_attrs, +}; +static const auto jlinvoke_func = new JuliaFunction{ + "jl_invoke", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue, T_pprjlvalue, T_uint32, T_prjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NonNull}), + {AttributeSet(), + Attributes(C, {Attribute::ReadOnly, Attribute::NoCapture})}); }, +}; +static const auto jlmethod_func = new JuliaFunction{ + "jl_method_def", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_prjlvalue, T_prjlvalue, T_pjlvalue}, false); }, +}; +static const auto jlgenericfunction_func = new JuliaFunction{ + "jl_generic_function_def", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_pjlvalue, T_pjlvalue, T_pprjlvalue, T_pjlvalue, T_pjlvalue}, false); }, +}; +static const auto jlenter_func = new JuliaFunction{ + "jl_enter_handler", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_pint8}, false); }, +}; +static const auto jl_current_exception_func = new JuliaFunction{ + "jl_current_exception", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, false); }, +}; +static const auto jlleave_func = new JuliaFunction{ + "jl_pop_handler", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_int32}, false); }, +}; +static const auto jl_restore_excstack_func = new JuliaFunction{ + "jl_restore_excstack", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_size}, false); }, +}; +static const auto jl_excstack_state_func = new JuliaFunction{ + "jl_excstack_state", + [](LLVMContext &C) { return FunctionType::get(T_size, false); }, +}; +static const auto jlegal_func = new JuliaFunction{ + "jl_egal", + [](LLVMContext &C) { + Type *T = PointerType::get(T_jlvalue, AddressSpace::CalleeRooted); + return FunctionType::get(T_int32, {T, T}, false); }, +}; +static const auto jl_alloc_obj_func = new JuliaFunction{ + "julia.gc_alloc_obj", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_pint8, T_size, T_prjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet::get(C, makeArrayRef({Attribute::getWithAllocSizeArgs(C, 1, None)})), // returns %1 bytes + Attributes(C, {Attribute::NoAlias, Attribute::NonNull}), + None); }, +}; +static const auto jl_newbits_func = new JuliaFunction{ + "jl_new_bits", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue, T_pint8}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NoAlias, Attribute::NonNull}), + None); }, +}; +static const auto jl_typeof_func = new JuliaFunction{ + "julia.typeof", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + Attributes(C, {Attribute::ReadOnly, Attribute::NoUnwind, Attribute::ArgMemOnly, Attribute::NoRecurse}), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +static const auto jl_loopinfo_marker_func = new JuliaFunction{ + "julia.loopinfo_marker", + [](LLVMContext &C) { return FunctionType::get(T_void, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + Attributes(C, {Attribute::ReadOnly, Attribute::NoRecurse, Attribute::InaccessibleMemOnly}), + AttributeSet(), + None); }, +}; +static const auto jl_write_barrier_func = new JuliaFunction{ + "julia.write_barrier", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_prjlvalue}, true); }, + [](LLVMContext &C) { return AttributeList::get(C, + Attributes(C, {Attribute::NoUnwind, Attribute::NoRecurse, Attribute::InaccessibleMemOnly}), + AttributeSet(), + None); }, +}; +static const auto jlisa_func = new JuliaFunction{ + "jl_isa", + [](LLVMContext &C) { return FunctionType::get(T_int32, + {T_prjlvalue, T_prjlvalue}, false); }, +}; + +static const auto jlsubtype_func = new JuliaFunction{ + "jl_subtype", + [](LLVMContext &C) { return FunctionType::get(T_int32, + {T_prjlvalue, T_prjlvalue}, false); }, +}; +static const auto jlapplytype_func = new JuliaFunction{ + "jl_instantiate_type_in_env", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_pjlvalue, T_pjlvalue, T_pprjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +static const auto jl_object_id__func = new JuliaFunction{ + "jl_object_id_", + [](LLVMContext &C) { return FunctionType::get(T_size, + {T_prjlvalue, PointerType::get(T_int8, AddressSpace::Derived)}, false); }, +}; +static const auto setjmp_func = new JuliaFunction{ + jl_setjmp_name, + [](LLVMContext &C) { return FunctionType::get(T_int32, + {T_pint8, +#ifndef _OS_WINDOWS_ + T_int32, +#endif + }, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + Attributes(C, {Attribute::ReturnsTwice}), + AttributeSet(), + None); }, +}; +static const auto memcmp_func = new JuliaFunction{ + "memcmp", + [](LLVMContext &C) { return FunctionType::get(T_int32, + {T_pint8, T_pint8, T_size}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + Attributes(C, {Attribute::ReadOnly, Attribute::NoUnwind, Attribute::ArgMemOnly}), + AttributeSet(), + None); }, + // TODO: inferLibFuncAttributes(*memcmp_func, TLI); +}; +static const auto jldlsym_func = new JuliaFunction{ + "jl_load_and_lookup", + [](LLVMContext &C) { return FunctionType::get(T_pvoidfunc, + {T_pint8, T_pint8, PointerType::get(T_pint8, 0)}, false); }, +}; +static const auto jltypeassert_func = new JuliaFunction{ + "jl_typeassert", + [](LLVMContext &C) { return FunctionType::get(T_void, + {T_prjlvalue, T_prjlvalue}, false); }, +}; +static const auto jlgetnthfieldchecked_func = new JuliaFunction{ + "jl_get_nth_field_checked", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue, T_size}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +static const auto jlgetcfunctiontrampoline_func = new JuliaFunction{ + "jl_get_cfunction_trampoline", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + { + T_prjlvalue, // f (object) + T_pjlvalue, // result + T_pint8, // cache + T_pjlvalue, // fill + FunctionType::get(T_pint8, { T_pint8, T_ppjlvalue }, false)->getPointerTo(), // trampoline + T_pjlvalue, // env + T_pprjlvalue, // vals + }, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet(), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +static const auto diff_gc_total_bytes_func = new JuliaFunction{ + "jl_gc_diff_total_bytes", + [](LLVMContext &C) { return FunctionType::get(T_int64, false); }, +}; +static const auto sync_gc_total_bytes_func = new JuliaFunction{ + "jl_gc_sync_total_bytes", + [](LLVMContext &C) { return FunctionType::get(T_int64, + {T_int64}, false); }, +}; +static const auto jlarray_data_owner_func = new JuliaFunction{ + "jl_array_data_owner", + [](LLVMContext &C) { return FunctionType::get(T_prjlvalue, + {T_prjlvalue}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + Attributes(C, {Attribute::ReadOnly, Attribute::NoUnwind}), + Attributes(C, {Attribute::NonNull}), + None); }, +}; +#define BOX_FUNC(ct,rt,at,attrs) \ +static const auto box_##ct##_func = new JuliaFunction{ \ + "jl_box_"#ct, \ + [](LLVMContext &C) { return FunctionType::get(rt, \ + {at}, false); }, \ + attrs, \ +} +BOX_FUNC(int8, T_pjlvalue, T_int8, get_attrs_sext); +BOX_FUNC(uint8, T_pjlvalue, T_int8, get_attrs_zext); +BOX_FUNC(int16, T_prjlvalue, T_int16, get_attrs_sext); +BOX_FUNC(uint16, T_prjlvalue, T_int16, get_attrs_zext); +BOX_FUNC(int32, T_prjlvalue, T_int32, get_attrs_sext); +BOX_FUNC(uint32, T_prjlvalue, T_int32, get_attrs_zext); +BOX_FUNC(int64, T_prjlvalue, T_int64, get_attrs_sext); +BOX_FUNC(uint64, T_prjlvalue, T_int64, get_attrs_zext); +BOX_FUNC(char, T_prjlvalue, T_char, get_attrs_zext); +BOX_FUNC(float32, T_prjlvalue, T_float32, nullptr); +BOX_FUNC(float64, T_prjlvalue, T_float64, nullptr); +BOX_FUNC(ssavalue, T_prjlvalue, T_size, nullptr); +#undef BOX_FUNC + // placeholder functions -static Function *gcroot_flush_func; -static Function *gc_preserve_begin_func; -static Function *gc_preserve_end_func; -static Function *except_enter_func; -static Function *pointer_from_objref_func; +static const auto gcroot_flush_func = new JuliaFunction{ + "julia.gcroot_flush", + [](LLVMContext &C) { return FunctionType::get(T_void, false); }, +}; +static const auto gc_preserve_begin_func = new JuliaFunction{ + "llvm.julia.gc_preserve_begin", + [](LLVMContext &C) { return FunctionType::get(Type::getTokenTy(C), true); }, +}; +static const auto gc_preserve_end_func = new JuliaFunction { + "llvm.julia.gc_preserve_end", + [](LLVMContext &C) { return FunctionType::get(T_void, {Type::getTokenTy(C)}, false); }, +}; +static const auto except_enter_func = new JuliaFunction{ + "julia.except_enter", + [](LLVMContext &C) { return FunctionType::get(T_int32, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet::get(C, makeArrayRef({Attribute::get(C, Attribute::ReturnsTwice)})), + AttributeSet(), + None); }, +}; +static const auto pointer_from_objref_func = new JuliaFunction{ + "julia.pointer_from_objref", + [](LLVMContext &C) { return FunctionType::get(T_pjlvalue, + {PointerType::get(T_jlvalue, AddressSpace::Derived)}, false); }, + [](LLVMContext &C) { return AttributeList::get(C, + AttributeSet::get(C, makeArrayRef({Attribute::get(C, Attribute::ReadNone), Attribute::get(C, Attribute::NoUnwind)})), + AttributeSet(), + None); }, +}; + +static const auto jltuple_func = new JuliaFunction{"jl_f_tuple", get_func_sig, get_func_attrs}; +static const auto jlgetfield_func = new JuliaFunction{"jl_f_getfield", get_func_sig, get_func_attrs}; +static const std::map builtin_func_map = { + { &jl_f_is, new JuliaFunction{"jl_f_is", get_func_sig, get_func_attrs} }, + { &jl_f_typeof, new JuliaFunction{"jl_f_typeof", get_func_sig, get_func_attrs} }, + { &jl_f_sizeof, new JuliaFunction{"jl_f_sizeof", get_func_sig, get_func_attrs} }, + { &jl_f_issubtype, new JuliaFunction{"jl_f_issubtype", get_func_sig, get_func_attrs} }, + { &jl_f_isa, new JuliaFunction{"jl_f_isa", get_func_sig, get_func_attrs} }, + { &jl_f_typeassert, new JuliaFunction{"jl_f_typeassert", get_func_sig, get_func_attrs} }, + { &jl_f_ifelse, new JuliaFunction{"jl_f_ifelse", get_func_sig, get_func_attrs} }, + { &jl_f__apply, new JuliaFunction{"jl_f__apply", get_func_sig, get_func_attrs} }, + { &jl_f__apply_iterate, new JuliaFunction{"jl_f__apply_iterate", get_func_sig, get_func_attrs} }, + { &jl_f__apply_pure, new JuliaFunction{"jl_f__apply_pure", get_func_sig, get_func_attrs} }, + { &jl_f__apply_latest, new JuliaFunction{"jl_f__apply_latest", get_func_sig, get_func_attrs} }, + { &jl_f_throw, new JuliaFunction{"jl_f_throw", get_func_sig, get_func_attrs} }, + { &jl_f_tuple, jltuple_func }, + { &jl_f_svec, new JuliaFunction{"jl_f_svec", get_func_sig, get_func_attrs} }, + { &jl_f_applicable, new JuliaFunction{"jl_f_applicable", get_func_sig, get_func_attrs} }, + { &jl_f_invoke, new JuliaFunction{"jl_f_invoke", get_func_sig, get_func_attrs} }, + { &jl_f_invoke_kwsorter, new JuliaFunction{"jl_f_invoke_kwsorter", get_func_sig, get_func_attrs} }, + { &jl_f_isdefined, new JuliaFunction{"jl_f_isdefined", get_func_sig, get_func_attrs} }, + { &jl_f_getfield, jlgetfield_func }, + { &jl_f_setfield, new JuliaFunction{"jl_f_setfield", get_func_sig, get_func_attrs} }, + { &jl_f_fieldtype, new JuliaFunction{"jl_f_fieldtype", get_func_sig, get_func_attrs} }, + { &jl_f_nfields, new JuliaFunction{"jl_f_nfields", get_func_sig, get_func_attrs} }, + { &jl_f__expr, new JuliaFunction{"jl_f__expr", get_func_sig, get_func_attrs} }, + { &jl_f__typevar, new JuliaFunction{"jl_f__typevar", get_func_sig, get_func_attrs} }, + { &jl_f_arrayref, new JuliaFunction{"jl_f_arrayref", get_func_sig, get_func_attrs} }, + { &jl_f_const_arrayref, new JuliaFunction{"jl_f_const_arrayref", get_func_sig, get_func_attrs} }, + { &jl_f_arrayset, new JuliaFunction{"jl_f_arrayset", get_func_sig, get_func_attrs} }, + { &jl_f_arraysize, new JuliaFunction{"jl_f_arraysize", get_func_sig, get_func_attrs} }, + { &jl_f_apply_type, new JuliaFunction{"jl_f_apply_type", get_func_sig, get_func_attrs} }, +}; -static std::map builtin_func_map; // --- code generation --- extern "C" { @@ -590,6 +1045,10 @@ class jl_codectx_t { } }; +GlobalVariable *JuliaVariable::realize(jl_codectx_t &ctx) { + return realize(jl_Module); +} + static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed = NULL); static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, StringRef name, jl_value_t *sig, jl_value_t *jlrettype); static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1); @@ -600,13 +1059,45 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i); static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const std::string &msg); static void allocate_gc_frame(jl_codectx_t &ctx, BasicBlock *b0); static void CreateTrap(IRBuilder<> &irbuilder); -static CallInst *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, +static CallInst *emit_jlcall(jl_codectx_t &ctx, Function *theFptr, Value *theF, + jl_cgval_t *args, size_t nargs, CallingConv::ID cc); +static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *theF, jl_cgval_t *args, size_t nargs, CallingConv::ID cc); static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); static GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G); static Instruction *tbaa_decorate(MDNode *md, Instruction *load_or_store); +static GlobalVariable *prepare_global_in(Module *M, JuliaVariable *G) +{ + return G->realize(M); +} + +static Function *prepare_call_in(Module *M, JuliaFunction *G) +{ + return G->realize(M); +} + +static inline GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G) +{ + if (G->getParent() == M) + return G; + GlobalValue *local = M->getNamedValue(G->getName()); + if (!local) { + // Copy the GlobalVariable, but without the initializer, so it becomes a declaration + GlobalVariable *proto = new GlobalVariable(*M, G->getType()->getElementType(), + G->isConstant(), GlobalVariable::ExternalLinkage, + nullptr, G->getName(), nullptr, G->getThreadLocalMode()); + proto->copyAttributesFrom(G); + // DLLImport only needs to be set for the shadow module + // it just gets annoying in the JIT + proto->setDLLStorageClass(GlobalValue::DefaultStorageClass); + return proto; + } + return cast(local); +} + + // --- convenience functions for tagging llvm values with julia types --- static GlobalVariable *get_pointer_to_constant(Constant *val, StringRef name, Module &M) @@ -2611,7 +3102,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return false; } -static CallInst *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, +static CallInst *emit_jlcall(jl_codectx_t &ctx, Function *theFptr, Value *theF, jl_cgval_t *argv, size_t nargs, CallingConv::ID cc) { // emit arguments @@ -2628,12 +3119,17 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, } FunctionType *FTy = FunctionType::get(T_prjlvalue, argsT, false); CallInst *result = ctx.builder.CreateCall(FTy, - ctx.builder.CreateBitCast(prepare_call(theFptr).getCallee(), FTy->getPointerTo()), + ctx.builder.CreateBitCast(theFptr, FTy->getPointerTo()), theArgs); add_return_attr(result, Attribute::NonNull); result->setCallingConv(cc); return result; } +static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *theF, + jl_cgval_t *argv, size_t nargs, CallingConv::ID cc) +{ + return emit_jlcall(ctx, prepare_call(theFptr), theF, argv, nargs, cc); +} static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_t *mi, jl_value_t *jlretty, StringRef specFunctionObject, @@ -2755,16 +3251,13 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_method_instance_ static jl_cgval_t emit_call_specfun_boxed(jl_codectx_t &ctx, StringRef specFunctionObject, jl_cgval_t *argv, size_t nargs, jl_value_t *inferred_retty) { - auto theFptr = jl_Module->getOrInsertFunction(specFunctionObject, jl_func_sig) + auto theFptr = cast(jl_Module->getOrInsertFunction(specFunctionObject, jl_func_sig) #if JL_LLVM_VERSION >= 90000 - .getCallee(); -#else - ; + .getCallee() #endif - if (auto F = dyn_cast(theFptr->stripPointerCasts())) { - add_return_attr(F, Attribute::NonNull); - F->addFnAttr(Thunk); - } + ); + add_return_attr(theFptr, Attribute::NonNull); + theFptr->addFnAttr(Thunk); Value *ret = emit_jlcall(ctx, theFptr, nullptr, argv, nargs, JLCALL_F_CC); return mark_julia_type(ctx, ret, true, inferred_retty); } @@ -2850,7 +3343,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) } } if (!handled) { - Value *r = emit_jlcall(ctx, prepare_call(jlinvoke_func).getCallee(), boxed(ctx, lival), argv, nargs, JLCALL_F2_CC); + Value *r = emit_jlcall(ctx, jlinvoke_func, boxed(ctx, lival), argv, nargs, JLCALL_F2_CC); result = mark_julia_type(ctx, r, true, rt); } if (result.typ == jl_bottom_type) @@ -2888,10 +3381,9 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) } // special case for known builtin not handled by emit_builtin_call - std::map::iterator it = builtin_func_map.find(jl_get_builtin_fptr(f.constant)); + auto it = builtin_func_map.find(jl_get_builtin_fptr(f.constant)); if (it != builtin_func_map.end()) { - Value *theFptr = it->second; - Value *ret = emit_jlcall(ctx, theFptr, maybe_decay_untracked(V_null), &argv[1], nargs - 1, JLCALL_F_CC); + Value *ret = emit_jlcall(ctx, it->second, maybe_decay_untracked(V_null), &argv[1], nargs - 1, JLCALL_F_CC); return mark_julia_type(ctx, ret, true, rt); } } @@ -3909,11 +4401,15 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod ctx.f = f; // for jl_Module BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", f); ctx.builder.SetInsertPoint(b0); - FunctionCallee theFunc; + Function *theFunc; Value *theFarg; if (codeinst->invoke != NULL) { StringRef theFptrName = jl_ExecutionEngine->getFunctionAtAddress((uintptr_t)codeinst->invoke, codeinst); - theFunc = M->getOrInsertFunction(theFptrName, jlinvoke_func->getFunctionType()); + theFunc = cast(M->getOrInsertFunction(theFptrName, jlinvoke_func->_type(jl_LLVMContext)) +#if JL_LLVM_VERSION >= 90000 + .getCallee() +#endif + ); theFarg = literal_pointer_val(ctx, (jl_value_t*)codeinst); } else { @@ -3923,7 +4419,7 @@ static Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Module *M, jl_cod theFarg = maybe_decay_untracked(theFarg); auto args = f->arg_begin(); CallInst *r = ctx.builder.CreateCall(theFunc, { &*args, &*++args, &*++args, theFarg }); - r->setAttributes(cast(theFunc.getCallee())->getAttributes()); + r->setAttributes(theFunc->getAttributes()); ctx.builder.CreateRet(r); return f; } @@ -3933,7 +4429,7 @@ static void emit_cfunc_invalidate( jl_value_t *calltype, jl_value_t *rettype, size_t nargs, jl_codegen_params_t ¶ms, - Function *target=jlapplygeneric_func) + Function *target) { jl_codectx_t ctx(jl_LLVMContext, params); ctx.f = gf_thunk; @@ -4022,6 +4518,16 @@ static void emit_cfunc_invalidate( } } +static void emit_cfunc_invalidate( + Function *gf_thunk, jl_returninfo_t::CallingConv cc, unsigned return_roots, + jl_value_t *calltype, jl_value_t *rettype, + size_t nargs, + jl_codegen_params_t ¶ms) +{ + emit_cfunc_invalidate(gf_thunk, cc, return_roots, calltype, rettype, nargs, params, + prepare_call_in(gf_thunk->getParent(), jlapplygeneric_func)); +} + static Function* gen_cfun_wrapper( Module *into, jl_codegen_params_t ¶ms, const function_sig_t &sig, jl_value_t *ff, const char *aliasname, @@ -4328,7 +4834,7 @@ static Function* gen_cfun_wrapper( ctx.builder.CreateBr(b_after); ctx.builder.SetInsertPoint(b_generic); } - Value *ret = emit_jlcall(ctx, prepare_call(jlapplygeneric_func).getCallee(), NULL, inputargs, nargs + 1, JLCALL_F_CC); + Value *ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, inputargs, nargs + 1, JLCALL_F_CC); if (age_ok) { ctx.builder.CreateBr(b_after); ctx.builder.SetInsertPoint(b_after); @@ -4718,12 +5224,8 @@ void jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declr // restore a ccallable from the system image void *addr; int found = jl_dlsym(sysimg_handle, name, &addr, 0); - if (found) { - FunctionType *ftype = sig.functype(); - Function *F = Function::Create(ftype, GlobalVariable::ExternalLinkage, name); - add_named_global(F, addr); - delete F; - } + if (found) + add_named_global(name, addr); } else { jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); @@ -5624,7 +6126,7 @@ static std::pair, jl_llvm_functions_t> emit_varinfo_assign(ctx, vi, tuple); } else { - restTuple = emit_jlcall(ctx, prepare_call(jltuple_func).getCallee(), maybe_decay_untracked(V_null), + restTuple = emit_jlcall(ctx, jltuple_func, maybe_decay_untracked(V_null), vargs, ctx.nvargs, JLCALL_F_CC); jl_cgval_t tuple = mark_julia_type(ctx, restTuple, true, vi.value.typ); emit_varinfo_assign(ctx, vi, tuple); @@ -5632,14 +6134,15 @@ static std::pair, jl_llvm_functions_t> } else { // restarg = jl_f_tuple(NULL, &args[nreq], nargs - nreq) + Function *F = prepare_call(jltuple_func); restTuple = - ctx.builder.CreateCall(prepare_call(jltuple_func), + ctx.builder.CreateCall(F, { maybe_decay_untracked(V_null), ctx.builder.CreateInBoundsGEP(T_prjlvalue, argArray, ConstantInt::get(T_size, nreq - 1)), ctx.builder.CreateSub(argCount, ConstantInt::get(T_int32, nreq - 1)) }); - restTuple->setAttributes(jltuple_func->getAttributes()); + restTuple->setAttributes(F->getAttributes()); ctx.builder.CreateStore(restTuple, vi.boxroot); } } @@ -6652,33 +7155,20 @@ std::pair tbaa_make_child(const char *name, MDNode *parent=null return std::make_pair(n, scalar); } -static GlobalVariable *global_to_llvm(const std::string &cname, void *addr, Module *m) -{ - GlobalVariable *gv = - new GlobalVariable(*m, T_pjlvalue, true, - GlobalVariable::ExternalLinkage, NULL, cname); - add_named_global(gv, addr); - return gv; -} -llvm::SmallVector, 16> gv_for_global; -static GlobalVariable *global_jlvalue_to_llvm(const std::string &cname, jl_value_t **addr, Module *m) +std::vector> gv_for_global; +static void global_jlvalue_to_llvm(JuliaVariable *var, jl_value_t **addr) { - GlobalVariable *gv = global_to_llvm(cname, (void*)addr, m); - gv_for_global.push_back(std::make_pair(addr, gv)); - return gv; + gv_for_global.push_back(std::make_pair(addr, var)); } -static GlobalVariable *julia_const_gv(jl_value_t *val) +static JuliaVariable *julia_const_gv(jl_value_t *val) { - for (auto& kv : gv_for_global) { + for (auto &kv : gv_for_global) { if (*kv.first == val) return kv.second; } return nullptr; } -// TODO: delete this -extern "C" void jl_fptr_to_llvm(void *fptr, jl_code_instance_t *lam, int specsig) { } - static void init_julia_llvm_meta(void) { tbaa_gcframe = tbaa_make_child("jtbaa_gcframe").first; @@ -6708,15 +7198,6 @@ static void init_julia_llvm_meta(void) Thunk = Attribute::get(jl_LLVMContext, "thunk"); } -static Function *jlcall_func_to_llvm(const std::string &cname, jl_fptr_args_t addr, Module *m) -{ - Function *f = Function::Create(jl_func_sig, Function::ExternalLinkage, cname, m); - add_return_attr(f, Attribute::NonNull); - f->addFnAttr(Thunk); - add_named_global(f, addr); - return f; -} - static void init_julia_llvm_env(Module *m) { // every variable or function mapped in this function must be @@ -6750,8 +7231,6 @@ static void init_julia_llvm_env(Module *m) T_void = Type::getVoidTy(jl_LLVMContext); T_pvoidfunc = FunctionType::get(T_void, /*isVarArg*/false)->getPointerTo(); - auto T_pint8_derived = PointerType::get(T_int8, AddressSpace::Derived); - // add needed base debugging definitions to our LLVM environment DIBuilder dbuilder(*m); DIFile *julia_h = dbuilder.createFile("julia.h", ""); @@ -6818,556 +7297,104 @@ static void init_julia_llvm_env(Module *m) jl_array_llvmt = StructType::create(jl_LLVMContext, makeArrayRef(vaelts), "jl_array_t"); jl_parray_llvmt = PointerType::get(jl_array_llvmt, 0); +} - global_to_llvm("__stack_chk_guard", (void*)&__stack_chk_guard, m); - Function *jl__stack_chk_fail = - Function::Create(FunctionType::get(T_void, false), - Function::ExternalLinkage, - "__stack_chk_fail", m); - jl__stack_chk_fail->setDoesNotReturn(); - add_named_global(jl__stack_chk_fail, &__stack_chk_fail); - - global_jlvalue_to_llvm("jl_true", &jl_true, m); - global_jlvalue_to_llvm("jl_false", &jl_false, m); - global_jlvalue_to_llvm("jl_emptysvec", (jl_value_t**)&jl_emptysvec, m); - global_jlvalue_to_llvm("jl_emptytuple", &jl_emptytuple, m); - global_jlvalue_to_llvm("jl_diverror_exception", &jl_diverror_exception, m); - global_jlvalue_to_llvm("jl_undefref_exception", &jl_undefref_exception, m); - - jlRTLD_DEFAULT_var = - new GlobalVariable(*m, T_pint8, - true, GlobalVariable::ExternalLinkage, - NULL, "jl_RTLD_DEFAULT_handle"); +static void init_jit_functions(void) +{ + add_named_global(jlstack_chk_guard_var, &__stack_chk_guard); add_named_global(jlRTLD_DEFAULT_var, &jl_RTLD_DEFAULT_handle); #ifdef _OS_WINDOWS_ - jlexe_var = - new GlobalVariable(*m, T_pint8, - true, GlobalVariable::ExternalLinkage, - NULL, "jl_exe_handle"); add_named_global(jlexe_var, &jl_exe_handle); - jldll_var = - new GlobalVariable(*m, T_pint8, - true, GlobalVariable::ExternalLinkage, - NULL, "jl_dl_handle"); add_named_global(jldll_var, &jl_dl_handle); #endif - - jltls_states_func = Function::Create(FunctionType::get(PointerType::get(T_ppjlvalue, 0), false), - Function::ExternalLinkage, "julia.ptls_states"); - add_named_global(jltls_states_func, (void*)NULL, /*dllimport*/false); - - std::vector args1(0); - args1.push_back(T_pint8); - jlerror_func = - Function::Create(FunctionType::get(T_void, args1, false), - Function::ExternalLinkage, - "jl_error", m); - jlerror_func->setDoesNotReturn(); + global_jlvalue_to_llvm(new JuliaVariable{"jl_true", true, get_pjlvalue}, &jl_true); + global_jlvalue_to_llvm(new JuliaVariable{"jl_false", true, get_pjlvalue}, &jl_false); + global_jlvalue_to_llvm(new JuliaVariable{"jl_emptysvec", true, get_pjlvalue}, (jl_value_t**)&jl_emptysvec); + global_jlvalue_to_llvm(new JuliaVariable{"jl_emptytuple", true, get_pjlvalue}, &jl_emptytuple); + global_jlvalue_to_llvm(new JuliaVariable{"jl_diverror_exception", true, get_pjlvalue}, &jl_diverror_exception); + global_jlvalue_to_llvm(new JuliaVariable{"jl_undefref_exception", true, get_pjlvalue}, &jl_undefref_exception); + add_named_global(jlgetworld_global, &jl_world_counter); + add_named_global("__stack_chk_fail", &__stack_chk_fail); + add_named_global(jltls_states_func, (void*)NULL); add_named_global(jlerror_func, &jl_error); - - std::vector args1_(0); - args1_.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - jlthrow_func = - Function::Create(FunctionType::get(T_void, args1_, false), - Function::ExternalLinkage, - "jl_throw", m); - jlthrow_func->setDoesNotReturn(); add_named_global(jlthrow_func, &jl_throw); - - // Symbols are not gc-tracked, but we'll treat them as callee rooted anyway, - // because they may come from a gc-rooted location - jlundefvarerror_func = - Function::Create(FunctionType::get(T_void, args1_, false), - Function::ExternalLinkage, - "jl_undefined_var_error", m); - jlundefvarerror_func->setDoesNotReturn(); add_named_global(jlundefvarerror_func, &jl_undefined_var_error); - - std::vector args2_boundserrorv(0); - args2_boundserrorv.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - args2_boundserrorv.push_back(T_psize); - args2_boundserrorv.push_back(T_size); - jlboundserrorv_func = - Function::Create(FunctionType::get(T_void, args2_boundserrorv, false), - Function::ExternalLinkage, - "jl_bounds_error_ints", m); - jlboundserrorv_func->setDoesNotReturn(); add_named_global(jlboundserrorv_func, &jl_bounds_error_ints); - - std::vector args2_boundserror(0); - args2_boundserror.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - args2_boundserror.push_back(T_size); - jlboundserror_func = - Function::Create(FunctionType::get(T_void, args2_boundserror, false), - Function::ExternalLinkage, - "jl_bounds_error_int", m); - jlboundserror_func->setDoesNotReturn(); add_named_global(jlboundserror_func, &jl_bounds_error_int); - - std::vector args3_vboundserror(0); - args3_vboundserror.push_back(T_pprjlvalue); - args3_vboundserror.push_back(T_size); - args3_vboundserror.push_back(T_size); - jlvboundserror_func = - Function::Create(FunctionType::get(T_void, args3_vboundserror, false), - Function::ExternalLinkage, - "jl_bounds_error_tuple_int", m); - jlvboundserror_func->setDoesNotReturn(); add_named_global(jlvboundserror_func, &jl_bounds_error_tuple_int); - - std::vector args3_uboundserror(0); - args3_uboundserror.push_back(T_pint8_derived); - args3_uboundserror.push_back(T_pjlvalue); - args3_uboundserror.push_back(T_size); - jluboundserror_func = - Function::Create(FunctionType::get(T_void, args3_uboundserror, false), - Function::ExternalLinkage, - "jl_bounds_error_unboxed_int", m); - jluboundserror_func->setDoesNotReturn(); add_named_global(jluboundserror_func, &jl_bounds_error_unboxed_int); - - jlnew_func = - Function::Create(jl_func_sig, Function::ExternalLinkage, - "jl_new_structv", m); - add_return_attr(jlnew_func, Attribute::NonNull); - jlnew_func->addFnAttr(Thunk); add_named_global(jlnew_func, &jl_new_structv); - - std::vector args_2rptrs_(0); - args_2rptrs_.push_back(T_prjlvalue); - args_2rptrs_.push_back(T_prjlvalue); - jlsplatnew_func = - Function::Create(FunctionType::get(T_prjlvalue, args_2rptrs_, false), - Function::ExternalLinkage, - "jl_new_structt", m); - add_return_attr(jlsplatnew_func, Attribute::NonNull); - jlsplatnew_func->addFnAttr(Thunk); add_named_global(jlsplatnew_func, &jl_new_structt); - - std::vector args2(0); - args2.push_back(T_pint8); -#ifndef _OS_WINDOWS_ - args2.push_back(T_int32); -#endif - setjmp_func = - Function::Create(FunctionType::get(T_int32, args2, false), - Function::ExternalLinkage, jl_setjmp_name, m); - setjmp_func->addFnAttr(Attribute::ReturnsTwice); add_named_global(setjmp_func, &jl_setjmp_f); - - std::vector args_memcmp(0); - args_memcmp.push_back(T_pint8); - args_memcmp.push_back(T_pint8); - args_memcmp.push_back(T_size); - memcmp_func = - Function::Create(FunctionType::get(T_int32, args_memcmp, false), - Function::ExternalLinkage, "memcmp", m); - memcmp_func->addFnAttr(Attribute::ReadOnly); - memcmp_func->addFnAttr(Attribute::NoUnwind); - memcmp_func->addFnAttr(Attribute::ArgMemOnly); add_named_global(memcmp_func, &memcmp); - // TODO: inferLibFuncAttributes(*memcmp_func, TLI); - - std::vector te_args(0); - te_args.push_back(T_pint8); - te_args.push_back(T_prjlvalue); - te_args.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - jltypeerror_func = - Function::Create(FunctionType::get(T_void, te_args, false), - Function::ExternalLinkage, - "jl_type_error", m); - jltypeerror_func->setDoesNotReturn(); add_named_global(jltypeerror_func, &jl_type_error); - - std::vector args_2ptrs(0); - args_2ptrs.push_back(T_pjlvalue); - args_2ptrs.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - jlcheckassign_func = - Function::Create(FunctionType::get(T_void, args_2ptrs, false), - Function::ExternalLinkage, - "jl_checked_assignment", m); add_named_global(jlcheckassign_func, &jl_checked_assignment); - - std::vector args_1binding(0); - args_1binding.push_back(T_pjlvalue); - jldeclareconst_func = - Function::Create(FunctionType::get(T_void, args_1binding, false), - Function::ExternalLinkage, - "jl_declare_constant", m); add_named_global(jldeclareconst_func, &jl_declare_constant); - - std::vector args_2ptrs_(0); - args_2ptrs_.push_back(T_pjlvalue); - args_2ptrs_.push_back(T_pjlvalue); - jlgetbindingorerror_func = - Function::Create(FunctionType::get(T_pjlvalue, args_2ptrs_, false), - Function::ExternalLinkage, - "jl_get_binding_or_error", m); add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error); - - jlboundp_func = - Function::Create(FunctionType::get(T_int32, args_2ptrs_, false), - Function::ExternalLinkage, - "jl_boundp", m); add_named_global(jlboundp_func, &jl_boundp); - - builtin_func_map[jl_f_is] = jlcall_func_to_llvm("jl_f_is", &jl_f_is, m); - builtin_func_map[jl_f_typeof] = jlcall_func_to_llvm("jl_f_typeof", &jl_f_typeof, m); - builtin_func_map[jl_f_sizeof] = jlcall_func_to_llvm("jl_f_sizeof", &jl_f_sizeof, m); - builtin_func_map[jl_f_issubtype] = jlcall_func_to_llvm("jl_f_issubtype", &jl_f_issubtype, m); - builtin_func_map[jl_f_isa] = jlcall_func_to_llvm("jl_f_isa", &jl_f_isa, m); - builtin_func_map[jl_f_typeassert] = jlcall_func_to_llvm("jl_f_typeassert", &jl_f_typeassert, m); - builtin_func_map[jl_f_ifelse] = jlcall_func_to_llvm("jl_f_ifelse", &jl_f_ifelse, m); - builtin_func_map[jl_f__apply] = jlcall_func_to_llvm("jl_f__apply", &jl_f__apply, m); - builtin_func_map[jl_f__apply_iterate] = jlcall_func_to_llvm("jl_f__apply_iterate", &jl_f__apply_iterate, m); - builtin_func_map[jl_f__apply_pure] = jlcall_func_to_llvm("jl_f__apply_pure", &jl_f__apply_pure, m); - builtin_func_map[jl_f__apply_latest] = jlcall_func_to_llvm("jl_f__apply_latest", &jl_f__apply_latest, m); - builtin_func_map[jl_f_throw] = jlcall_func_to_llvm("jl_f_throw", &jl_f_throw, m); - builtin_func_map[jl_f_tuple] = jlcall_func_to_llvm("jl_f_tuple", &jl_f_tuple, m); - builtin_func_map[jl_f_svec] = jlcall_func_to_llvm("jl_f_svec", &jl_f_svec, m); - builtin_func_map[jl_f_applicable] = jlcall_func_to_llvm("jl_f_applicable", &jl_f_applicable, m); - builtin_func_map[jl_f_invoke] = jlcall_func_to_llvm("jl_f_invoke", &jl_f_invoke, m); - builtin_func_map[jl_f_invoke_kwsorter] = jlcall_func_to_llvm("jl_f_invoke_kwsorter", &jl_f_invoke_kwsorter, m); - builtin_func_map[jl_f_isdefined] = jlcall_func_to_llvm("jl_f_isdefined", &jl_f_isdefined, m); - builtin_func_map[jl_f_getfield] = jlcall_func_to_llvm("jl_f_getfield", &jl_f_getfield, m); - builtin_func_map[jl_f_setfield] = jlcall_func_to_llvm("jl_f_setfield", &jl_f_setfield, m); - builtin_func_map[jl_f_fieldtype] = jlcall_func_to_llvm("jl_f_fieldtype", &jl_f_fieldtype, m); - builtin_func_map[jl_f_nfields] = jlcall_func_to_llvm("jl_f_nfields", &jl_f_nfields, m); - builtin_func_map[jl_f__expr] = jlcall_func_to_llvm("jl_f__expr", &jl_f__expr, m); - builtin_func_map[jl_f__typevar] = jlcall_func_to_llvm("jl_f__typevar", &jl_f__typevar, m); - builtin_func_map[jl_f_arrayref] = jlcall_func_to_llvm("jl_f_arrayref", &jl_f_arrayref, m); - builtin_func_map[jl_f_const_arrayref] = jlcall_func_to_llvm("jl_f_const_arrayref", &jl_f_arrayref, m); - builtin_func_map[jl_f_arrayset] = jlcall_func_to_llvm("jl_f_arrayset", &jl_f_arrayset, m); - builtin_func_map[jl_f_arraysize] = jlcall_func_to_llvm("jl_f_arraysize", &jl_f_arraysize, m); - builtin_func_map[jl_f_apply_type] = jlcall_func_to_llvm("jl_f_apply_type", &jl_f_apply_type, m); - jltuple_func = builtin_func_map[jl_f_tuple]; - jlgetfield_func = builtin_func_map[jl_f_getfield]; - - jlapplygeneric_func = Function::Create(jl_func_sig, - Function::ExternalLinkage, - "jl_apply_generic", m); - add_return_attr(jlapplygeneric_func, Attribute::NonNull); - jlapplygeneric_func->addFnAttr(Thunk); + for (auto it : builtin_func_map) + add_named_global(it.second, it.first); add_named_global(jlapplygeneric_func, &jl_apply_generic); - - std::vector invokeargs(0); - invokeargs.push_back(T_prjlvalue); - invokeargs.push_back(T_pprjlvalue); - invokeargs.push_back(T_uint32); - invokeargs.push_back(T_prjlvalue); - jlinvoke_func = Function::Create(FunctionType::get(T_prjlvalue, invokeargs, false), - Function::ExternalLinkage, - "jl_invoke", m); - add_return_attr(jlinvoke_func, Attribute::NonNull); - jlinvoke_func->addAttribute(2, Attribute::ReadOnly); - jlinvoke_func->addAttribute(2, Attribute::NoCapture); add_named_global(jlinvoke_func, &jl_invoke); - - std::vector exp_args(0); - exp_args.push_back(T_int1); - expect_func = Intrinsic::getDeclaration(m, Intrinsic::expect, exp_args); - - std::vector args_topeval(0); - args_topeval.push_back(T_pjlvalue); - args_topeval.push_back(T_pjlvalue); - jltopeval_func = - Function::Create(FunctionType::get(T_pjlvalue, args_topeval, false), - Function::ExternalLinkage, - "jl_toplevel_eval", m); - add_return_attr(jltopeval_func, Attribute::NonNull); add_named_global(jltopeval_func, &jl_toplevel_eval); - - std::vector args_copyast(0); - args_copyast.push_back(T_prjlvalue); - jlcopyast_func = - Function::Create(FunctionType::get(T_prjlvalue, args_copyast, false), - Function::ExternalLinkage, - "jl_copy_ast", m); - add_return_attr(jlcopyast_func, Attribute::NonNull); add_named_global(jlcopyast_func, &jl_copy_ast); - - std::vector args5(0); - args5.push_back(T_size); - jlnsvec_func = - Function::Create(FunctionType::get(T_pjlvalue, args5, true), - Function::ExternalLinkage, - "jl_svec", m); - add_return_attr(jlnsvec_func, Attribute::NonNull); - add_named_global(jlnsvec_func, &jl_svec); - - std::vector mdargs(0); - mdargs.push_back(T_prjlvalue); - mdargs.push_back(T_prjlvalue); - mdargs.push_back(T_pjlvalue); - jlmethod_func = - Function::Create(FunctionType::get(T_void, mdargs, false), - Function::ExternalLinkage, - "jl_method_def", m); + //add_named_global(jlnsvec_func, &jl_svec); add_named_global(jlmethod_func, &jl_method_def); - - std::vector funcdefargs(0); - funcdefargs.push_back(T_pjlvalue); - funcdefargs.push_back(T_pjlvalue); - funcdefargs.push_back(T_pprjlvalue); - funcdefargs.push_back(T_pjlvalue); - funcdefargs.push_back(T_pjlvalue); - jlgenericfunction_func = - Function::Create(FunctionType::get(T_prjlvalue, funcdefargs, false), - Function::ExternalLinkage, - "jl_generic_function_def", m); add_named_global(jlgenericfunction_func, &jl_generic_function_def); - - std::vector ehargs(0); - ehargs.push_back(T_pint8); - jlenter_func = - Function::Create(FunctionType::get(T_void, ehargs, false), - Function::ExternalLinkage, - "jl_enter_handler", m); add_named_global(jlenter_func, &jl_enter_handler); - - jl_current_exception_func = - Function::Create(FunctionType::get(T_prjlvalue, false), - Function::ExternalLinkage, - "jl_current_exception", m); add_named_global(jl_current_exception_func, &jl_current_exception); - -#ifdef _OS_WINDOWS_ -#ifndef FORCE_ELF -#if defined(_CPU_X86_64_) -#if defined(_COMPILER_GCC_) - Function *chkstk_func = Function::Create(FunctionType::get(T_void, false), - Function::ExternalLinkage, "___chkstk_ms", m); - add_named_global(chkstk_func, &___chkstk_ms, /*dllimport*/false); -#else - Function *chkstk_func = Function::Create(FunctionType::get(T_void, false), - Function::ExternalLinkage, "__chkstk", m); - add_named_global(chkstk_func, &__chkstk, /*dllimport*/false); -#endif -#else -#if defined(_COMPILER_GCC_) - Function *chkstk_func = Function::Create(FunctionType::get(T_void, false), - Function::ExternalLinkage, "_alloca", m); - add_named_global(chkstk_func, &_alloca, /*dllimport*/false); -#else - Function *chkstk_func = Function::Create(FunctionType::get(T_void, false), - Function::ExternalLinkage, "_chkstk", m); - add_named_global(chkstk_func, &_chkstk, /*dllimport*/false); -#endif -#endif -#endif -#endif - - std::vector lhargs(0); - lhargs.push_back(T_int32); - jlleave_func = - Function::Create(FunctionType::get(T_void, lhargs, false), - Function::ExternalLinkage, - "jl_pop_handler", m); add_named_global(jlleave_func, &jl_pop_handler); - - jl_restore_excstack_func = - Function::Create(FunctionType::get(T_void, T_size, false), - Function::ExternalLinkage, - "jl_restore_excstack", m); add_named_global(jl_restore_excstack_func, &jl_restore_excstack); - - jl_excstack_state_func = - Function::Create(FunctionType::get(T_size, false), - Function::ExternalLinkage, - "jl_excstack_state", m); add_named_global(jl_excstack_state_func, &jl_excstack_state); - - std::vector args_2vals_callee_rooted(0); - args_2vals_callee_rooted.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - args_2vals_callee_rooted.push_back(PointerType::get(T_jlvalue, AddressSpace::CalleeRooted)); - jlegal_func = - Function::Create(FunctionType::get(T_int32, args_2vals_callee_rooted, false), - Function::ExternalLinkage, - "jl_egal", m); add_named_global(jlegal_func, &jl_egal); - - std::vector args_2vals_tracked(0); - args_2vals_tracked.push_back(T_prjlvalue); - args_2vals_tracked.push_back(T_prjlvalue); - jlisa_func = - Function::Create(FunctionType::get(T_int32, args_2vals_tracked, false), - Function::ExternalLinkage, - "jl_isa", m); add_named_global(jlisa_func, &jl_isa); - - jlsubtype_func = - Function::Create(FunctionType::get(T_int32, args_2vals_tracked, false), - Function::ExternalLinkage, - "jl_subtype", m); add_named_global(jlsubtype_func, &jl_subtype); - - jltypeassert_func = Function::Create(FunctionType::get(T_void, args_2vals_tracked, false), - Function::ExternalLinkage, - "jl_typeassert", m); add_named_global(jltypeassert_func, &jl_typeassert); - - std::vector applytype_args(0); - applytype_args.push_back(T_pjlvalue); - applytype_args.push_back(T_pjlvalue); - applytype_args.push_back(T_pprjlvalue); - jlapplytype_func = - Function::Create(FunctionType::get(T_prjlvalue, applytype_args, false), - Function::ExternalLinkage, - "jl_instantiate_type_in_env", m); - add_return_attr(jlapplytype_func, Attribute::NonNull); add_named_global(jlapplytype_func, &jl_instantiate_type_in_env); - - std::vector objectid__args(0); - objectid__args.push_back(T_prjlvalue); - objectid__args.push_back(T_pint8_derived); - jl_object_id__func = - Function::Create(FunctionType::get(T_size, objectid__args, false), - Function::ExternalLinkage, - "jl_object_id_", m); add_named_global(jl_object_id__func, &jl_object_id_); - - std::vector gc_alloc_args(0); - gc_alloc_args.push_back(T_pint8); - gc_alloc_args.push_back(T_size); - gc_alloc_args.push_back(T_prjlvalue); - jl_alloc_obj_func = Function::Create(FunctionType::get(T_prjlvalue, gc_alloc_args, false), - Function::ExternalLinkage, - "julia.gc_alloc_obj"); - add_return_attr(jl_alloc_obj_func, Attribute::NoAlias); - add_return_attr(jl_alloc_obj_func, Attribute::NonNull); - jl_alloc_obj_func->addFnAttr(Attribute::getWithAllocSizeArgs(jl_LLVMContext, 1, None)); // returns %1 bytes - add_named_global(jl_alloc_obj_func, (void*)NULL, /*dllimport*/false); - - std::vector newbits_args(0); - newbits_args.push_back(T_prjlvalue); - newbits_args.push_back(T_pint8); - jl_newbits_func = Function::Create(FunctionType::get(T_prjlvalue, newbits_args, false), - Function::ExternalLinkage, - "jl_new_bits"); - add_return_attr(jl_newbits_func, Attribute::NoAlias); - add_return_attr(jl_newbits_func, Attribute::NonNull); + add_named_global(jl_alloc_obj_func, (void*)NULL); add_named_global(jl_newbits_func, (void*)jl_new_bits); - - jl_loopinfo_marker_func = Function::Create(FunctionType::get(T_void, {}, false), - Function::ExternalLinkage, - "julia.loopinfo_marker"); - jl_loopinfo_marker_func->addFnAttr(Attribute::NoUnwind); - jl_loopinfo_marker_func->addFnAttr(Attribute::NoRecurse); - jl_loopinfo_marker_func->addFnAttr(Attribute::InaccessibleMemOnly); - - jl_typeof_func = Function::Create(FunctionType::get(T_prjlvalue, {T_prjlvalue}, false), - Function::ExternalLinkage, - "julia.typeof"); - jl_typeof_func->addFnAttr(Attribute::ReadOnly); - jl_typeof_func->addFnAttr(Attribute::NoUnwind); - jl_typeof_func->addFnAttr(Attribute::ArgMemOnly); - jl_typeof_func->addFnAttr(Attribute::NoRecurse); - add_return_attr(jl_typeof_func, Attribute::NonNull); - add_named_global(jl_typeof_func, (void*)NULL, /*dllimport*/false); - - jl_write_barrier_func = Function::Create(FunctionType::get(T_void, {T_prjlvalue,}, true), - Function::ExternalLinkage, - "julia.write_barrier"); - jl_write_barrier_func->addFnAttr(Attribute::InaccessibleMemOnly); - jl_write_barrier_func->addFnAttr(Attribute::NoUnwind); - jl_write_barrier_func->addFnAttr(Attribute::NoRecurse); - add_named_global(jl_write_barrier_func, (void*)NULL, /*dllimport*/false); - - std::vector dlsym_args(0); - dlsym_args.push_back(T_pint8); - dlsym_args.push_back(T_pint8); - dlsym_args.push_back(PointerType::get(T_pint8,0)); - jldlsym_func = - Function::Create(FunctionType::get(T_pvoidfunc, dlsym_args, false), - Function::ExternalLinkage, - "jl_load_and_lookup", m); + add_named_global(jl_loopinfo_marker_func, (void*)NULL); + add_named_global(jl_typeof_func, (void*)NULL); + add_named_global(jl_write_barrier_func, (void*)NULL); add_named_global(jldlsym_func, &jl_load_and_lookup); - - std::vector getcfunctiontrampoline_args(0); - getcfunctiontrampoline_args.push_back(T_prjlvalue); // f (object) - getcfunctiontrampoline_args.push_back(T_pjlvalue); // result - getcfunctiontrampoline_args.push_back(T_pint8); // cache - getcfunctiontrampoline_args.push_back(T_pjlvalue); // fill - getcfunctiontrampoline_args.push_back(FunctionType::get(T_pint8, { T_pint8, T_ppjlvalue }, false)->getPointerTo()); // trampoline - getcfunctiontrampoline_args.push_back(T_pjlvalue); // env - getcfunctiontrampoline_args.push_back(T_pprjlvalue); // vals - jlgetcfunctiontrampoline_func = - Function::Create(FunctionType::get(T_prjlvalue, getcfunctiontrampoline_args, false), - Function::ExternalLinkage, - "jl_get_cfunction_trampoline", m); - add_return_attr(jlgetcfunctiontrampoline_func, Attribute::NonNull); add_named_global(jlgetcfunctiontrampoline_func, &jl_get_cfunction_trampoline); - - std::vector getnthfld_args(0); - getnthfld_args.push_back(T_prjlvalue); - getnthfld_args.push_back(T_size); - jlgetnthfieldchecked_func = - Function::Create(FunctionType::get(T_prjlvalue, getnthfld_args, false), - Function::ExternalLinkage, - "jl_get_nth_field_checked", m); - add_return_attr(jlgetnthfieldchecked_func, Attribute::NonNull); add_named_global(jlgetnthfieldchecked_func, &jl_get_nth_field_checked); - - diff_gc_total_bytes_func = - Function::Create(FunctionType::get(T_int64, false), - Function::ExternalLinkage, - "jl_gc_diff_total_bytes", m); add_named_global(diff_gc_total_bytes_func, &jl_gc_diff_total_bytes); - - sync_gc_total_bytes_func = - Function::Create(FunctionType::get(T_int64, {T_int64}, false), - Function::ExternalLinkage, - "jl_gc_sync_total_bytes", m); add_named_global(sync_gc_total_bytes_func, &jl_gc_sync_total_bytes); - - - std::vector array_owner_args(0); - array_owner_args.push_back(T_prjlvalue); - jlarray_data_owner_func = - Function::Create(FunctionType::get(T_prjlvalue, array_owner_args, false), - Function::ExternalLinkage, - "jl_array_data_owner", m); - jlarray_data_owner_func->addFnAttr(Attribute::ReadOnly); - jlarray_data_owner_func->addFnAttr(Attribute::NoUnwind); - add_return_attr(jlarray_data_owner_func, Attribute::NonNull); add_named_global(jlarray_data_owner_func, &jl_array_data_owner); + add_named_global(gcroot_flush_func, (void*)NULL); + add_named_global(gc_preserve_begin_func, (void*)NULL); + add_named_global(gc_preserve_end_func, (void*)NULL); + add_named_global(pointer_from_objref_func, (void*)NULL); + add_named_global(except_enter_func, (void*)NULL); - gcroot_flush_func = Function::Create(FunctionType::get(T_void, false), - Function::ExternalLinkage, - "julia.gcroot_flush"); - add_named_global(gcroot_flush_func, (void*)NULL, /*dllimport*/false); - - gc_preserve_begin_func = Function::Create(FunctionType::get(Type::getTokenTy(jl_LLVMContext), - ArrayRef(), true), - Function::ExternalLinkage, - "llvm.julia.gc_preserve_begin"); - add_named_global(gc_preserve_begin_func, (void*)NULL, /*dllimport*/false); - - gc_preserve_end_func = Function::Create(FunctionType::get(T_void, - ArrayRef(Type::getTokenTy(jl_LLVMContext)), false), - Function::ExternalLinkage, - "llvm.julia.gc_preserve_end"); - add_named_global(gc_preserve_end_func, (void*)NULL, /*dllimport*/false); - - pointer_from_objref_func = Function::Create(FunctionType::get(T_pjlvalue, - ArrayRef(PointerType::get(T_jlvalue, AddressSpace::Derived)), false), - Function::ExternalLinkage, - "julia.pointer_from_objref"); - pointer_from_objref_func->addFnAttr(Attribute::ReadNone); - pointer_from_objref_func->addFnAttr(Attribute::NoUnwind); - add_named_global(pointer_from_objref_func, (void*)NULL, /*dllimport*/false); - - except_enter_func = Function::Create(FunctionType::get(T_int32, false), - Function::ExternalLinkage, - "julia.except_enter"); - except_enter_func->addFnAttr(Attribute::ReturnsTwice); - add_named_global(except_enter_func, (void*)NULL, /*dllimport*/false); - - jlgetworld_global = - new GlobalVariable(*m, T_size, - false, GlobalVariable::ExternalLinkage, - NULL, "jl_world_counter"); - add_named_global(jlgetworld_global, &jl_world_counter); +#ifdef _OS_WINDOWS_ +#ifndef FORCE_ELF +#if defined(_CPU_X86_64_) +#if defined(_COMPILER_GCC_) + add_named_global("___chkstk_ms", &___chkstk_ms); +#else + add_named_global("__chkstk", &__chkstk); +#endif +#else +#if defined(_COMPILER_GCC_) + add_named_global("_alloca", &_alloca); +#else + add_named_global("_chkstk", &_chkstk); +#endif +#endif +#endif +#endif + +#define BOX_F(ct) add_named_global("jl_box_"#ct, &jl_box_##ct); + BOX_F(int8); BOX_F(uint8); + BOX_F(int16); BOX_F(uint16); + BOX_F(int32); BOX_F(uint32); + BOX_F(int64); BOX_F(uint64); + BOX_F(float32); BOX_F(float64); + BOX_F(char); BOX_F(ssavalue); +#undef BOX_F } extern "C" void jl_init_llvm(void) @@ -7527,19 +7554,13 @@ extern "C" void jl_init_codegen(void) jl_init_llvm(); // Now that the execution engine exists, initialize all modules jl_init_jit(); + init_jit_functions(); + Module *m = new Module("julia", jl_LLVMContext); jl_setup_module(m); init_julia_llvm_env(m); - SBOX_F_PERM(int8,int8); UBOX_F_PERM(uint8,uint8); - SBOX_F(int16,int16); UBOX_F(uint16,uint16); - SBOX_F(int32,int32); UBOX_F(uint32,uint32); - SBOX_F(int64,int64); UBOX_F(uint64,uint64); - BOX_F(float32,float32,T_prjlvalue); BOX_F(float64,float64,T_prjlvalue); - UBOX_F(char,char); - UBOX_F(ssavalue,size); - - jl_init_intrinsic_functions_codegen(m); + jl_init_intrinsic_functions_codegen(); } extern "C" void jl_teardown_codegen() diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 9cff44db3190b..28e06d72ee1d1 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -7,32 +7,28 @@ namespace JL_I { #include "ccall.cpp" using namespace JL_I; -static Function *runtime_func[num_intrinsics]; + +FunctionType *get_intr_args1(LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue}, false); } +FunctionType *get_intr_args2(LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_prjlvalue}, false); } +FunctionType *get_intr_args3(LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_prjlvalue, T_prjlvalue}, false); } +FunctionType *get_intr_args4(LLVMContext &C) { return FunctionType::get(T_prjlvalue, {T_prjlvalue, T_prjlvalue, T_prjlvalue, T_prjlvalue}, false); } + +static JuliaFunction *runtime_func[num_intrinsics] = { +#define ADD_I(name, nargs) new JuliaFunction{"jl_"#name, get_intr_args##nargs}, +#define ADD_HIDDEN ADD_I +#define ALIAS(alias, base) nullptr, + INTRINSICS +#undef ADD_I +#undef ADD_HIDDEN +#undef ALIAS +}; + static bool float_func[num_intrinsics]; -static void jl_init_intrinsic_functions_codegen(Module *m) + +static void jl_init_intrinsic_functions_codegen(void) { - std::vector args1(0); \ - args1.push_back(T_prjlvalue); \ - std::vector args2(0); \ - args2.push_back(T_prjlvalue); \ - args2.push_back(T_prjlvalue); \ - std::vector args3(0); \ - args3.push_back(T_prjlvalue); \ - args3.push_back(T_prjlvalue); \ - args3.push_back(T_prjlvalue); \ - std::vector args4(0); \ - args4.push_back(T_prjlvalue); \ - args4.push_back(T_prjlvalue); \ - args4.push_back(T_prjlvalue); \ - args4.push_back(T_prjlvalue); - -#define ADD_I(name, nargs) do { \ - Function *func = Function::Create(FunctionType::get(T_prjlvalue, args##nargs, false), \ - Function::ExternalLinkage, "jl_"#name, m); \ - runtime_func[name] = func; \ - add_named_global(func, &jl_##name); \ - } while (0); -#define ADD_HIDDEN ADD_I +#define ADD_I(name, nargs) +#define ADD_HIDDEN(name, nargs) #define ALIAS(alias, base) runtime_func[alias] = runtime_func[base]; INTRINSICS #undef ADD_I @@ -414,7 +410,7 @@ static jl_value_t *staticeval_bitstype(const jl_cgval_t &targ) static jl_cgval_t emit_runtime_call(jl_codectx_t &ctx, JL_I::intrinsic f, const jl_cgval_t *argv, size_t nargs) { - FunctionCallee func = prepare_call(runtime_func[f]); + Function *func = prepare_call(runtime_func[f]); Value **argvalues = (Value**)alloca(sizeof(Value*) * nargs); for (size_t i = 0; i < nargs; ++i) { argvalues[i] = boxed(ctx, argv[i]); @@ -1284,27 +1280,3 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg } assert(0 && "unreachable"); } - -#define BOX_F(ct,jl_ct,rt) \ - box_##ct##_func = boxfunc_llvm(ft1arg(rt, T_##jl_ct), \ - "jl_box_"#ct, &jl_box_##ct, m); - -#define SBOX_F(ct,jl_ct) BOX_F(ct,jl_ct,T_prjlvalue); box_##ct##_func->addAttribute(1, Attribute::SExt); -#define UBOX_F(ct,jl_ct) BOX_F(ct,jl_ct,T_prjlvalue); box_##ct##_func->addAttribute(1, Attribute::ZExt); -#define SBOX_F_PERM(ct,jl_ct) BOX_F(ct,jl_ct,T_pjlvalue); box_##ct##_func->addAttribute(1, Attribute::SExt); -#define UBOX_F_PERM(ct,jl_ct) BOX_F(ct,jl_ct,T_pjlvalue); box_##ct##_func->addAttribute(1, Attribute::ZExt); - -template -static Function *boxfunc_llvm(FunctionType *ft, const std::string &cname, - T *addr, Module *m) -{ - Function *f = - Function::Create(ft, Function::ExternalLinkage, cname, m); - add_named_global(f, addr); - return f; -} - -static FunctionType *ft1arg(Type *ret, Type *arg) -{ - return FunctionType::get(ret, { arg }, false); -} diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index af8364af25844..b7c4a78b2ddca 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -618,16 +618,12 @@ JuliaOJIT::JuliaOJIT(TargetMachine &TM) void JuliaOJIT::addGlobalMapping(StringRef Name, uint64_t Addr) { - bool successful = GlobalSymbolTable.insert(std::make_pair(Name, (void*)Addr)).second; + std::string MangleName = getMangledName(Name); + bool successful = GlobalSymbolTable.insert(std::make_pair(MangleName, (void*)Addr)).second; (void)successful; assert(successful); } -void JuliaOJIT::addGlobalMapping(const GlobalValue *GV, void *Addr) -{ - addGlobalMapping(getMangledName(GV), (uintptr_t)Addr); -} - void *JuliaOJIT::getPointerToGlobalIfAvailable(StringRef S) { SymbolTableT::const_iterator pos = GlobalSymbolTable.find(S); @@ -991,16 +987,7 @@ static uint64_t getAddressForFunction(StringRef fname) } // helper function for adding a DLLImport (dlsym) address to the execution engine -void add_named_global(GlobalObject *gv, void *addr, bool dllimport) +void add_named_global(StringRef name, void *addr) { -#ifdef _OS_WINDOWS_ - // setting JL_DLLEXPORT correctly only matters when building a binary - if (dllimport && imaging_mode) { - assert(gv->getLinkage() == GlobalValue::ExternalLinkage); - // add the __declspec(dllimport) attribute - gv->setDLLStorageClass(GlobalValue::DLLImportStorageClass); - } -#endif // _OS_WINDOWS_ - - jl_ExecutionEngine->addGlobalMapping(gv, addr); + jl_ExecutionEngine->addGlobalMapping(name, (uint64_t)(uintptr_t)addr); } diff --git a/src/jitlayers.h b/src/jitlayers.h index ed957ad44b588..91ced69b210a2 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -28,6 +28,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, bool lowe void jl_finalize_module(std::unique_ptr m); void jl_merge_module(Module *dest, std::unique_ptr src); Module *jl_create_llvm_module(StringRef name); +GlobalVariable *jl_emit_RTLD_DEFAULT_var(Module *M); typedef struct _jl_llvm_functions_t { std::string functionObject; // jlcall llvm Function name @@ -113,33 +114,7 @@ void jl_compile_workqueue( Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_tupletype_t *argt, jl_codegen_params_t ¶ms); -static inline GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G) -{ - if (G->getParent() == M) - return G; - GlobalValue *local = M->getNamedValue(G->getName()); - if (!local) { - // Copy the GlobalVariable, but without the initializer, so it becomes a declaration - GlobalVariable *proto = new GlobalVariable(*M, G->getType()->getElementType(), - G->isConstant(), GlobalVariable::ExternalLinkage, - nullptr, G->getName(), nullptr, G->getThreadLocalMode()); - proto->copyAttributesFrom(G); - // DLLImport only needs to be set for the shadow module - // it just gets annoying in the JIT - proto->setDLLStorageClass(GlobalValue::DefaultStorageClass); - return proto; - } - return cast(local); -} - -void add_named_global(GlobalObject *gv, void *addr, bool dllimport); -template -static inline void add_named_global(GlobalObject *gv, T *addr, bool dllimport = true) -{ - // cast through integer to avoid c++ pedantic warning about casting between - // data and code pointers - add_named_global(gv, (void*)(uintptr_t)addr, dllimport); -} +void add_named_global(StringRef name, void *addr); static inline Constant *literal_static_pointer_val(const void *p, Type *T) { @@ -212,7 +187,6 @@ class JuliaOJIT { const object::ObjectFile &Obj, const RuntimeDyld::LoadedObjectInfo &LoadedObjectInfo); void addGlobalMapping(StringRef Name, uint64_t Addr); - void addGlobalMapping(const GlobalValue *GV, void *Addr); void *getPointerToGlobalIfAvailable(StringRef S); void *getPointerToGlobalIfAvailable(const GlobalValue *GV); void addModule(std::unique_ptr M); diff --git a/src/julia_internal.h b/src/julia_internal.h index 61123c4b4423d..7c2fe499de5a8 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -636,7 +636,6 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_value_t *caller); uint32_t jl_module_next_counter(jl_module_t *m); -void jl_fptr_to_llvm(void *fptr, jl_code_instance_t *codeinst, int spec_abi); jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs); int jl_has_meta(jl_array_t *body, jl_sym_t *sym); diff --git a/src/staticdata.c b/src/staticdata.c index 6f436fcdc339c..4211df93d4f7f 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1189,7 +1189,6 @@ static void jl_update_all_fptrs(jl_serializer_state *s) else { codeinst->invoke = (jl_callptr_t)fptr; } - jl_fptr_to_llvm(fptr, codeinst, specfunc); } } jl_register_fptrs(sysimage_base, &fvars, linfos, sysimg_fvars_max); From 43fd66c8ac9010a193fbcf870ce731b99dd38b4f Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Wed, 17 Jun 2020 14:13:40 -0400 Subject: [PATCH 184/232] Remove NEWS.md from the PDF of the manual (#36312) * Partial fix for #35495 This removes the NEWS.md link at the bottom of the first page of the manual. Given that we link to it prominently on top of the page, I feel this is ok. This will remove Section II of the PDF manual, where the release notes take 15 pages (because ofthe way markdown gets converted to PDF). * Better layout for the generated PDF --- doc/make.jl | 214 ++++++++++++++++++++++++++++------------------------ 1 file changed, 116 insertions(+), 98 deletions(-) diff --git a/doc/make.jl b/doc/make.jl index 15e381d7bed61..35a252da1a7b7 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -41,6 +41,9 @@ cd(joinpath(@__DIR__, "src")) do end end +# Check if we are building a PDF +const render_pdf = "pdf" in ARGS + # Generate a suitable markdown file from NEWS.md and put it in src str = read(joinpath(@__DIR__, "..", "NEWS.md"), String) splitted = split(str, "") @@ -48,106 +51,122 @@ splitted = split(str, "") replaced_links = replace(splitted[1], r"\[\#([0-9]*?)\]" => s"[#\g<1>](https://github.com/JuliaLang/julia/issues/\g<1>)") write(joinpath(@__DIR__, "src", "NEWS.md"), replaced_links) -const PAGES = [ - "Home" => "index.md", - hide("NEWS.md"), - "Manual" => [ - "manual/getting-started.md", - "manual/variables.md", - "manual/integers-and-floating-point-numbers.md", - "manual/mathematical-operations.md", - "manual/complex-and-rational-numbers.md", - "manual/strings.md", - "manual/functions.md", - "manual/control-flow.md", - "manual/variables-and-scoping.md", - "manual/types.md", - "manual/methods.md", - "manual/constructors.md", - "manual/conversion-and-promotion.md", - "manual/interfaces.md", - "manual/modules.md", - "manual/documentation.md", - "manual/metaprogramming.md", - "manual/arrays.md", - "manual/missing.md", - "manual/networking-and-streams.md", - "manual/parallel-computing.md", - "manual/asynchronous-programming.md", - "manual/multi-threading.md", - "manual/distributed-computing.md", - "manual/running-external-programs.md", - "manual/calling-c-and-fortran-code.md", - "manual/handling-operating-system-variation.md", - "manual/environment-variables.md", - "manual/embedding.md", - "manual/code-loading.md", - "manual/profile.md", - "manual/stacktraces.md", - "manual/performance-tips.md", - "manual/workflow-tips.md", - "manual/style-guide.md", - "manual/faq.md", - "manual/noteworthy-differences.md", - "manual/unicode-input.md", - ], - "Base" => [ - "base/base.md", - "base/collections.md", - "base/math.md", - "base/numbers.md", - "base/strings.md", - "base/arrays.md", - "base/parallel.md", - "base/multi-threading.md", - "base/constants.md", - "base/file.md", - "base/io-network.md", - "base/punctuation.md", - "base/sort.md", - "base/iterators.md", - "base/c.md", - "base/libc.md", - "base/stacktraces.md", - "base/simd-types.md", - ], - "Standard Library" => - [stdlib.targetfile for stdlib in STDLIB_DOCS], - "Developer Documentation" => [ - "devdocs/reflection.md", - "Documentation of Julia's Internals" => [ - "devdocs/init.md", - "devdocs/ast.md", - "devdocs/types.md", - "devdocs/object.md", - "devdocs/eval.md", - "devdocs/callconv.md", - "devdocs/compiler.md", - "devdocs/functions.md", - "devdocs/cartesian.md", - "devdocs/meta.md", - "devdocs/subarrays.md", - "devdocs/isbitsunionarrays.md", - "devdocs/sysimg.md", - "devdocs/llvm.md", - "devdocs/stdio.md", - "devdocs/boundscheck.md", - "devdocs/locks.md", - "devdocs/offset-arrays.md", - "devdocs/require.md", - "devdocs/inference.md", - "devdocs/ssair.md", - "devdocs/gc-sa.md", - ], - "Developing/debugging Julia's C code" => [ - "devdocs/backtraces.md", - "devdocs/debuggingtips.md", - "devdocs/valgrind.md", - "devdocs/sanitizers.md", - ] +Manual = [ + "manual/getting-started.md", + "manual/variables.md", + "manual/integers-and-floating-point-numbers.md", + "manual/mathematical-operations.md", + "manual/complex-and-rational-numbers.md", + "manual/strings.md", + "manual/functions.md", + "manual/control-flow.md", + "manual/variables-and-scoping.md", + "manual/types.md", + "manual/methods.md", + "manual/constructors.md", + "manual/conversion-and-promotion.md", + "manual/interfaces.md", + "manual/modules.md", + "manual/documentation.md", + "manual/metaprogramming.md", + "manual/arrays.md", + "manual/missing.md", + "manual/networking-and-streams.md", + "manual/parallel-computing.md", + "manual/asynchronous-programming.md", + "manual/multi-threading.md", + "manual/distributed-computing.md", + "manual/running-external-programs.md", + "manual/calling-c-and-fortran-code.md", + "manual/handling-operating-system-variation.md", + "manual/environment-variables.md", + "manual/embedding.md", + "manual/code-loading.md", + "manual/profile.md", + "manual/stacktraces.md", + "manual/performance-tips.md", + "manual/workflow-tips.md", + "manual/style-guide.md", + "manual/faq.md", + "manual/noteworthy-differences.md", + "manual/unicode-input.md", +] + +BaseDocs = [ + "base/base.md", + "base/collections.md", + "base/math.md", + "base/numbers.md", + "base/strings.md", + "base/arrays.md", + "base/parallel.md", + "base/multi-threading.md", + "base/constants.md", + "base/file.md", + "base/io-network.md", + "base/punctuation.md", + "base/sort.md", + "base/iterators.md", + "base/c.md", + "base/libc.md", + "base/stacktraces.md", + "base/simd-types.md", +] + +StdlibDocs = [stdlib.targetfile for stdlib in STDLIB_DOCS] + +DevDocs = [ + "devdocs/reflection.md", + "Documentation of Julia's Internals" => [ + "devdocs/init.md", + "devdocs/ast.md", + "devdocs/types.md", + "devdocs/object.md", + "devdocs/eval.md", + "devdocs/callconv.md", + "devdocs/compiler.md", + "devdocs/functions.md", + "devdocs/cartesian.md", + "devdocs/meta.md", + "devdocs/subarrays.md", + "devdocs/isbitsunionarrays.md", + "devdocs/sysimg.md", + "devdocs/llvm.md", + "devdocs/stdio.md", + "devdocs/boundscheck.md", + "devdocs/locks.md", + "devdocs/offset-arrays.md", + "devdocs/require.md", + "devdocs/inference.md", + "devdocs/ssair.md", + "devdocs/gc-sa.md", ], + "Developing/debugging Julia's C code" => [ + "devdocs/backtraces.md", + "devdocs/debuggingtips.md", + "devdocs/valgrind.md", + "devdocs/sanitizers.md", + ] ] + +if render_pdf +const PAGES = [ + "Manual" => ["index.md", Manual...], + "Base" => BaseDocs, + "Standard Library" => StdlibDocs, + "Developer Documentation" => DevDocs +] +else +const PAGES = [ + "Julia Documentation" => "index.md", + "Manual" => Manual, + "Base" => BaseDocs, + "Standard Library" => StdlibDocs, + "Developer Documentation" => DevDocs +] +end + for stdlib in STDLIB_DOCS @eval using $(stdlib.stdlib) # All standard library modules get `using $STDLIB` as their global @@ -161,7 +180,6 @@ DocMeta.setdocmeta!(UUIDs, :DocTestSetup, :(using UUIDs, Random), recursive=true DocMeta.setdocmeta!(Pkg, :DocTestSetup, :(using Pkg, Pkg.Artifacts), recursive=true, warn=false) DocMeta.setdocmeta!(Pkg.BinaryPlatforms, :DocTestSetup, :(using Pkg, Pkg.BinaryPlatforms), recursive=true, warn=false) -const render_pdf = "pdf" in ARGS let r = r"buildroot=(.+)", i = findfirst(x -> occursin(r, x), ARGS) global const buildroot = i === nothing ? (@__DIR__) : first(match(r, ARGS[i]).captures) end From 8ca6e8d25fbb25cc8d72055f5dcfd61b4a758220 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 17 Jun 2020 15:41:33 -0400 Subject: [PATCH 185/232] rearrange `assemble_inline_todo!` to handle union splitting in the outer loop (#35891) --- base/compiler/ssair/inlining.jl | 135 +++++++++++++++++--------------- 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index eb7222496a5f4..d5986c34bee1f 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1023,95 +1023,100 @@ function assemble_inline_todo!(ir::IRCode, sv::OptimizationState) continue end - # Regular case: Retrieve matching methods from cache (or compute them) - (meth, min_valid, max_valid) = get(sv.matching_methods_cache, sig.atype) do - # World age does not need to be taken into account in the cache - # because it is forwarded from type inference through `sv.params` - # in the case that the cache is nonempty, so it should be unchanged - # The max number of methods should be the same as in inference most - # of the time, and should not affect correctness otherwise. - min_val = UInt[typemin(UInt)] - max_val = UInt[typemax(UInt)] - ms = _methods_by_ftype(sig.atype, sv.params.MAX_METHODS, - sv.world, min_val, max_val) - return (ms, min_val[1], max_val[1]) - end - if meth === false || length(meth) == 0 - # No applicable method, or too many applicable methods - continue + nu = countunionsplit(sig.atypes) + if nu == 1 || nu > sv.params.MAX_UNION_SPLITTING + splits = Any[sig.atype] + else + splits = Any[] + for union_sig in UnionSplitSignature(sig.atypes) + push!(splits, argtypes_to_type(union_sig)) + end end - update_valid_age!(min_valid, max_valid, sv) cases = Pair{Any, Any}[] - # TODO: This could be better - signature_union = Union{Any[match[1]::Type for match in meth]...} - signature_fully_covered = sig.atype <: signature_union - fully_covered = signature_fully_covered - split_out_sigs = Any[] - - # For any method match that's a dispatch tuple, extract those cases first - for (i, match) in enumerate(meth) - (metharg, methsp, method) = (match[1]::Type, match[2]::SimpleVector, match[3]::Method) - if !isdispatchtuple(metharg) - fully_covered = false - continue + signature_union = Union{} + only_method = nothing # keep track of whether there is one matching method + too_many = false + local meth + local fully_covered = true + for atype in splits + # Regular case: Retrieve matching methods from cache (or compute them) + (meth, min_valid, max_valid) = get(sv.matching_methods_cache, atype) do + # World age does not need to be taken into account in the cache + # because it is forwarded from type inference through `sv.params` + # in the case that the cache is nonempty, so it should be unchanged + # The max number of methods should be the same as in inference most + # of the time, and should not affect correctness otherwise. + min_val = UInt[typemin(UInt)] + max_val = UInt[typemax(UInt)] + ms = _methods_by_ftype(atype, sv.params.MAX_METHODS, + sv.world, min_val, max_val) + return (ms, min_val[1], max_val[1]) end - case_sig = Signature(sig.f, sig.ft, sig.atypes, metharg) - case = analyze_method!(idx, case_sig, metharg, methsp, method, - stmt, sv, false, nothing, calltype) - if case === nothing - fully_covered = false + if meth === false + # Too many applicable methods + too_many = true + break + elseif length(meth) == 0 + # No applicable methods; try next union split continue + elseif length(meth) == 1 && only_method !== false + if only_method === nothing + only_method = meth[1][3] + elseif only_method !== meth[1][3] + only_method = false + end + else + only_method = false end - push!(cases, Pair{Any,Any}(metharg, case)) - push!(split_out_sigs, metharg) - end + update_valid_age!(min_valid, max_valid, sv) - # Now, if profitable union split the atypes into dispatch tuples and match the appropriate method - nu = countunionsplit(sig.atypes) - if nu != 1 && nu <= sv.params.MAX_UNION_SPLITTING - fully_covered = true - for union_sig in UnionSplitSignature(sig.atypes) - metharg′ = argtypes_to_type(union_sig) - if !isdispatchtuple(metharg′) + for match in meth::Vector{Any} + (metharg, methsp, method) = (match[1]::Type, match[2]::SimpleVector, match[3]::Method) + # TODO: This could be better + signature_union = Union{signature_union, metharg} + if !isdispatchtuple(metharg) fully_covered = false continue - elseif _any(x->x === metharg′, split_out_sigs) - continue - end - # `meth` is in specificity order, so find the first applicable method - found_any = false - for (i, match) in enumerate(meth) - (metharg, methsp, method) = (match[1]::Type, match[2]::SimpleVector, match[3]::Method) - metharg′ <: method.sig || continue - case_sig = Signature(sig.f, sig.ft, sig.atypes, metharg′) - case = analyze_method!(idx, case_sig, metharg′, methsp, method, stmt, sv, false, nothing, - calltype) - if case !== nothing - found_any = true - push!(cases, Pair{Any,Any}(metharg′, case)) - end - break end - if !found_any + case_sig = Signature(sig.f, sig.ft, sig.atypes, metharg) + case = analyze_method!(idx, case_sig, metharg, methsp, method, + stmt, sv, false, nothing, calltype) + if case === nothing fully_covered = false continue + elseif _any(p->p[1] === metharg, cases) + continue end + push!(cases, Pair{Any,Any}(metharg, case)) end end + too_many && continue + + signature_fully_covered = sig.atype <: signature_union # If we're fully covered and there's only one applicable method, # we inline, even if the signature is not a dispatch tuple - if signature_fully_covered && length(cases) == 0 && length(meth) == 1 - metharg = meth[1][1]::Type - methsp = meth[1][2]::SimpleVector - method = meth[1][3]::Method + if signature_fully_covered && length(cases) == 0 && only_method isa Method + if length(splits) > 1 + # get match information for a single overall match instead of union splits + meth = get(sv.matching_methods_cache, sig.atype) do + ms = _methods_by_ftype(sig.atype, sv.params.MAX_METHODS, + sv.world, UInt[typemin(UInt)], UInt[typemin(UInt)]) + return ms + end + @assert length(meth) == 1 + end + (metharg, methsp, method) = (meth[1][1]::Type, meth[1][2]::SimpleVector, meth[1][3]::Method) fully_covered = true case = analyze_method!(idx, sig, metharg, methsp, method, stmt, sv, false, nothing, calltype) case === nothing && continue push!(cases, Pair{Any,Any}(metharg, case)) end + if !signature_fully_covered + fully_covered = false + end # If we only have one case and that case is fully covered, we may either # be able to do the inlining now (for constant cases), or push it directly From 65c09e31d61cda7a05befe06740c7ef7a35a34c6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 17 Jun 2020 17:23:58 -0400 Subject: [PATCH 186/232] Revert "Update to GMP 6.2.0 (#36309)" (#36328) This reverts commit c54a6ea77b8475598744c7a134a04318bdbe5ba6. --- contrib/refresh_bb_tarballs.sh | 4 +- deps/Versions.make | 4 +- .../GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 | 1 + .../GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 | 1 + .../GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 - .../sha512 | 1 - deps/gmp.mk | 10 +- deps/patches/gmp-config-ldflags.patch | 381 ++++++++++++++++++ deps/patches/gmp-exception.patch | 35 ++ 83 files changed, 455 insertions(+), 57 deletions(-) create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 delete mode 100644 deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 create mode 100644 deps/patches/gmp-config-ldflags.patch create mode 100644 deps/patches/gmp-exception.patch diff --git a/contrib/refresh_bb_tarballs.sh b/contrib/refresh_bb_tarballs.sh index c139b38f3557f..5cac80d52fd78 100755 --- a/contrib/refresh_bb_tarballs.sh +++ b/contrib/refresh_bb_tarballs.sh @@ -12,9 +12,9 @@ TRIPLETS="i686-linux-gnu x86_64-linux-gnu aarch64-linux-gnu armv7l-linux-gnueabihf powerpc64le-linux-gnu i686-linux-musl x86_64-linux-musl aarch64-linux-musl armv7l-linux-musleabihf x86_64-apple-darwin14 x86_64-unknown-freebsd11.1 i686-w64-mingw32 x86_64-w64-mingw32" # These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded: -BB_PROJECTS="mbedtls libssh2 mpfr curl libgit2 pcre libuv unwind osxunwind dsfmt objconv p7zip zlib suitesparse openlibm" +BB_PROJECTS="gmp mbedtls libssh2 mpfr curl libgit2 pcre libuv unwind osxunwind dsfmt objconv p7zip zlib suitesparse openlibm" BB_GCC_EXPANDED_PROJECTS="openblas" -BB_CXX_EXPANDED_PROJECTS="gmp llvm" +BB_CXX_EXPANDED_PROJECTS="llvm" # If we've been given a project name, filter down to that one: if [ -n "${1}" ]; then diff --git a/deps/Versions.make b/deps/Versions.make index 49ef420d889ca..a69bffca1b4e1 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -15,8 +15,8 @@ UNWIND_VER = 1.3.1 UNWIND_BB_REL = 4 OSXUNWIND_VER = 0.0.5 OSXUNWIND_BB_REL = 0 -GMP_VER = 6.2.0 -GMP_BB_REL = 0 +GMP_VER = 6.1.2 +GMP_BB_REL = 4 MPFR_VER = 4.0.2 MPFR_BB_REL = 2 PATCHELF_VER = 0.9 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..a33c13e8df4c4 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +fb8136c2a92d37edcd0ba4ae22352b5c diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..7a530e1181850 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9622464c9054371ee1621efe87801259b900e80457aa5cf39d4323e51dec56b7b3527c6fcf9ae74058f2b4db8034a204f71b9a28c02b3975239064261db5c361 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..e0793c221b695 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +f7d1f68ba15b3a5e9996cb349d4d5c58 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..f7483c5f3b67f --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +921723c1e71cc80e1dd9f35f4142ca16be030ab1bd7a3c78c73a63be2d0ed80a342e28376bbc074d7203c97bec6e593b386f41ab25fbf22b8942d9d6818edbea diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000000000..862c586220ed0 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +b7210d1ffa5e517ef0bada8669303fab diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..86f9c96bd1a3e --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +77be741f35248a4c4fda80d5efb3b7b1b4ca3fd4f0fed9e305794b89bbb88c7baf5d1d57747bb8e0047377bf1ccbb00cba1a61df17d1c58df6a9901ca42eb593 diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000000000..fb8e567a332e7 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +cbd71c0e90e4c92381c79045d9310752 diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..c6b1d6ece69da --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +1a8a622746ad1bc7c6576476d7217e383c72771a8b2b3a3c74e5e9d3b0daec1920f85f556339c95857c16ebeb5d5c79669c900cb4b30f68969b9b57146f53b4c diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..c044213f35214 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +96228c26a324e616715ff5116129efbf diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..0a98d76631389 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +dda1eed06658ee175868c80dac0dcc4848a2b179fa6226e5899de13492205e6d509180c65d533a132b46fdbcbcaa49a85df627a637336f5b14291ea747350d84 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..d4061b698772b --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +bcb20e336ed586893a2bd1664bd4af96 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..06cdc0b869a8a --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +e0cfabe7d2b331c3cc693ca50311319737785a087fc84b15f1862c6a60d036dfffed6f4e9a865d0b83fc374cba3b625face165505fd45de78f674bdafb34fbd2 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..37b7334e40f11 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +df89ac295cf9a17e16357e433fd917a9 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..72d168d87424f --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +856cbaf79376e470ec5be0913a37f4ff06827bb6d06fceb462007375bd8d2b430ff8de938d7f3807a6612ba20d57cddfbb7fa52c94d828d62c678f75c1f5cb31 diff --git a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..7b2febc809adc --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +5b232584c835a70ff5d43cfc57ec3438 diff --git a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..ce283f5b9b001 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9ce5e5686bfcb14ca1ae7589cba6beec49215ffa2fe9b179c6afd5300d9da7b5c31133888a29cba923e4d399322b018ffb19d5a2bc096c1577a9e7bc7b8b33de diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000000000..364fe0a283846 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +a0d321bba6e59a449c92088fa693c969 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000000000..bb0ddb68ce569 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +2b9ae682eecf5b5513bf7bc06872bd2bb79ac0d09d1a032217e8727854429e57bb415c5e6a6a04fd7da94cbcb45e3ad8a8578e7b9cf3ba1949db82dd8a9eb6ff diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..bb7bbc3e41a82 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a6fabb3bc108b0eef63c5543d1f13b7d diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..c6cb48ddae0c9 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +cf1fd3a1072f4a5ed3f71db9bf6cab2fa54fab8ae55fcc7bb2d6496e106a6a9a1844f9d8b8d7fdacfcd51e811e168fb99605ebbe1e87e61f978449ad6a280e24 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..2164b3144c461 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +ba1b090802d2636f460ee71253825a4c diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..9f14c65b65f5e --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6320d81bc7d27848606aa7063cf29e1c088f22272db28600a27238a0f933c6695b917ecb54670c93473dbdb7141c7ee33e65b8d717edd0f400966d5a03d5bd86 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000000000..65452b89573ef --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +deb35cf6f3aa221c814a9cb9b77a99d9 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000000000..e92eb52b76acb --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +d80954af671b6c24d8f0988c8cca8e89499dbb7206c8d0cd5c655688df9cc3a6b2e6ca8dbafb86ffaa42707be3e6da44e82c04d8173f7543514dae8dab4d5162 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..9d25f75ef5be6 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +51fb1b1a43cec0ec216c61d17cd637cd diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..7521ace512391 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +9a702da6de85f3a81262858a2299222664417d424d89b1704eca702661f77e27cd07b7fca8fbd77c58521ef9caa83287c05956fbe97d9047272a4cb6912fc80d diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 deleted file mode 100644 index 2a1eab0c936ac..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -20672d3ba36a77d359172cc095f78805 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 deleted file mode 100644 index 4e546d4640aa3..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b2f456fa97f4a68b512c286e89b5cc599f77f7e84956d761a93e6365f3ca425624e1c675f7c8c3132746d2ffcbc785537144401fca8f744869c606a579654512 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 deleted file mode 100644 index 8aece55be2b87..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -460394e5250cc11ba294645d2f1ffeac diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 deleted file mode 100644 index e5e419c1020c0..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-gnu-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6691625b2941ee9ef8fcdcbccd53e161a45cb37cbb3b87e4f40f2ae919189e2b7dcf5c10ffbd271be7d9f9bf60fb4a05e9532e965cac66ccd57836c364e288a2 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 deleted file mode 100644 index 92901a53a42bd..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -dc00a630947a489351d67903f5c12aae diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 deleted file mode 100644 index e1c8ac10fa62a..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8434a751bb6c0e610dda33e37a09a78331f41376df7362192e215d3c328c6730bb138fe8fd773dd907a6bb3b31b5e408eaf0a706f858c6a498b69db0315925c6 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 deleted file mode 100644 index 81c75fd01a9d1..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d36cbb7473f9ce430df233b82b4c14f4 diff --git a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 deleted file mode 100644 index b6b9d88f57942..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.aarch64-linux-musl-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8fcda8c37131f0b44682548eaa4420fe74819fd9ab2ac6448464947c1f4b8328a5856ccd2ef3b8a400dc5e5d861d332354370ce36127bbea9eb6569705095b5b diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 deleted file mode 100644 index faa2bae259fcb..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f540d3dbe282d10469a6358b55d28939 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 deleted file mode 100644 index fd764fcd495f3..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -04a66ec0d96bc3ed35fcaa0830a1592068677b71f0c7c1e1d34e8fa5626f17ee202562510d36ccda1ccc710d85fa1799bbf795d4cd46841e160e2a7920403310 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 deleted file mode 100644 index 30a72c0af3815..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e11d981d3fbcdc0dec57d9e6683096c0 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 deleted file mode 100644 index 154d2fdf4915c..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ddf3d98681ba2d0d886dc0c26a7d916e34ec16e3f1ef75ebdc3ec87a984ea874076af0d20220d85185de0e878f0819d8bb1a7f1ddb1a5f3b53362552f104e5e8 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 deleted file mode 100644 index 061d79e52f80c..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2d8041c9fdafc830a89a5cc2a2864283 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 deleted file mode 100644 index 8fa17cbe17741..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e366bb276ae4c6ad03ddacfb74c78793826e200465c7abcf8e158d05b16794e296ba8abe1780fbb586e0cadf28ed267ac981a408fb9dc3231547d30310962d29 diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 deleted file mode 100644 index c8df501c2aa37..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1cb79462479e95afb33de29b26c9c60f diff --git a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 deleted file mode 100644 index b2aa513df365e..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -56b1b8adbb9b660a19b10095c489fa67787c5a383fc409062c76afe659684ee44e31086ed683d3e058621302f69dd6547186901950ad2a57dc52c3f11df85e1d diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 deleted file mode 100644 index 45946d227f975..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -98dbed7361d07bbcbace591767a394b0 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 deleted file mode 100644 index d52fab671d358..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -26a3ad2641b51fe8aa4b18383adc35a2093abae4f8bd483bf730428159f79d8a155e180ac1b71fe77a558d3071e3cf6701c45b2b54c6bdce9710c9dd8ae6ee05 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 deleted file mode 100644 index 884be1df6672c..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f4311e1c856d88ef56b2e1a317486b0a diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 deleted file mode 100644 index dbc5d0f14924f..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-gnu-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -80a61943871a1c78c9483acd96e5821a2e0a684a6fca9283639abd29a6c523dc5f191dbd8001e8c45501c99d2ee6a2a1aa34293e5ee15670481f2e64f8cd8001 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 deleted file mode 100644 index 893aa492a92c0..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7de233cd7941bd4d4fe463cb390b9b76 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 deleted file mode 100644 index e2f3b9da29a67..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -3005c252e9fa3f42fa1eb7a42b6e239630df2cbb9641e278b1fe0e24c0aafaf086eade41c1a68660b0e60cc3a9eb61883835df265373e5476e90f701b452d042 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 deleted file mode 100644 index 529c0ad49feb5..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -bbbc8ac72aba716263c39cf621cd7028 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 deleted file mode 100644 index 3c3f0a34bdd5f..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-linux-musl-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7ce94e3a5ac55ed27ed380c17931f51a5f6eeba9b06c7d23396fa27a1e198839fbd6dfc1f90374e2c8434608e160adff758da85d4e6560a56b3ae1bed39e832e diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 deleted file mode 100644 index 16ec4a5f0f49c..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -56a84b9057036f41d69847e8f4127fea diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 deleted file mode 100644 index 40be98fb1ed7f..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d1cfdec4d462eddf3969a6c3b581c73cd90d70adf2afd07e1fa7bfce04a32630305e7eec22ff465e9ecedc0b12a337f0820f4f91e92f76ec9e161e9d0749ebca diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 deleted file mode 100644 index cd4a2fec244f2..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -23c5bf724a267de767ac633c94aedd79 diff --git a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 deleted file mode 100644 index 859600955afd5..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.i686-w64-mingw32-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4e00c86635c09576200def2de7e4f5c6697a98cfce134c617f4c21dd7874a0a22665908f3530801a854b2867955885bf51cfbffd8f370be733c219448e212e6c diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 deleted file mode 100644 index 455cab5ec1b28..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -3046fbc73ae52f4105c7083f9fe62c4a diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 deleted file mode 100644 index 093f519d4bccb..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9f6c01bedfe38c332255e8a4a2c4e03390db081c963f82ba9ad50117c5c5f810f387d5247466769d22c19bce8e2914305d0cb5f3d5214c6315aa764665e57e00 diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 deleted file mode 100644 index deef9ab89aeb5..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8d337e5b138241591cb2d1baec2485db diff --git a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 deleted file mode 100644 index 5e2e1bcd821fa..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2ff8a58d78a80cf1f9629dccedece0e4bdf4af4a3a5b56dcb2664cf42c849764760ee823a62f2722d54405ff49a0f222af1c5ccf50cc3dd34ccbafdee2bc44be diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 deleted file mode 100644 index a5a0d8985a643..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d4e319df84721a019b033e538e0bbd97 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 deleted file mode 100644 index e8972c7deb4fb..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -791ddf702c8751d7756fe2dc1ef3aa0d67fa5b4773064cc3e3012447f9b09516108dfa86b9b9326f19045bcd1807377b920efc2de6e90250f42a08d9ae4e99cb diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 deleted file mode 100644 index 989a8cb6e6361..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2eb46789ca2af60a9fae1a0b92bec9c3 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 deleted file mode 100644 index 19f64a4f0bb36..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-apple-darwin14-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8ce0f8c6a43f748885d3b044792e995c1729fb3576db52b136de5f8a1054b3d83d9c33ad38e907bf442d40b13b6eb5d2f539058d8a642d85405e2cccc09137e2 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 deleted file mode 100644 index a6b35b3fd29c3..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -bd4b77b4ad10d932352b93cc79583963 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 deleted file mode 100644 index 6f4cfdecd622f..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ae87d70aaeae01027cdb1f4fe76ee44abf8a68361f2086ecb93cfc54b7974a749508dc1ea60d7bb1806bdb1f0d58296d66d731c33d6f98a93bc6254374162de8 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 deleted file mode 100644 index bbe8f53cbd4cf..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -42a7bfaa427ad3bd20df3b94652ee16b diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 deleted file mode 100644 index d0e103093876a..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-gnu-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0fdd19cb13a81efec2b5c36287f97659000c0bc2a812e11cfa2053cbb42aa7aeeb5558ea531a6f2c8378467f03073fe87b33898230197bdfd1c5b168e2f87c41 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 deleted file mode 100644 index 389e67d548feb..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -fdf152c6473d303c58624497984547d5 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 deleted file mode 100644 index aa42a96b36b95..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -f68204bbadece63ed2980ef1d51567fbaa9689f2c6b68306636eaf44fa80001748cfacf41a16c16cd441acc049a139436964cd9f56220832f4789b14edc5a815 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 deleted file mode 100644 index bd1603c893110..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -959a97e62c016409a722f35e92940109 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 deleted file mode 100644 index cf03c140d1da1..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-linux-musl-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6364c770cdf039c52a27f946ec0cf4229d0a3ef90c7c4beb9c8ccd0f915787436be11c340106742dd223cc50bf1f98a7cb228fee10bda151d166c646f326e14c diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 deleted file mode 100644 index a4a5f02e76620..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -484d6a7beed5248b8611f458cbfc311f diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 deleted file mode 100644 index 7c3e641a62207..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -368bb6415b913fc4494fffc7bedb1ac48fd8d5c3ca697f790b59c1f8edce1780877b91acb20c627426de5bc024a9a3238b25d57150e2b483e1bc3d19da2c5460 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 deleted file mode 100644 index a9a5eb3bb7dbb..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -83db3a1e6d1faeffaf241faf906d6659 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 deleted file mode 100644 index 956d3632e2a2c..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4e3afafda2355463f3c301a112b23ddebeb5018e6b1271339ecc9a2e76297215c25228f6eb08a34a3fd8be93378040b1172457c714bec1af702b803baa92937d diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 deleted file mode 100644 index 102acd525d14d..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -96a5f00e789fc2fb31b42f67a44ff3b0 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 deleted file mode 100644 index 7887d60e0ae43..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx03.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9b3982200a1e034964908b019e288dcc8b192524d5ecfc23bf9e3e367dd5ef3b57d6c0aec8bf3ef6e365e5ecee15d4590634342e681c1896c0e992b7edcc6607 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 deleted file mode 100644 index a2f589d52c2ef..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1ec5c4395753cae4c741abcfb1be7a11 diff --git a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 b/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 deleted file mode 100644 index 2ab3c64c5d7b1..0000000000000 --- a/deps/checksums/GMP.v6.2.0-0.x86_64-w64-mingw32-cxx11.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -486ddc1d1e5ca320a2cfc329486ebf7e5ebb5cfb3388cab2ce0221ed615634dcaafd003bfa53191e510f790d3854afc26e7a45dc7d35610ed467d34126b19f9b diff --git a/deps/gmp.mk b/deps/gmp.mk index 6757f20208b4b..dbc21c7ebf439 100644 --- a/deps/gmp.mk +++ b/deps/gmp.mk @@ -20,9 +20,17 @@ $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 echo 1 > $@ $(SRCCACHE)/gmp-$(GMP_VER)/build-patched: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted + cp $(SRCDIR)/patches/config.sub $(SRCCACHE)/gmp-$(GMP_VER)/configfsf.sub + cd $(dir $@) && patch < $(SRCDIR)/patches/gmp-exception.patch cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch echo 1 > $@ +$(SRCCACHE)/gmp-$(GMP_VER)/gmp-config-ldflags.patch-applied: | $(SRCCACHE)/gmp-$(GMP_VER)/build-patched + cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp-config-ldflags.patch + echo 1 > $@ + +$(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-config-ldflags.patch-applied + $(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ @@ -73,5 +81,5 @@ else # USE_BINARYBUILDER_GMP GMP_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/GMP_jll.jl/releases/download/GMP-v$(GMP_VER)+$(GMP_BB_REL) GMP_BB_NAME := GMP.v$(GMP_VER) -$(eval $(call bb-install,gmp,GMP,false,true)) +$(eval $(call bb-install,gmp,GMP,false)) endif diff --git a/deps/patches/gmp-config-ldflags.patch b/deps/patches/gmp-config-ldflags.patch new file mode 100644 index 0000000000000..fb89fa66b8da5 --- /dev/null +++ b/deps/patches/gmp-config-ldflags.patch @@ -0,0 +1,381 @@ +--- gmp-6.1.2/configure 2019-03-25 17:58:41.928471374 -0400 ++++ gmp-6.1.2-LDFLAGS/configure 2019-03-26 13:08:07.756316866 -0400 +@@ -5880,7 +5880,7 @@ if test "$gmp_prog_cc_works" = yes; then + int main () { return 0; } + EOF + echo "Test compile: " >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -5934,7 +5934,7 @@ void *f() { return g(); } + int main () { return 0; } + EOF + echo "Test compile: function pointer return" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -5990,7 +5990,7 @@ int cmov () { return (n >= 0 ? n : 0); } + int main () { return 0; } + EOF + echo "Test compile: cmov instruction" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6047,7 +6047,7 @@ unsigned long gcc303 () { return (unsign + int main () { return 0; } + EOF + echo "Test compile: double -> ulong conversion" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6102,7 +6102,7 @@ unsigned long fneg () { return -fneg_dat + int main () { return 0; } + EOF + echo "Test compile: double negation" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6158,7 +6158,7 @@ float ftod () { return (float) ftod_data + int main () { return 0; } + EOF + echo "Test compile: double -> float conversion" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6243,7 +6243,7 @@ param_init () + int main () { return 0; } + EOF + echo "Test compile: gnupro alpha ev6 char spilling" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6294,7 +6294,7 @@ if test "$gmp_prog_cc_works" = yes; then + int k; int foo () { __builtin_alloca (k); } + EOF + echo "Test compile: __builtin_alloca availability" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6340,7 +6340,7 @@ int foo () + int main () { return 0; } + EOF + echo "Test compile: alloca array" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6418,7 +6418,7 @@ int f () + int main () { return 0; } + EOF + echo "Test compile: abs int -> double conversion" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6483,7 +6483,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 1" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6544,7 +6544,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 2" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6605,7 +6605,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: freebsd hacked gcc" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6704,7 +6704,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6813,7 +6813,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization 2" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7325,7 +7325,7 @@ _main: + xorl %eax, %eax + ret + EOF +- gmp_compile="$cc $cflags $cppflags conftest.s -o conftest >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.s -o conftest >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7390,7 +7390,7 @@ $as_echo_n "checking compiler $cc $cflag + cat >conftest.c <&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7498,7 +7498,7 @@ if test "$gmp_prog_cc_works" = yes; then + int main () { return 0; } + EOF + echo "Test compile: " >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7552,7 +7552,7 @@ void *f() { return g(); } + int main () { return 0; } + EOF + echo "Test compile: function pointer return" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7608,7 +7608,7 @@ int cmov () { return (n >= 0 ? n : 0); } + int main () { return 0; } + EOF + echo "Test compile: cmov instruction" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7665,7 +7665,7 @@ unsigned long gcc303 () { return (unsign + int main () { return 0; } + EOF + echo "Test compile: double -> ulong conversion" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7720,7 +7720,7 @@ unsigned long fneg () { return -fneg_dat + int main () { return 0; } + EOF + echo "Test compile: double negation" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7776,7 +7776,7 @@ float ftod () { return (float) ftod_data + int main () { return 0; } + EOF + echo "Test compile: double -> float conversion" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7861,7 +7861,7 @@ param_init () + int main () { return 0; } + EOF + echo "Test compile: gnupro alpha ev6 char spilling" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7912,7 +7912,7 @@ if test "$gmp_prog_cc_works" = yes; then + int k; int foo () { __builtin_alloca (k); } + EOF + echo "Test compile: __builtin_alloca availability" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7958,7 +7958,7 @@ int foo () + int main () { return 0; } + EOF + echo "Test compile: alloca array" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8036,7 +8036,7 @@ int f () + int main () { return 0; } + EOF + echo "Test compile: abs int -> double conversion" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8101,7 +8101,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 1" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8162,7 +8162,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 2" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8223,7 +8223,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: freebsd hacked gcc" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8322,7 +8322,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8431,7 +8431,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization 2" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -9987,7 +9987,7 @@ main () + return 0; + } + EOF +-gmp_compile="$CC_FOR_BUILD conftest.c" ++gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c" + cc_for_build_works=no + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 +@@ -10019,7 +10019,7 @@ main () + return 0; + } + EOF +-gmp_compile="$HOST_CC conftest.c" ++gmp_compile="$HOST_CC $LDFLAGS conftest.c" + cc_for_build_works=no + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 +@@ -10052,7 +10052,7 @@ main () + return 0; + } + EOF +-gmp_compile="$i conftest.c" ++gmp_compile="$i $LDFLAGS conftest.c" + cc_for_build_works=no + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 +@@ -10132,7 +10132,7 @@ main () + } + EOF + for i in .exe ,ff8 ""; do +- gmp_compile="$CC_FOR_BUILD conftest.c -o conftest$i" ++ gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c -o conftest$i" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -10168,7 +10168,7 @@ main (int argc, char **argv) + return 0; + } + EOF +-gmp_compile="$CC_FOR_BUILD conftest.c" ++gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -10210,7 +10210,7 @@ foo () + return log (d); + } + EOF +-gmp_compile="$CC_FOR_BUILD conftest.c -lm" ++gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c -lm" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -10543,7 +10543,7 @@ if test "$gmp_prog_cxx_works" = yes; the + int main (void) { return 0; } + EOF + echo "Test compile: " >&5 +- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" ++ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 + (eval $gmp_cxxcompile) 2>&5 + ac_status=$? +@@ -10583,7 +10583,7 @@ using namespace foo; + int main (void) { return 0; } + EOF + echo "Test compile: namespace" >&5 +- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" ++ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 + (eval $gmp_cxxcompile) 2>&5 + ac_status=$? +@@ -10629,7 +10629,7 @@ void someoutput (void) { std::cout << 12 + int main (void) { return 0; } + EOF + echo "Test compile: std iostream" >&5 +- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" ++ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 + (eval $gmp_cxxcompile) 2>&5 + ac_status=$? +@@ -27095,7 +27095,7 @@ for tmp_underscore in "" "_"; do + ${tmp_gsym_prefix}main$gmp_cv_asm_label_suffix + addl $ ${tmp_underscore}_GLOBAL_OFFSET_TABLE_, %ebx + EOF +- gmp_compile="$CCAS $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.s >&5 && $CC $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.$OBJEXT >&5" ++ gmp_compile="$CCAS $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.s >&5 && $CC $CFLAGS $CPPFLAGS $LDFLAGS $lt_prog_compiler_pic conftest.$OBJEXT >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? + diff --git a/deps/patches/gmp-exception.patch b/deps/patches/gmp-exception.patch new file mode 100644 index 0000000000000..e4672be10244b --- /dev/null +++ b/deps/patches/gmp-exception.patch @@ -0,0 +1,35 @@ +diff -r 842c2ba359bf errno.c +--- a/errno.c Sun Jan 24 22:06:51 2016 +0100 ++++ b/errno.c Thu Jan 28 13:37:54 2016 -0500 +@@ -33,24 +33,24 @@ + see https://www.gnu.org/licenses/. */ + + #include ++ ++#include ++ + #include "gmp.h" + #include "gmp-impl.h" + + int gmp_errno = 0; + + +-/* The deliberate divide by zero triggers an exception on most systems. On +- those where it doesn't, for example power and powerpc, use abort instead. +- +- Enhancement: Perhaps raise(SIGFPE) (or the same with kill()) would be +- better than abort. Perhaps it'd be possible to get the BSD style +- FPE_INTDIV_TRAP parameter in there too. */ +- ++/* Use SIGFPE on systems which have it. Otherwise, deliberate divide ++ by zero, which triggers an exception on most systems. On those ++ where it doesn't, for example power and powerpc, use abort instead. */ + void + __gmp_exception (int error_bit) + { + gmp_errno |= error_bit; + __gmp_junk = 10 / __gmp_0; ++ raise (SIGFPE); + abort (); + } + From 96e7647844bd8ccfad36bcc1069fb2a85c90fd5e Mon Sep 17 00:00:00 2001 From: Liozou Date: Thu, 18 Jun 2020 00:08:30 +0200 Subject: [PATCH 187/232] fix introspection macros with getproperty (#36286) --- stdlib/InteractiveUtils/src/macros.jl | 18 +++++++++++++----- stdlib/InteractiveUtils/test/runtests.jl | 7 +++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 10b247beff11f..f529d5f2cc31e 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -68,12 +68,20 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[]) end fully_qualified_symbol &= ex1 isa Symbol if fully_qualified_symbol - if string(fcn) == "which" - return quote $(fcn)($(esc(ex0.args[1])), $(ex0.args[2])) end - else - return Expr(:call, :error, "expression is not a function call or symbol") + return quote + local arg1 = $(esc(ex0.args[1])) + if isa(arg1, Module) + $(if string(fcn) == "which" + :(which(arg1, $(ex0.args[2]))) + else + :(error("expression is not a function call")) + end) + else + local args = typesof($(map(esc, ex0.args)...)) + $(fcn)(Base.getproperty, args) + end end - elseif ex0.args[2] isa Expr + else return Expr(:call, :error, "dot expressions are not lowered to " * "a single function call, so @$fcn cannot analyze " * "them. You may want to use Meta.@lower to identify " diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 7e2d6f3136b3e..be4c1ea42d538 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -325,6 +325,13 @@ B33163(x) = x @test_throws ErrorException (@functionloc Base.nothing) @test (@code_typed (3//4).num)[2] == Int +struct A14637 + x +end +a14637 = A14637(0) +@test (@which a14637.x).name == :getproperty +@test (@functionloc a14637.x)[2] isa Integer + # Issue #28615 @test_throws ErrorException (@which [1, 2] .+ [3, 4]) @test (@code_typed optimize=true max.([1,7], UInt.([4])))[2] == Vector{UInt} From a985bed30d3269b771aa7369a2a24cbe49ca9383 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 17 Jun 2020 22:40:32 -0400 Subject: [PATCH 188/232] Delete dead :& code (#36337) We used to use & in ccall, but that's been dead for a long time. I believe this code is just a leftover relic. --- base/compiler/abstractinterpretation.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 441d6a097bb25..f8d657fc6e00f 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1058,9 +1058,6 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va t = PartialStruct(t, at.fields) end end - elseif e.head === :& - abstract_eval(interp, e.args[1], vtypes, sv) - t = Any elseif e.head === :foreigncall abstract_eval(interp, e.args[1], vtypes, sv) t = sp_type_rewrap(e.args[2], sv.linfo, true) From d5b47a0493139bde1fb0147fae79cc28915795a6 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 18 Jun 2020 01:45:19 -0400 Subject: [PATCH 189/232] Test SingularException throw for naivesub (#36331) --- stdlib/LinearAlgebra/test/bidiag.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 27cf693a54169..4d6fefdeb52e9 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -232,6 +232,10 @@ Random.seed!(1) @test_throws DimensionMismatch transpose(T) \ offsizemat @test_throws DimensionMismatch T' \ offsizemat + if elty <: BlasReal + @test_throws SingularException LinearAlgebra.naivesub!(Bidiagonal(zeros(elty, n), ones(elty, n-1), :U), rand(elty, n)) + @test_throws SingularException LinearAlgebra.naivesub!(Bidiagonal(zeros(elty, n), ones(elty, n-1), :L), rand(elty, n)) + end let bb = b, cc = c for atype in ("Array", "SubArray") if atype == "Array" From 96fc2aa03b8f09619e737c17962990d97ec9cb62 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 18 Jun 2020 01:33:53 -0700 Subject: [PATCH 190/232] Switch `httpbin` tests over to JuliaLang-hosted `httpbin` mock server (#36336) --- test/download.jl | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/test/download.jl b/test/download.jl index 31b8217827476..3f56b04dc4e43 100644 --- a/test/download.jl +++ b/test/download.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -let urls = ["http://httpbin.org/ip", "https://httpbin.org/ip"] +# Test that `Base.download_url()` is altered by `Base.DOWNLOAD_HOOKS`. +let urls = ["http://httpbin.julialang.org/ip", "https://httpbin.julialang.org/ip"] for url in urls @test Base.download_url(url) == url end @@ -17,14 +18,14 @@ end mktempdir() do temp_dir # Download a file file = joinpath(temp_dir, "ip") - @test download("http://httpbin.org/ip", file) == file + @test download("https://httpbin.julialang.org/ip", file) == file @test isfile(file) @test !isempty(read(file)) ip = read(file, String) # Test download rewrite hook push!(Base.DOWNLOAD_HOOKS, url->replace(url, r"/status/404$" => "/ip")) - @test download("http://httpbin.org/status/404", file) == file + @test download("https://httpbin.julialang.org/status/404", file) == file @test isfile(file) @test !isempty(read(file)) @test ip == read(file, String) @@ -32,22 +33,24 @@ mktempdir() do temp_dir # Download an empty file empty_file = joinpath(temp_dir, "empty") - @test download("http://httpbin.org/status/200", empty_file) == empty_file + @test download("https://httpbin.julialang.org/status/200", empty_file) == empty_file # Windows and older versions of curl do not create the empty file (https://github.com/curl/curl/issues/183) @test !isfile(empty_file) || isempty(read(empty_file)) # Make sure that failed downloads do not leave files around missing_file = joinpath(temp_dir, "missing") - @test_throws ProcessFailedException download("http://httpbin.org/status/404", missing_file) + @test_throws ProcessFailedException download("https://httpbin.julialang.org/status/404", missing_file) @test !isfile(missing_file) - # Make sure we properly handle metachar ' - metachar_file = joinpath(temp_dir, "metachar") - download("https://httpbin.org/get?test='^'", metachar_file) - metachar_string = read(metachar_file, String) - m = match(r"\"url\"\s*:\s*\"(.*)\"", metachar_string) - @test m.captures[1] == "https://httpbin.org/get?test='^'" + # Make sure we properly handle metachar ' on windows with ^ escaping + if Sys.iswindows() + metachar_file = joinpath(temp_dir, "metachar") + Base.download_powershell("https://httpbin.julialang.org/get?test='^'", metachar_file) + metachar_string = read(metachar_file, String) + m = match(r"\"test\"\s*:\s*\"(.*)\"", metachar_string) + @test m.captures[1] == "'^'" + end # Use a TEST-NET (192.0.2.0/24) address which shouldn't be bound invalid_host_file = joinpath(temp_dir, "invalid_host") From 4d94a63bc212d9abd9dbc6df13d883cd8f9d6df7 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 18 Jun 2020 01:54:51 -0700 Subject: [PATCH 191/232] Add `httpbin.julialang.org` AWS API Gateway config This serves as a backup of the AWS API Gateway configuration hosted at `httpbin.julialang.org` --- contrib/httpbin-prod-swagger-apigateway.yaml | 101 +++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 contrib/httpbin-prod-swagger-apigateway.yaml diff --git a/contrib/httpbin-prod-swagger-apigateway.yaml b/contrib/httpbin-prod-swagger-apigateway.yaml new file mode 100644 index 0000000000000..528d37038df5a --- /dev/null +++ b/contrib/httpbin-prod-swagger-apigateway.yaml @@ -0,0 +1,101 @@ +--- +swagger: "2.0" +info: + description: "Looks like an httpbin" + version: "2020-06-18T07:18:54Z" + title: "httpbin" +host: "httpbin.julialang.org" +schemes: +- "https" +paths: + /get: + get: + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "200 response" + schema: + $ref: "#/definitions/Empty" + x-amazon-apigateway-integration: + responses: + default: + statusCode: "200" + responseTemplates: + application/json: "#set($allParams = $input.params())\n{\n\"args\":\ + \ {\n#set($params = $allParams.get(\"querystring\"))\n#foreach($paramName\ + \ in $params.keySet())\n\"$paramName\" : \"$util.escapeJavaScript($params.get($paramName)).replaceAll(\"\ + \\\\'\",\"'\")\"\n#if($foreach.hasNext),#end\n\n#end\n},\n\"headers\"\ + : {\n#set($params = $allParams.get(\"header\"))\n#foreach($paramName\ + \ in $params.keySet())\n\"$paramName\" : \"$util.escapeJavaScript($params.get($paramName)).replaceAll(\"\ + \\\\'\",\"'\")\"#if($foreach.hasNext),#end\n\n#end\n},\n\"origin\"\ + : \"$context.identity.sourceIp\",\n\"url\": \"$context.path\"\n}\n" + requestTemplates: + application/json: "{\"statusCode\": 200}" + passthroughBehavior: "when_no_match" + type: "mock" + /ip: + get: + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "200 response" + x-amazon-apigateway-integration: + responses: + default: + statusCode: "200" + responseTemplates: + application/json: "{\n \"origin\" : \"$context.identity.sourceIp\"\n\ + }" + requestTemplates: + application/json: "{\"statusCode\": 200}" + passthroughBehavior: "when_no_match" + type: "mock" + /status/200: + get: + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "200 response" + schema: + $ref: "#/definitions/Empty" + x-amazon-apigateway-integration: + responses: + default: + statusCode: "200" + requestTemplates: + application/json: "{\"statusCode\": 200}" + passthroughBehavior: "when_no_match" + type: "mock" + /status/404: + get: + consumes: + - "application/json" + responses: + 404: + description: "404 response" + x-amazon-apigateway-integration: + responses: + default: + statusCode: "404" + requestTemplates: + application/json: "{\"statusCode\": 404}" + passthroughBehavior: "when_no_match" + type: "mock" +definitions: + Empty: + type: "object" + title: "Empty Schema" +x-amazon-apigateway-gateway-responses: + DEFAULT_4XX: + statusCode: 401 + responseTemplates: + application/json: "{\"message\":$context.error.messageString}" From 5420dd2c90bc3a193e5cc6a762953004a02762e0 Mon Sep 17 00:00:00 2001 From: Klaus Crusius Date: Thu, 18 Jun 2020 15:22:41 +0200 Subject: [PATCH 192/232] allow `redirect_std*` to `devnull` (#36136) (#36146) --- base/stream.jl | 15 ++++++++++++--- test/spawn.jl | 13 +++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/base/stream.jl b/base/stream.jl index d5f3b05fc0327..84354a6eb801c 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -1098,6 +1098,15 @@ for (x, writable, unix_fd, c_symbol) in ($f)($(writable ? :write : :read)) return (read, write) end + function ($f)(::DevNull) + global $x + nulldev = @static Sys.iswindows() ? "NUL" : "/dev/null" + handle = open(nulldev, write=$writable) + $(_f)(handle) + close(handle) # handle has been dup'ed in $(_f) + $x = devnull + return devnull + end end end @@ -1115,7 +1124,7 @@ elsewhere. If called with the optional `stream` argument, then returns `stream` itself. !!! note - `stream` must be a `TTY`, a `Pipe`, or a socket. + `stream` must be an `IOStream`, a `TTY`, a `Pipe`, a socket, or `devnull`. """ redirect_stdout @@ -1125,7 +1134,7 @@ redirect_stdout Like [`redirect_stdout`](@ref), but for [`stderr`](@ref). !!! note - `stream` must be a `TTY`, a `Pipe`, or a socket. + `stream` must be an `IOStream`, a `TTY`, a `Pipe`, a socket, or `devnull`. """ redirect_stderr @@ -1137,7 +1146,7 @@ Note that the order of the return tuple is still `(rd, wr)`, i.e. data to be read from [`stdin`](@ref) may be written to `wr`. !!! note - `stream` must be a `TTY`, a `Pipe`, or a socket. + `stream` must be an `IOStream`, a `TTY`, a `Pipe`, a socket, or `devnull`. """ redirect_stdin diff --git a/test/spawn.jl b/test/spawn.jl index 09648e7e02806..8a73394d8ae47 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -255,6 +255,19 @@ end end end +# issue #36136 +@testset "redirect to devnull" begin + @test redirect_stdout(devnull) do; println("Hello") end === nothing + @test redirect_stderr(devnull) do; println(stderr, "Hello") end === nothing + # stdin is unavailable on the workers. Run test on master. + ret = Core.eval(Main, quote + remotecall_fetch(1) do + redirect_stdin(devnull) do; read(stdin, String) end + end + end) + @test ret == "" +end + # Test that redirecting an IOStream does not crash the process let fname = tempname(), p cmd = """ From 9cbe145bc9c539cd84a4459ab80267a60160717a Mon Sep 17 00:00:00 2001 From: Harry Scholes Date: Thu, 18 Jun 2020 16:45:32 +0100 Subject: [PATCH 193/232] Add sizehint! to Dict constructor (#35254) * Add sizehint to Dict constructor * Check iterator has length --- base/dict.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/base/dict.jl b/base/dict.jl index 1f42ab8c82f85..7e785930c6d36 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -99,6 +99,10 @@ mutable struct Dict{K,V} <: AbstractDict{K,V} end function Dict{K,V}(kv) where V where K h = Dict{K,V}() + chklen = IteratorSize(kv) + if (chklen isa HasLength) || (chklen isa HasShape) + sizehint!(h, length(kv)) + end for (k,v) in kv h[k] = v end From ee0621fabead899300a74d4d58cccef026c073b1 Mon Sep 17 00:00:00 2001 From: Gautam Mishra Date: Thu, 18 Jun 2020 22:34:31 +0530 Subject: [PATCH 194/232] Fix: lastindex for Numbers (#36314) * Fix: lastindex for Numbers * Boundcheck for lastindex numbers * tests for lastindex, firstindex for numbers * include example test from #36311 --- base/number.jl | 2 ++ test/numbers.jl | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/base/number.jl b/base/number.jl index 9b615c99d4147..298e72fc258c6 100644 --- a/base/number.jl +++ b/base/number.jl @@ -68,7 +68,9 @@ ndims(x::Number) = 0 ndims(::Type{<:Number}) = 0 length(x::Number) = 1 firstindex(x::Number) = 1 +firstindex(x::Number, d::Int) = d < 1 ? throw(BoundsError()) : 1 lastindex(x::Number) = 1 +lastindex(x::Number, d::Int) = d < 1 ? throw(BoundsError()) : 1 IteratorSize(::Type{<:Number}) = HasShape{0}() keys(::Number) = OneTo(1) diff --git a/test/numbers.jl b/test/numbers.jl index bec8ee797ebb4..00aaf80957f9c 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2401,7 +2401,12 @@ end @test size(1) == () @test length(1) == 1 @test firstindex(1) == 1 + @test firstindex(1, 1) == 1 + @test_throws BoundsError firstindex(1,0) @test lastindex(1) == 1 + @test lastindex(1, 1) == 1 + @test 1[end,end] == 1 + @test_throws BoundsError lastindex(1,0) @test eltype(Integer) == Integer end From e96728cae156a7c8c42f9a1b14ac482230006d17 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 18 Jun 2020 14:23:33 -0400 Subject: [PATCH 195/232] FileWatching: cleanup timers on return (#36301) Following my own advice at #36217. Avoids a minor temporary resource leak. --- stdlib/FileWatching/src/FileWatching.jl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/stdlib/FileWatching/src/FileWatching.jl b/stdlib/FileWatching/src/FileWatching.jl index 6f316e5a772f0..9e39ab2590479 100644 --- a/stdlib/FileWatching/src/FileWatching.jl +++ b/stdlib/FileWatching/src/FileWatching.jl @@ -649,10 +649,13 @@ giving the result of the polling. function poll_fd(s::Union{RawFD, Sys.iswindows() ? WindowsRawSocket : Union{}}, timeout_s::Real=-1; readable=false, writable=false) wt = Condition() fdw = _FDWatcher(s, readable, writable) + local timer try if timeout_s >= 0 result::FDEvent = FDEvent() - @async (sleep(timeout_s); notify(wt)) + timer = Timer(timeout_s) do t + notify(wt) + end @async begin try result = wait(fdw, readable=readable, writable=writable) @@ -669,6 +672,7 @@ function poll_fd(s::Union{RawFD, Sys.iswindows() ? WindowsRawSocket : Union{}}, end finally close(fdw, readable, writable) + @isdefined(timer) && close(timer) end end @@ -686,13 +690,17 @@ This behavior of this function varies slightly across platforms. See """ function watch_file(s::AbstractString, timeout_s::Real=-1) fm = FileMonitor(s) + local timer try if timeout_s >= 0 - @async (sleep(timeout_s); close(fm)) + timer = Timer(timeout_s) do t + close(fm) + end end return wait(fm) finally close(fm) + @isdefined(timer) && close(timer) end end @@ -730,14 +738,17 @@ function watch_folder(s::String, timeout_s::Real=-1) # create a second monitor object just for that purpose. # We still take the events from the primary stream. fm2 = FileMonitor(s) + timer = Timer(timeout_s) do t + close(fm2) + end try - @async (sleep(timeout_s); close(fm2)) while isopen(fm.notify) && !isready(fm.notify) fm2.handle == C_NULL && return "" => FileEvent() # timeout wait(fm2) end finally close(fm2) + close(timer) end # guaranteed that next call to `wait(fm)` is non-blocking # since we haven't entered the libuv event loop yet @@ -783,9 +794,12 @@ it is more reliable and efficient, although in some situations it may not be ava """ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::Real=-1) pfw = PollingFileWatcher(s, Float64(interval_seconds)) + local timer try if timeout_s >= 0 - @async (sleep(timeout_s); close(pfw)) + timer = Timer(timeout_s) do t + close(pfw) + end end statdiff = wait(pfw) if isa(statdiff[2], IOError) @@ -795,6 +809,7 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R return statdiff finally close(pfw) + @isdefined(timer) && close(timer) end end From 1f8b44204fafb1caabc6a1cd6ca39458a550e2fc Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Thu, 18 Jun 2020 11:58:23 -0700 Subject: [PATCH 196/232] Add Iterators.map (#34352) --- NEWS.md | 2 ++ base/iterators.jl | 38 +++++++++++++++++++++++++++++--------- doc/src/base/iterators.md | 1 + test/iterators.jl | 7 +++++++ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/NEWS.md b/NEWS.md index b6df634e55755..c9d73854e0f9d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -36,6 +36,8 @@ New library functions * New function `Base.kron!` and corresponding overloads for various matrix types for performing Kronecker product in-place. ([#31069]). * New function `Base.Threads.foreach(f, channel::Channel)` for multithreaded `Channel` consumption. ([#34543]). +* `Iterators.map` is added. It provides another syntax `Iterators.map(f, iterators...)` + for writing `(f(args...) for args in zip(iterators...))`, i.e. a lazy `map` ([#34352]). New library features -------------------- diff --git a/base/iterators.jl b/base/iterators.jl index 366791be94e8e..fe5146f7585ab 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -12,7 +12,7 @@ using .Base: @inline, Pair, AbstractDict, IndexLinear, IndexCartesian, IndexStyle, AbstractVector, Vector, tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, Generator, AbstractRange, - LinearIndices, (:), |, +, -, !==, !, <=, <, missing, map, any, @boundscheck, @inbounds + LinearIndices, (:), |, +, -, !==, !, <=, <, missing, any, @boundscheck, @inbounds import .Base: first, last, @@ -24,6 +24,26 @@ import .Base: export enumerate, zip, rest, countfrom, take, drop, takewhile, dropwhile, cycle, repeated, product, flatten, partition +""" + Iterators.map(f, iterators...) + +Create a lazy mapping. This is another syntax for writing +`(f(args...) for args in zip(iterators...))`. + +!!! compat "Julia 1.6" + This function requires at least Julia 1.6. + +# Examples +```jldoctest +julia> collect(Iterators.map(x -> x^2, 1:3)) +3-element Array{Int64,1}: + 1 + 4 + 9 +``` +""" +map(f, args...) = Base.Generator(f, args...) + tail_if_any(::Tuple{}) = () tail_if_any(x::Tuple) = tail(x) @@ -322,8 +342,8 @@ eltype(::Type{Zip{Is}}) where {Is<:Tuple} = _zip_eltype(Is) _zip_eltype(::Type{Is}) where {Is<:Tuple} = tuple_type_cons(eltype(tuple_type_head(Is)), _zip_eltype(tuple_type_tail(Is))) _zip_eltype(::Type{Tuple{}}) = Tuple{} -@inline isdone(z::Zip) = _zip_any_isdone(z.is, map(_ -> (), z.is)) -@inline isdone(z::Zip, ss) = _zip_any_isdone(z.is, map(tuple, ss)) +@inline isdone(z::Zip) = _zip_any_isdone(z.is, Base.map(_ -> (), z.is)) +@inline isdone(z::Zip, ss) = _zip_any_isdone(z.is, Base.map(tuple, ss)) @inline function _zip_any_isdone(is, ss) d1 = isdone(is[1], ss[1]...) d1 === true && return true @@ -331,8 +351,8 @@ _zip_eltype(::Type{Tuple{}}) = Tuple{} end @inline _zip_any_isdone(::Tuple{}, ::Tuple{}) = false -@propagate_inbounds iterate(z::Zip) = _zip_iterate_all(z.is, map(_ -> (), z.is)) -@propagate_inbounds iterate(z::Zip, ss) = _zip_iterate_all(z.is, map(tuple, ss)) +@propagate_inbounds iterate(z::Zip) = _zip_iterate_all(z.is, Base.map(_ -> (), z.is)) +@propagate_inbounds iterate(z::Zip, ss) = _zip_iterate_all(z.is, Base.map(tuple, ss)) # This first queries isdone from every iterator. If any gives true, it immediately returns # nothing. It then iterates all those where isdone returned missing, afterwards all those @@ -388,7 +408,7 @@ _zip_iterator_eltype(::Type{Is}) where {Is<:Tuple} = _zip_iterator_eltype(tuple_type_tail(Is))) _zip_iterator_eltype(::Type{Tuple{}}) = HasEltype() -reverse(z::Zip) = Zip(map(reverse, z.is)) +reverse(z::Zip) = Zip(Base.map(reverse, z.is)) # filter @@ -982,7 +1002,7 @@ end isdone(P) === true && return nothing next = _piterate(P.iterators...) next === nothing && return nothing - return (map(x -> x[1], next), next) + return (Base.map(x -> x[1], next), next) end @inline _piterate1(::Tuple{}, ::Tuple{}) = nothing @@ -1003,10 +1023,10 @@ end isdone(P, states) === true && return nothing next = _piterate1(P.iterators, states) next === nothing && return nothing - return (map(x -> x[1], next), next) + return (Base.map(x -> x[1], next), next) end -reverse(p::ProductIterator) = ProductIterator(map(reverse, p.iterators)) +reverse(p::ProductIterator) = ProductIterator(Base.map(reverse, p.iterators)) # flatten an iterator of iterators diff --git a/doc/src/base/iterators.md b/doc/src/base/iterators.md index 00655d977d785..8afc54b3bd11b 100644 --- a/doc/src/base/iterators.md +++ b/doc/src/base/iterators.md @@ -15,6 +15,7 @@ Base.Iterators.repeated Base.Iterators.product Base.Iterators.flatten Base.Iterators.partition +Base.Iterators.map Base.Iterators.filter Base.Iterators.accumulate Base.Iterators.reverse diff --git a/test/iterators.jl b/test/iterators.jl index a487f2721c2da..7137e70b5958f 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -101,6 +101,13 @@ end @test length(zip(1:3,product(1:7,cycle(1:3)))) == 3 @test length(zip(1:3,product(1:7,cycle(1:3)),8)) == 1 +# map +# ---- +@testset "Iterators.map" begin + @test collect(Iterators.map(string, 1:3)::Base.Generator) == map(string, 1:3) + @test collect(Iterators.map(tuple, 1:3, 4:6)::Base.Generator) == map(tuple, 1:3, 4:6) +end + # rest # ---- let s = "hello" From a9dd7f389c3775a4a0b0ddd065480e735837fb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 18 Jun 2020 21:39:10 +0200 Subject: [PATCH 197/232] ' ,' -> ', ' (#36349) --- base/sort.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/sort.jl b/base/sort.jl index ff8edf15bd645..c3af5c4312dcc 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -244,7 +244,7 @@ function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) lt(o, first(a), x) ? length(a) + 1 : 1 else n = round(Integer, clamp((x - first(a)) / step(a) + 1, 1, length(a))) - lt(o, a[n] ,x) ? n + 1 : n + lt(o, a[n], x) ? n + 1 : n end end From 638510733757efd066b578582997af7cbf3b4fa9 Mon Sep 17 00:00:00 2001 From: Adam B Date: Thu, 18 Jun 2020 15:41:27 -0400 Subject: [PATCH 198/232] Consider terminal emacs in EDITOR_CALLBACKS before graphical (#36346) Since the regex r"\bemacs" matches the cmd `emacs -nw`, the regex r"\bemacs\b.*\s(-nw|--no-window-system)\b" should be considered in the EDITOR_CALLBACKS list before it. Without this change, when JULIA_EDITOR="emacs -nw", julia tries to open the editor with run(...; wait=false) which causes the error: emacs: standard input is not a tty or for emacsclient: emacsclient: could not get terminal name emacsclient: error executing alternate editor "" --- stdlib/InteractiveUtils/src/editless.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/InteractiveUtils/src/editless.jl b/stdlib/InteractiveUtils/src/editless.jl index c92211220a910..e4e2fcf20d903 100644 --- a/stdlib/InteractiveUtils/src/editless.jl +++ b/stdlib/InteractiveUtils/src/editless.jl @@ -58,6 +58,7 @@ Note that many editors are already defined. All of the following commands should already work: - emacs +- emacsclient - vim - nvim - nano @@ -113,15 +114,16 @@ function define_default_editors() define_editor(r".*") do cmd, path, line `$cmd $path` end + define_editor([r"\bemacs", "gedit", r"\bgvim"]) do cmd, path, line + `$cmd +$line $path` + end + # Must check that emacs not running in -t/-nw before regex match for general emacs define_editor([ "vim", "vi", "nvim", "mvim", "nano", r"\bemacs\b.*\s(-nw|--no-window-system)\b", r"\bemacsclient\b.\s*-(-?nw|t|-?tty)\b"], wait=true) do cmd, path, line `$cmd +$line $path` end - define_editor([r"\bemacs", "gedit", r"\bgvim"]) do cmd, path, line - `$cmd +$line $path` - end define_editor(["textmate", "mate", "kate"]) do cmd, path, line `$cmd $path -l $line` end From d19a27939c0481af92d0e1fe3951673ce520fa33 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Thu, 18 Jun 2020 15:42:11 -0400 Subject: [PATCH 199/232] macOS lazily saves the AVX512 context (#36330) --- src/processor_x86.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index 06b3e42f075ac..0b8cfe8dccdce 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -545,7 +545,14 @@ static NOINLINE std::pair> _get_host_cpu(void) unset_bits(features, 32 + 27); if (!hasavx) features_disable_avx(features); +#ifdef _OS_DARWIN_ + // See https://github.com/llvm/llvm-project/commit/82921bf2baed96b700f90b090d5dc2530223d9c0 + // and https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/osfmk/i386/fpu.c#L174 + // Darwin lazily saves the AVX512 context on first use + bool hasavx512save = hasavx; +#else bool hasavx512save = hasavx && test_all_bits(xcr0, 0xe0); +#endif if (!hasavx512save) features_disable_avx512(features); // Ignore feature bits that we are not interested in. From 3c2a6e8a1675183043b9ecb92a80f67bc4341f6c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 18 Jun 2020 15:43:59 -0400 Subject: [PATCH 200/232] show: differentiate TypeName and wrapper when printing (#36303) --- base/abstractdict.jl | 4 ++-- base/arrayshow.jl | 6 +++--- base/essentials.jl | 2 +- base/show.jl | 7 +++++-- src/rtutils.c | 5 +++++ test/dict.jl | 10 +++++----- test/show.jl | 2 ++ 7 files changed, 23 insertions(+), 13 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index b0f5784daf453..47853debcf344 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -45,7 +45,7 @@ struct ValueIterator{T<:AbstractDict} end function summary(io::IO, iter::T) where {T<:Union{KeySet,ValueIterator}} - print(io, T.name, " for a ") + print(io, T.name.name, " for a ") summary(io, iter.dict) end @@ -558,7 +558,7 @@ Dict{Symbol,Int64} with 2 entries: :b => 2 julia> map!(v -> v-1, values(d)) -Base.ValueIterator for a Dict{Symbol,Int64} with 2 entries. Values: +ValueIterator for a Dict{Symbol,Int64} with 2 entries. Values: 0 1 ``` diff --git a/base/arrayshow.jl b/base/arrayshow.jl index d57320dfdfb24..a7882278ee464 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -516,9 +516,9 @@ function typeinfo_prefix(io::IO, X) if X isa AbstractDict if eltype_X == eltype_ctx - string(typeof(X).name), false + sprint(show_type_name, typeof(X).name), false elseif !isempty(X) && typeinfo_implicit(keytype(X)) && typeinfo_implicit(valtype(X)) - string(typeof(X).name), true + sprint(show_type_name, typeof(X).name), true else string(typeof(X)), false end @@ -529,7 +529,7 @@ function typeinfo_prefix(io::IO, X) elseif !isempty(X) && typeinfo_implicit(eltype_X) "", true elseif print_without_params(eltype_X) - string(unwrap_unionall(eltype_X).name), false # Print "Array" rather than "Array{T,N}" + sprint(show_type_name, unwrap_unionall(eltype_X).name), false # Print "Array" rather than "Array{T,N}" else string(eltype_X), false end diff --git a/base/essentials.jl b/base/essentials.jl index 6a74e99c32a24..3b4b03f91179a 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -750,7 +750,7 @@ of a general iterator are normally considered its "values". julia> d = Dict("a"=>1, "b"=>2); julia> values(d) -Base.ValueIterator for a Dict{String,Int64} with 2 entries. Values: +ValueIterator for a Dict{String,Int64} with 2 entries. Values: 2 1 diff --git a/base/show.jl b/base/show.jl index 300acd186e14b..ddf5091d6b5fe 100644 --- a/base/show.jl +++ b/base/show.jl @@ -499,7 +499,7 @@ function show(io::IO, @nospecialize(x::Type)) show_datatype(io, x) return elseif x isa Union - if x.a isa DataType && Core.Compiler.typename(x.a) === Core.Compiler.typename(DenseArray) + if x.a isa DataType && x.a.name === typename(DenseArray) T, N = x.a.parameters if x == StridedArray{T,N} print(io, "StridedArray") @@ -522,7 +522,7 @@ function show(io::IO, @nospecialize(x::Type)) x::UnionAll if print_without_params(x) - return show(io, unwrap_unionall(x).name) + return show_type_name(io, unwrap_unionall(x).name) end if x.var.name === :_ || io_has_tvar_name(io, x.var.name, x) @@ -593,6 +593,7 @@ function show_type_name(io::IO, tn::Core.TypeName) show_sym(io, sym) quo && print(io, ")") globfunc && print(io, ")") + nothing end function show_datatype(io::IO, x::DataType) @@ -647,7 +648,9 @@ macro show(exs...) end function show(io::IO, tn::Core.TypeName) + print(io, "typename(") show_type_name(io, tn) + print(io, ")") end show(io::IO, ::Nothing) = print(io, "nothing") diff --git a/src/rtutils.c b/src/rtutils.c index 731a34294940d..5500d66a98a50 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -813,6 +813,11 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, " where "); n += jl_static_show_x(out, (jl_value_t*)ua->var, depth->prev); } + else if (vt == jl_typename_type) { + n += jl_printf(out, "typename("); + n += jl_static_show_x(out, jl_unwrap_unionall(((jl_typename_t*)v)->wrapper), depth); + n += jl_printf(out, ")"); + } else if (vt == jl_tvar_type) { // show type-var bounds only if they aren't going to be printed by UnionAll later jl_tvar_t *var = (jl_tvar_t*)v; diff --git a/test/dict.jl b/test/dict.jl index c281c0cda9424..176314671f937 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1062,20 +1062,20 @@ end @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry: …" show(io, MIME"text/plain"(), keys(d)) @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys: …" + "KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys: …" io = IOContext(io, :displaysize => (5, 80)) show(io, MIME"text/plain"(), d) @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry:\n 1 => 2" show(io, MIME"text/plain"(), keys(d)) @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys:\n 1" + "KeySet for a Base.ImmutableDict{$Int,$Int} with 1 entry. Keys:\n 1" d = Base.ImmutableDict(d, 3=>4) show(io, MIME"text/plain"(), d) @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 2 entries:\n ⋮ => ⋮" show(io, MIME"text/plain"(), keys(d)) @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n ⋮" + "KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n ⋮" io = IOContext(io, :displaysize => (6, 80)) show(io, MIME"text/plain"(), d) @@ -1083,14 +1083,14 @@ end "Base.ImmutableDict{$Int,$Int} with 2 entries:\n 3 => 4\n 1 => 2" show(io, MIME"text/plain"(), keys(d)) @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n 3\n 1" + "KeySet for a Base.ImmutableDict{$Int,$Int} with 2 entries. Keys:\n 3\n 1" d = Base.ImmutableDict(d, 5=>6) show(io, MIME"text/plain"(), d) @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 3 entries:\n 5 => 6\n ⋮ => ⋮" show(io, MIME"text/plain"(), keys(d)) @test String(take!(buf)) == - "Base.KeySet for a Base.ImmutableDict{$Int,$Int} with 3 entries. Keys:\n 5\n ⋮" + "KeySet for a Base.ImmutableDict{$Int,$Int} with 3 entries. Keys:\n 5\n ⋮" end @testset "copy!" begin diff --git a/test/show.jl b/test/show.jl index fc186ec5081e5..ac628739fd5bf 100644 --- a/test/show.jl +++ b/test/show.jl @@ -616,6 +616,8 @@ end @test_repr "Array{<:Real}" @test_repr "Array{>:Real}" +@test repr(Base.typename(Array)) == "typename(Array)" + let oldout = stdout, olderr = stderr local rdout, wrout, rderr, wrerr, out, err, rd, wr, io try From 4d062a514a178f54fccc6c81aec3fcfb7dbd501f Mon Sep 17 00:00:00 2001 From: Jetafull Date: Fri, 19 Jun 2020 04:45:37 +0900 Subject: [PATCH 201/232] Pass IO to helpmode function (#36289) --- stdlib/REPL/src/REPL.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 378dfb1413118..3589681f32f7c 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -939,8 +939,10 @@ function setup_interface( (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, - # When we're done transform the entered line into a call to help("$line") - on_done = respond(helpmode, repl, julia_prompt, pass_empty=true, suppress_on_semicolon=false)) + # When we're done transform the entered line into a call to helpmode function + on_done = respond(line->helpmode(outstream(repl), line), repl, julia_prompt, + pass_empty=true, suppress_on_semicolon=false)) + # Set up shell mode shell_mode = Prompt("shell> "; From 5142abf4507525a7a02e7d984ce39802ee4fe134 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Thu, 18 Jun 2020 21:37:22 -0400 Subject: [PATCH 202/232] Add Release Notes back to the html version in the same place (#36333) as before and to the PDF version as well. In the PDF version, they are at the back of the document as the last Part. --- doc/make.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/make.jl b/doc/make.jl index 35a252da1a7b7..112dbcae63126 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -155,15 +155,17 @@ const PAGES = [ "Manual" => ["index.md", Manual...], "Base" => BaseDocs, "Standard Library" => StdlibDocs, - "Developer Documentation" => DevDocs + "Developer Documentation" => DevDocs, + hide("NEWS.md"), ] else const PAGES = [ "Julia Documentation" => "index.md", + hide("NEWS.md"), "Manual" => Manual, "Base" => BaseDocs, "Standard Library" => StdlibDocs, - "Developer Documentation" => DevDocs + "Developer Documentation" => DevDocs, ] end From d6d5208d66be637543232bec4b0c79b2811ea1c6 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 18 Jun 2020 23:52:02 -0400 Subject: [PATCH 203/232] use ReturnNode, GotoIfNot, and Argument more consistently in IR (#36318) --- base/boot.jl | 21 ++++- base/compiler/abstractinterpretation.jl | 10 +-- base/compiler/optimize.jl | 33 +++----- base/compiler/ssair/driver.jl | 18 +---- base/compiler/ssair/ir.jl | 39 +-------- base/compiler/ssair/legacy.jl | 29 +------ base/compiler/ssair/show.jl | 6 +- base/compiler/ssair/slot2ssa.jl | 3 +- base/compiler/typeinfer.jl | 24 ++++-- base/compiler/utilities.jl | 12 ++- base/compiler/validation.jl | 32 ++++---- doc/src/devdocs/ast.md | 25 +++--- src/ast.c | 10 ++- src/builtins.c | 3 + src/codegen.cpp | 97 ++++++++++++++--------- src/dump.c | 8 +- src/interpreter.c | 31 ++++---- src/ircode.c | 26 +++++- src/jltypes.c | 24 ++++-- src/julia.h | 9 +++ src/julia_internal.h | 2 +- src/method.c | 22 ++++- src/serialize.h | 5 +- src/staticdata.c | 2 +- src/toplevel.c | 8 +- stdlib/InteractiveUtils/test/runtests.jl | 2 +- stdlib/Serialization/src/Serialization.jl | 20 ++++- test/compiler/contextual.jl | 8 +- test/compiler/inference.jl | 41 +++++----- test/compiler/inline.jl | 13 +-- test/compiler/interpreter_exec.jl | 15 ++-- test/compiler/irpasses.jl | 30 +++---- test/compiler/ssair.jl | 4 +- test/compiler/validation.jl | 12 +-- test/core.jl | 2 +- test/syntax.jl | 4 +- 36 files changed, 354 insertions(+), 296 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index c2c8c35c34a87..86f0b6bfcbdc9 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -103,6 +103,15 @@ # label::Int #end +#struct GotoIfNot +# cond::Any +# dest::Int +#end + +#struct ReturnNode +# val::Any +#end + #struct PiNode # val # typ @@ -368,6 +377,10 @@ _new(:GotoNode, :Int) _new(:NewvarNode, :SlotNumber) _new(:QuoteNode, :Any) _new(:SSAValue, :Int) +_new(:Argument, :Int) +_new(:ReturnNode, :Any) +eval(Core, :(ReturnNode() = $(Expr(:new, :ReturnNode)))) # unassigned val indicates unreachable +eval(Core, :(GotoIfNot(@nospecialize(cond), dest::Int) = $(Expr(:new, :GotoIfNot, :cond, :dest)))) eval(Core, :(LineNumberNode(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) eval(Core, :(LineNumberNode(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) LineNumberNode(l::Int, f::String) = LineNumberNode(l, Symbol(f)) @@ -453,12 +466,12 @@ Symbol(s::Symbol) = s # module providing the IR object model module IR -export CodeInfo, MethodInstance, CodeInstance, GotoNode, - NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, +export CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode, + NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, Argument, PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode -import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, - NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, +import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode, + NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, Argument, PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index f8d657fc6e00f..2faffbadb6e50 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1174,13 +1174,13 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) changes[sn] = VarState(Bottom, true) elseif isa(stmt, GotoNode) pc´ = (stmt::GotoNode).label - elseif hd === :gotoifnot - condt = abstract_eval(interp, stmt.args[1], s[pc], frame) + elseif isa(stmt, GotoIfNot) + condt = abstract_eval(interp, stmt.cond, s[pc], frame) if condt === Bottom break end condval = maybe_extract_const_bool(condt) - l = stmt.args[2]::Int + l = stmt.dest::Int # constant conditions if condval === true elseif condval === false @@ -1207,9 +1207,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) s[l] = newstate_else end end - elseif hd === :return + elseif isa(stmt, ReturnNode) pc´ = n + 1 - rt = widenconditional(abstract_eval(interp, stmt.args[1], s[pc], frame)) + rt = widenconditional(abstract_eval(interp, stmt.val, s[pc], frame)) if !isa(rt, Const) && !isa(rt, Type) && !isa(rt, PartialStruct) # only propagate information we know we can store # and is valid inter-procedurally diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 120d010937ff4..10be5d97ecafc 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -339,12 +339,6 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any} # prevent inlining. extyp = line == -1 ? Any : src.ssavaluetypes[line] return extyp === Union{} ? 0 : 20 - elseif head === :return - a = ex.args[1] - if a isa Expr - return statement_cost(a, -1, src, sptypes, slottypes, params) - end - return 0 elseif head === :(=) if ex.args[1] isa GlobalRef cost = 20 @@ -364,12 +358,6 @@ function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any} # since these aren't usually performance-sensitive functions, # and llvm is more likely to miscompile them when these functions get large return typemax(Int) - elseif head === :gotoifnot - target = ex.args[2]::Int - # loops are generally always expensive - # but assume that forward jumps are already counted for from - # summing the cost of the not-taken branch - return target < line ? 40 : 0 end return 0 end @@ -386,6 +374,8 @@ function inline_worthy(body::Array{Any,1}, src::CodeInfo, sptypes::Vector{Any}, # but assume that forward jumps are already counted for from # summing the cost of the not-taken branch thiscost = stmt.label < line ? 40 : 0 + elseif stmt isa GotoIfNot + thiscost = stmt.dest < line ? 40 : 0 else continue end @@ -423,20 +413,23 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab el = body[i] if isa(el, GotoNode) body[i] = GotoNode(el.label + labelchangemap[el.label]) + elseif isa(el, GotoIfNot) + cond = el.cond + if isa(cond, SSAValue) + cond = SSAValue(cond.id + ssachangemap[cond.id]) + end + body[i] = GotoIfNot(cond, el.dest + labelchangemap[el.dest]) + elseif isa(el, ReturnNode) + if isdefined(el, :val) && isa(el.val, SSAValue) + body[i] = ReturnNode(SSAValue(el.val.id + ssachangemap[el.val.id])) + end elseif isa(el, SSAValue) body[i] = SSAValue(el.id + ssachangemap[el.id]) elseif isa(el, Expr) if el.head === :(=) && el.args[2] isa Expr el = el.args[2]::Expr end - if el.head === :gotoifnot - cond = el.args[1] - if isa(cond, SSAValue) - el.args[1] = SSAValue(cond.id + ssachangemap[cond.id]) - end - tgt = el.args[2]::Int - el.args[2] = tgt + labelchangemap[tgt] - elseif el.head === :enter + if el.head === :enter tgt = el.args[1]::Int el.args[1] = tgt + labelchangemap[tgt] elseif !is_meta_expr_head(el.head) diff --git a/base/compiler/ssair/driver.jl b/base/compiler/ssair/driver.jl index 4fd1bdee5d658..b6fd4a788a5ce 100644 --- a/base/compiler/ssair/driver.jl +++ b/base/compiler/ssair/driver.jl @@ -20,18 +20,6 @@ include("compiler/ssair/verify.jl") include("compiler/ssair/legacy.jl") #@isdefined(Base) && include("compiler/ssair/show.jl") -function normalize_expr(stmt::Expr) - if stmt.head === :gotoifnot - return GotoIfNot(stmt.args[1], stmt.args[2]::Int) - elseif stmt.head === :return - return (length(stmt.args) == 0) ? ReturnNode(nothing) : ReturnNode(stmt.args[1]) - elseif stmt.head === :unreachable - return ReturnNode() - else - return stmt - end -end - function normalize(@nospecialize(stmt), meta::Vector{Any}) if isa(stmt, Expr) if stmt.head === :meta @@ -40,10 +28,6 @@ function normalize(@nospecialize(stmt), meta::Vector{Any}) push!(meta, stmt) end return nothing - elseif stmt.head === :line - return nothing # deprecated - we shouldn't encounter this - else - return normalize_expr(stmt) end end return stmt @@ -72,7 +56,7 @@ function convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, narg prevloc = codeloc end if code[idx] isa Expr && ci.ssavaluetypes[idx] === Union{} - if !(idx < length(code) && isexpr(code[idx + 1], :unreachable)) + if !(idx < length(code) && isa(code[idx + 1], ReturnNode) && !isdefined((code[idx + 1]::ReturnNode), :val)) # insert unreachable in the same basic block after the current instruction (splitting it) insert!(code, idx + 1, ReturnNode()) insert!(ci.codelocs, idx + 1, ci.codelocs[idx]) diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 6bef646790d0c..9204590fb882b 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -4,23 +4,6 @@ @eval Core.UpsilonNode() = $(Expr(:new, Core.UpsilonNode)) Core.PhiNode() = Core.PhiNode(Any[], Any[]) -struct Argument - n::Int -end - -struct GotoIfNot - cond::Any - dest::Int - GotoIfNot(@nospecialize(cond), dest::Int) = new(cond, dest) -end - -struct ReturnNode - val::Any - ReturnNode(@nospecialize(val)) = new(val) - # unassigned val indicates unreachable - ReturnNode() = new() -end - """ Like UnitRange{Int}, but can handle the `last` field, being temporarily < first (this can happen during compacting) @@ -85,14 +68,6 @@ function basic_blocks_starts(stmts::Vector{Any}) push!(jump_dests, idx+1) # The catch block is a jump dest push!(jump_dests, stmt.args[1]::Int) - elseif stmt.head === :gotoifnot - # also tolerate expr form of IR - push!(jump_dests, idx+1) - push!(jump_dests, stmt.args[2]::Int) - elseif stmt.head === :return - # also tolerate expr form of IR - # This is a fake dest to force the next stmt to start a bb - idx < length(stmts) && push!(jump_dests, idx+1) end end if isa(stmt, PhiNode) @@ -129,7 +104,7 @@ function compute_basic_blocks(stmts::Vector{Any}) # Compute successors/predecessors for (num, b) in enumerate(blocks) terminator = stmts[last(b.stmts)] - if isa(terminator, ReturnNode) || isexpr(terminator, :return) + if isa(terminator, ReturnNode) # return never has any successors continue end @@ -161,15 +136,6 @@ function compute_basic_blocks(stmts::Vector{Any}) push!(blocks[block′].preds, num) push!(blocks[block′].preds, 0) push!(b.succs, block′) - elseif terminator.head === :gotoifnot - block′ = block_for_inst(basic_block_index, terminator.args[2]::Int) - if block′ == num + 1 - # This GotoIfNot acts like a noop - treat it as such. - # We will drop it during SSA renaming - else - push!(blocks[block′].preds, num) - push!(b.succs, block′) - end end end # statement fall-through @@ -396,8 +362,7 @@ function is_relevant_expr(e::Expr) :gc_preserve_begin, :gc_preserve_end, :foreigncall, :isdefined, :copyast, :undefcheck, :throw_undef_if_not, - :cfunction, :method, :pop_exception, - #=legacy IR format support=# :gotoifnot, :return) + :cfunction, :method, :pop_exception) end function setindex!(x::UseRef, @nospecialize(v)) diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 14283e789312e..c36cb73fac828 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -12,22 +12,9 @@ end function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) code = copy_exprargs(ci.code) # TODO: this is a huge hot-spot - for i = 1:length(code) - if isa(code[i], Expr) - code[i] = normalize_expr(code[i]) - end - end cfg = compute_basic_blocks(code) for i = 1:length(code) stmt = code[i] - urs = userefs(stmt) - for op in urs - val = op[] - if isa(val, SlotNumber) - op[] = Argument(val.id) - end - end - stmt = urs[] # Translate statement edges to bb_edges if isa(stmt, GotoNode) code[i] = GotoNode(block_for_inst(cfg, stmt.label)) @@ -67,26 +54,12 @@ function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) # (and undo normalization for now) for i = 1:length(ci.code) stmt = ci.code[i] - urs = userefs(stmt) - for op in urs - val = op[] - if isa(val, Argument) - op[] = SlotNumber(val.n) - end - end - stmt = urs[] if isa(stmt, GotoNode) stmt = GotoNode(first(ir.cfg.blocks[stmt.label].stmts)) elseif isa(stmt, GotoIfNot) - stmt = Expr(:gotoifnot, stmt.cond, first(ir.cfg.blocks[stmt.dest].stmts)) + stmt = GotoIfNot(stmt.cond, first(ir.cfg.blocks[stmt.dest].stmts)) elseif isa(stmt, PhiNode) stmt = PhiNode(Any[last(ir.cfg.blocks[edge::Int].stmts) for edge in stmt.edges], stmt.values) - elseif isa(stmt, ReturnNode) - if isdefined(stmt, :val) - stmt = Expr(:return, stmt.val) - else - stmt = Expr(:unreachable) - end elseif isa(stmt, Expr) && stmt.head === :enter stmt.args[1] = first(ir.cfg.blocks[stmt.args[1]::Int].stmts) end diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index bc77095b45d0a..62df1ccb60e46 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -141,7 +141,7 @@ end function should_print_ssa_type(@nospecialize node) if isa(node, Expr) - return !(node.head in (:gc_preserve_begin, :gc_preserve_end, :meta, :return, :enter, :leave)) + return !(node.head in (:gc_preserve_begin, :gc_preserve_end, :meta, :enter, :leave)) end return !isa(node, PiNode) && !isa(node, GotoIfNot) && !isa(node, GotoNode) && !isa(node, ReturnNode) && @@ -722,9 +722,7 @@ function show_ir_stmt(io::IO, code::CodeInfo, idx::Int, line_info_preprinter, li print(io, inlining_indent, " ") # convert statement index to labels, as expected by print_stmt if stmt isa Expr - if stmt.head === :gotoifnot && length(stmt.args) == 2 && stmt.args[2] isa Int - stmt = GotoIfNot(stmt.args[1], block_for_inst(cfg, stmt.args[2]::Int)) - elseif stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int + if stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int stmt = Expr(:enter, block_for_inst(cfg, stmt.args[1]::Int)) end elseif isa(stmt, GotoIfNot) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 0255dab82ce5a..0de11fd01f084 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -43,7 +43,6 @@ function lift_defuse(cfg::CFG, defuse) end end -@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id function scan_slot_def_use(nargs::Int, ci::CodeInfo, code::Vector{Any}) nslots = length(ci.slotflags) result = SlotInfo[SlotInfo() for i = 1:nslots] @@ -821,7 +820,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg elseif isexpr(stmt, :enter) new_code[idx] = Expr(:enter, block_for_inst(cfg, stmt.args[1])) ssavalmap[idx] = SSAValue(idx) # Slot to store token for pop_exception - elseif isexpr(stmt, :leave) || isexpr(stmt, :(=)) || isexpr(stmt, :return) || + elseif isexpr(stmt, :leave) || isexpr(stmt, :(=)) || isa(stmt, ReturnNode) || isexpr(stmt, :meta) || isa(stmt, NewvarNode) new_code[idx] = stmt else diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 12b3da4a4e2db..85da23bcbcf82 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -251,6 +251,15 @@ function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, unde end end +function annotate_slot_load(@nospecialize(e), vtypes::VarTable, sv::InferenceState, undefs::Array{Bool,1}) + if isa(e, Expr) + annotate_slot_load!(e, vtypes, sv, undefs) + elseif isa(e, Slot) + return visit_slot_load!(e, vtypes, sv, undefs) + end + return e +end + function visit_slot_load!(sl::Slot, vtypes::VarTable, sv::InferenceState, undefs::Array{Bool,1}) id = slot_id(sl) s = vtypes[id] @@ -330,13 +339,12 @@ function type_annotate!(sv::InferenceState) body = src.code::Array{Any,1} nexpr = length(body) - # replace gotoifnot with its condition if the branch target is unreachable + # replace GotoIfNot with its condition if the branch target is unreachable for i = 1:nexpr expr = body[i] - if isa(expr, Expr) && expr.head === :gotoifnot - tgt = expr.args[2]::Int - if !isa(states[tgt], VarTable) - body[i] = expr.args[1] + if isa(expr, GotoIfNot) + if !isa(states[expr.dest], VarTable) + body[i] = expr.cond end end end @@ -353,6 +361,10 @@ function type_annotate!(sv::InferenceState) # st_i === nothing => unreached statement (see issue #7836) if isa(expr, Expr) annotate_slot_load!(expr, st_i, sv, undefs) + elseif isa(expr, ReturnNode) && isdefined(expr, :val) + body[i] = ReturnNode(annotate_slot_load(expr.val, st_i, sv, undefs)) + elseif isa(expr, GotoIfNot) + body[i] = GotoIfNot(annotate_slot_load(expr.cond, st_i, sv, undefs), expr.dest) elseif isa(expr, Slot) body[i] = visit_slot_load!(expr, st_i, sv, undefs) end @@ -549,7 +561,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance) if invoke_api(code) == 2 i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) - tree.code = Any[ Expr(:return, quoted(code.rettype_const)) ] + tree.code = Any[ ReturnNode(quoted(code.rettype_const)) ] nargs = Int(method.nargs) tree.slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), method.slot_syms) tree.slotflags = fill(0x00, nargs) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 84ca65834726f..5206eeebc349e 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -166,7 +166,9 @@ function argextype(@nospecialize(x), src, sptypes::Vector{Any}, slottypes::Vecto elseif isa(x, SSAValue) return abstract_eval_ssavalue(x::SSAValue, src) elseif isa(x, Argument) - return isa(src, IncrementalCompact) ? src.ir.argtypes[x.n] : src.argtypes[x.n] + return isa(src, IncrementalCompact) ? src.ir.argtypes[x.n] : + isa(src, IRCode) ? src.argtypes[x.n] : + slottypes[x.n] elseif isa(x, QuoteNode) return AbstractEvalConstant((x::QuoteNode).value) elseif isa(x, GlobalRef) @@ -188,6 +190,11 @@ function find_ssavalue_uses(body::Vector{Any}, nvals::Int) uses = BitSet[ BitSet() for i = 1:nvals ] for line in 1:length(body) e = body[line] + if isa(e, ReturnNode) + e = e.val + elseif isa(e, GotoIfNot) + e = e.cond + end if isa(e, SSAValue) push!(uses[e.id], line) elseif isa(e, Expr) @@ -213,7 +220,8 @@ function find_ssavalue_uses(e::Expr, uses::Vector{BitSet}, line::Int) end # using a function to ensure we can infer this -@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id +@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : + isa(s, Argument) ? (s::Argument).n : (s::TypedSlot).id ########### # options # diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index ad1208679d71b..018d9eb82ece3 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -5,15 +5,12 @@ const VALID_EXPR_HEADS = IdDict{Any,Any}( :call => 1:typemax(Int), :invoke => 2:typemax(Int), :static_parameter => 1:1, - :gotoifnot => 2:2, :(&) => 1:1, :(=) => 2:2, :method => 1:4, :const => 1:1, :new => 1:typemax(Int), :splatnew => 2:2, - :return => 1:1, - :unreachable => 0:0, :the_exception => 0:0, :enter => 1:1, :leave => 1:1, @@ -39,7 +36,7 @@ const INVALID_EXPR_HEAD = "invalid expression head" const INVALID_EXPR_NARGS = "invalid number of expression args" const INVALID_LVALUE = "invalid LHS value" const INVALID_RVALUE = "invalid RHS value" -const INVALID_RETURN = "invalid argument to :return" +const INVALID_RETURN = "invalid argument to return" const INVALID_CALL_ARG = "invalid :call argument" const EMPTY_SLOTNAMES = "slotnames field is empty" const SLOTFLAGS_MISMATCH = "length(slotnames) < length(slotflags)" @@ -130,22 +127,12 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ end validate_val!(lhs) validate_val!(rhs) - elseif head === :gotoifnot - if !is_valid_argument(x.args[1]) - push!(errors, InvalidCodeError(INVALID_CALL_ARG, x.args[1])) - end - validate_val!(x.args[1]) - elseif head === :return - if !is_valid_return(x.args[1]) - push!(errors, InvalidCodeError(INVALID_RETURN, x.args[1])) - end - validate_val!(x.args[1]) elseif head === :call || head === :invoke || head === :gc_preserve_end || head === :meta || head === :inbounds || head === :foreigncall || head === :cfunction || head === :const || head === :enter || head === :leave || head === :pop_exception || head === :method || head === :global || head === :static_parameter || head === :new || head === :splatnew || head === :thunk || head === :loopinfo || - head === :throw_undef_if_not || head === :unreachable || head === :code_coverage_effect + head === :throw_undef_if_not || head === :code_coverage_effect validate_val!(x) else # TODO: nothing is actually in statement position anymore @@ -153,8 +140,21 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ end elseif isa(x, NewvarNode) elseif isa(x, GotoNode) + elseif isa(x, GotoIfNot) + if !is_valid_argument(x.cond) + push!(errors, InvalidCodeError(INVALID_CALL_ARG, x.cond)) + end + validate_val!(x.cond) + elseif isa(x, ReturnNode) + if isdefined(x, :val) + if !is_valid_return(x.val) + push!(errors, InvalidCodeError(INVALID_RETURN, x.val)) + end + validate_val!(x.val) + end elseif x === nothing elseif isa(x, SlotNumber) + elseif isa(x, Argument) elseif isa(x, GlobalRef) elseif isa(x, LineNumberNode) elseif isa(x, PiNode) @@ -213,7 +213,7 @@ validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...) is_valid_lvalue(@nospecialize(x)) = isa(x, Slot) || isa(x, GlobalRef) function is_valid_argument(@nospecialize(x)) - if isa(x, Slot) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) || + if isa(x, Slot) || isa(x, Argument) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) || (isa(x,Expr) && (x.head in (:static_parameter, :boundscheck))) || isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) || isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index cf95b5a0eec88..87a8daaf0fa4b 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -247,7 +247,7 @@ types exist in lowered form: Has a node type indicated by the `head` field, and an `args` field which is a `Vector{Any}` of subexpressions. While almost every part of a surface AST is represented by an `Expr`, the IR uses only a - limited number of `Expr`s, mostly for calls, conditional branches (`gotoifnot`), and returns. + limited number of `Expr`s, mostly for calls and some top-level-only forms. * `Slot` @@ -258,6 +258,11 @@ types exist in lowered form: Slots that require per-use type annotations are represented with `TypedSlot`, which has a `typ` field. + * `Argument` + + The same as `SlotNumber`, but appears only post-optimization. Indicates that the + referenced slot is an argument of the enclosing function. + * `CodeInfo` Wraps the IR of a group of statements. Its `code` field is an array of expressions to execute. @@ -267,6 +272,16 @@ types exist in lowered form: Unconditional branch. The argument is the branch target, represented as an index in the code array to jump to. + * `GotoIfNot` + + Conditional branch. If the `cond` field evaluates to false, goes to the index identified + by the `dest` field. + + * `ReturnNode` + + Returns its argument (the `val` field) as the value of the enclosing function. + If the `val` field is undefined, then this represents an unreachable statement. + * `QuoteNode` Wraps an arbitrary value to reference as data. For example, the function `f() = :a` contains a @@ -305,10 +320,6 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. Reference a static parameter by index. - * `gotoifnot` - - Conditional branch. If `args[1]` is false, goes to the index identified in `args[2]`. - * `=` Assignment. In the IR, the first argument is always a Slot or a GlobalRef. @@ -415,10 +426,6 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. Similar to `new`, except field values are passed as a single tuple. Works similarly to `Base.splat(new)` if `new` were a first-class function, hence the name. - * `return` - - Returns its argument as the value of the enclosing function. - * `isdefined` `Expr(:isdefined, :x)` returns a Bool indicating whether `x` has diff --git a/src/ast.c b/src/ast.c index 51cf262ad9ff2..7add2573a63c6 100644 --- a/src/ast.c +++ b/src/ast.c @@ -34,7 +34,7 @@ jl_sym_t *export_sym; jl_sym_t *import_sym; jl_sym_t *toplevel_sym; jl_sym_t *quote_sym; jl_sym_t *line_sym; jl_sym_t *jl_incomplete_sym; jl_sym_t *goto_sym; jl_sym_t *goto_ifnot_sym; -jl_sym_t *return_sym; jl_sym_t *unreachable_sym; +jl_sym_t *return_sym; jl_sym_t *lambda_sym; jl_sym_t *assign_sym; jl_sym_t *globalref_sym; jl_sym_t *do_sym; jl_sym_t *method_sym; jl_sym_t *core_sym; @@ -344,7 +344,6 @@ void jl_init_common_symbols(void) goto_sym = jl_symbol("goto"); goto_ifnot_sym = jl_symbol("gotoifnot"); return_sym = jl_symbol("return"); - unreachable_sym = jl_symbol("unreachable"); lambda_sym = jl_symbol("lambda"); module_sym = jl_symbol("module"); export_sym = jl_symbol("export"); @@ -555,11 +554,16 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, jl_module_t *m JL_GC_POP(); return temp; } - JL_GC_PUSH1(&ex); + JL_GC_PUSH2(&ex, &temp); if (sym == goto_sym) { ex = scm_to_julia_(fl_ctx, car_(e), mod); temp = jl_new_struct(jl_gotonode_type, ex); } + else if (sym == goto_ifnot_sym) { + ex = scm_to_julia_(fl_ctx, car_(e), mod); + temp = scm_to_julia(fl_ctx, car_(cdr_(e)), mod); + temp = jl_new_struct(jl_gotoifnot_type, ex, temp); + } else if (sym == newvar_sym) { ex = scm_to_julia_(fl_ctx, car_(e), mod); temp = jl_new_struct(jl_newvarnode_type, ex); diff --git a/src/builtins.c b/src/builtins.c index 3ea207cf7d55a..5457adae0f64e 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1561,6 +1561,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("Slot", (jl_value_t*)jl_abstractslot_type); add_builtin("SlotNumber", (jl_value_t*)jl_slotnumber_type); add_builtin("TypedSlot", (jl_value_t*)jl_typedslot_type); + add_builtin("Argument", (jl_value_t*)jl_argument_type); add_builtin("IntrinsicFunction", (jl_value_t*)jl_intrinsic_type); add_builtin("Function", (jl_value_t*)jl_function_type); add_builtin("Builtin", (jl_value_t*)jl_builtin_type); @@ -1579,6 +1580,8 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("LineNumberNode", (jl_value_t*)jl_linenumbernode_type); add_builtin("LineInfoNode", (jl_value_t*)jl_lineinfonode_type); add_builtin("GotoNode", (jl_value_t*)jl_gotonode_type); + add_builtin("GotoIfNot", (jl_value_t*)jl_gotoifnot_type); + add_builtin("ReturnNode", (jl_value_t*)jl_returnnode_type); add_builtin("PiNode", (jl_value_t*)jl_pinode_type); add_builtin("PhiNode", (jl_value_t*)jl_phinode_type); add_builtin("PhiCNode", (jl_value_t*)jl_phicnode_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index 0dea4c8b35316..5fecee611e82c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1934,7 +1934,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex, int sparams=tr return jl_get_global(ctx.module, sym); return NULL; } - if (jl_is_slot(ex)) + if (jl_is_slot(ex) || jl_is_argument(ex)) return NULL; if (jl_is_ssavalue(ex)) { ssize_t idx = ((jl_ssavalue_t*)ex)->id - 1; @@ -2030,7 +2030,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex, int sparams=tr static bool slot_eq(jl_value_t *e, int sl) { - return jl_is_slot(e) && jl_slot_number(e)-1 == sl; + return (jl_is_slot(e) || jl_is_argument(e)) && jl_slot_number(e)-1 == sl; } // --- code gen for intrinsic functions --- @@ -2054,6 +2054,14 @@ static bool local_var_occurs(jl_value_t *e, int sl) return true; } } + else if (jl_is_returnnode(e)) { + jl_value_t *retexpr = jl_returnnode_value(e); + if (retexpr != NULL) + return local_var_occurs(retexpr, sl); + } + else if (jl_is_gotoifnot(e)) { + return local_var_occurs(jl_gotoifnot_cond(e), sl); + } return false; } @@ -2105,7 +2113,7 @@ static void mark_volatile_vars(jl_array_t *stmts, std::vector &slo // to eagerly remove slot assignments that are never read from static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) { - if (jl_is_slot(expr)) { + if (jl_is_slot(expr) || jl_is_argument(expr)) { int i = jl_slot_number(expr) - 1; ctx.slots[i].used = true; } @@ -2129,6 +2137,14 @@ static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) } } } + else if (jl_is_returnnode(expr)) { + jl_value_t *retexpr = jl_returnnode_value(expr); + if (retexpr != NULL) + simple_use_analysis(ctx, retexpr); + } + else if (jl_is_gotoifnot(expr)) { + simple_use_analysis(ctx, jl_gotoifnot_cond(expr)); + } else if (jl_is_pinode(expr)) { simple_use_analysis(ctx, jl_fieldref_noalloc(expr, 0)); } @@ -3519,7 +3535,7 @@ static jl_cgval_t emit_global(jl_codectx_t &ctx, jl_sym_t *sym) static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) { Value *isnull = NULL; - if (jl_is_slot(sym)) { + if (jl_is_slot(sym) || jl_is_argument(sym)) { size_t sl = jl_slot_number(sym) - 1; jl_varinfo_t &vi = ctx.slots[sl]; if (!vi.usedUndef) @@ -3997,6 +4013,9 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result) (void)emit_expr(ctx, expr); return; } + if (jl_is_argument(expr) && ssaval_result == -1) { + return; + } if (jl_is_newvarnode(expr)) { jl_value_t *var = jl_fieldref(expr, 0); assert(jl_is_slot(var)); @@ -4055,7 +4074,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) jl_sym_t *sym = (jl_sym_t*)expr; return emit_global(ctx, sym); } - if (jl_is_slot(expr)) { + if (jl_is_slot(expr) || jl_is_argument(expr)) { return emit_local(ctx, expr); } if (jl_is_ssavalue(expr)) { @@ -4078,6 +4097,9 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) if (jl_is_gotonode(expr)) { jl_error("GotoNode in value position"); } + if (jl_is_gotoifnot(expr)) { + jl_error("GotoIfNot in value position"); + } if (jl_is_pinode(expr)) { return convert_julia_type(ctx, emit_expr(ctx, jl_fieldref_noalloc(expr, 0)), jl_fieldref_noalloc(expr, 1)); } @@ -4202,7 +4224,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) bp = julia_binding_gv(ctx, bnd); bp_owner = literal_pointer_val(ctx, (jl_value_t*)mod); } - else if (jl_is_slot(mn)) { + else if (jl_is_slot(mn) || jl_is_argument(mn)) { int sl = jl_slot_number(mn)-1; jl_varinfo_t &vi = ctx.slots[sl]; bp = vi.boxroot; @@ -4301,7 +4323,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) I->setMetadata("julia.loopinfo", MD); return jl_cgval_t(); } - else if (head == goto_ifnot_sym || head == leave_sym || head == coverageeffect_sym + else if (head == leave_sym || head == coverageeffect_sym || head == pop_exception_sym || head == enter_sym || head == inbounds_sym || head == aliasscope_sym || head == popaliasscope_sym) { jl_errorf("Expr(:%s) in value position", jl_symbol_name(head)); @@ -5517,8 +5539,6 @@ static std::pair, jl_llvm_functions_t> JL_GC_PUSH2(&ctx.code, &ctx.roots); ctx.code = src->code; - //jl_static_show(JL_STDOUT, (jl_value_t*)ast); - //jl_printf(JL_STDOUT, "\n"); std::map labels; ctx.module = jl_is_method(lam->def.method) ? lam->def.method->module : lam->def.module; ctx.linfo = lam; @@ -5667,9 +5687,11 @@ static std::pair, jl_llvm_functions_t> int retarg = -1; for (size_t i = 0; i < jl_array_len(stmts); ++i) { jl_value_t *stmt = jl_array_ptr_ref(stmts, i); - if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == return_sym) { - stmt = jl_exprarg(stmt, 0); - if (!jl_is_slot(stmt)) + if (jl_is_returnnode(stmt)) { + stmt = jl_returnnode_value(stmt); + if (stmt == NULL) + continue; + if (!jl_is_argument(stmt)) return -1; unsigned sl = jl_slot_number(stmt) - 1; if (sl >= nreq) @@ -6359,23 +6381,20 @@ static std::pair, jl_llvm_functions_t> { for (size_t i = 0; i < stmtslen; ++i) { jl_value_t *stmt = jl_array_ptr_ref(stmts, i); - if (jl_is_expr(stmt)) { - if (((jl_expr_t*)stmt)->head == goto_ifnot_sym) { - int dest = jl_unbox_long(jl_array_ptr_ref(((jl_expr_t*)stmt)->args, 1)); - branch_targets.insert(dest); - // The next 1-indexed statement + if (jl_is_gotoifnot(stmt)) { + int dest = jl_gotoifnot_label(stmt); + branch_targets.insert(dest); + // The next 1-indexed statement + branch_targets.insert(i + 2); + } else if (jl_is_returnnode(stmt)) { + // We don't do dead branch elimination before codegen + // so we need to make sure to start a BB after any + // return node, even if they aren't otherwise branch + // targets. + if (i + 2 <= stmtslen) branch_targets.insert(i + 2); - } else if (((jl_expr_t*)stmt)->head == return_sym) { - // We don't do dead branch elimination before codegen - // so we need to make sure to start a BB after any - // return node, even if they aren't otherwise branch - // targets. - if (i + 2 <= stmtslen) - branch_targets.insert(i + 2); - } else if (((jl_expr_t*)stmt)->head == unreachable_sym) { - if (i + 2 <= stmtslen) - branch_targets.insert(i + 2); - } else if (((jl_expr_t*)stmt)->head == enter_sym) { + } else if (jl_is_expr(stmt)) { + if (((jl_expr_t*)stmt)->head == enter_sym) { branch_targets.insert(i + 1); if (i + 2 <= stmtslen) branch_targets.insert(i + 2); @@ -6419,15 +6438,16 @@ static std::pair, jl_llvm_functions_t> ctx.aliasscope = aliasscopes[cursor]; jl_value_t *stmt = jl_array_ptr_ref(stmts, cursor); jl_expr_t *expr = jl_is_expr(stmt) ? (jl_expr_t*)stmt : nullptr; - if (expr && expr->head == unreachable_sym) { - ctx.builder.CreateUnreachable(); - find_next_stmt(-1); - continue; - } - if (expr && expr->head == return_sym) { + if (jl_is_returnnode(stmt)) { + jl_value_t *retexpr = jl_returnnode_value(stmt); + if (retexpr == NULL) { + ctx.builder.CreateUnreachable(); + find_next_stmt(-1); + continue; + } // this is basically a copy of emit_assignment, // but where the assignment slot is the retval - jl_cgval_t retvalinfo = emit_expr(ctx, jl_exprarg(expr, 0)); + jl_cgval_t retvalinfo = emit_expr(ctx, retexpr); retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype); if (retvalinfo.typ == jl_bottom_type) { ctx.builder.CreateUnreachable(); @@ -6557,10 +6577,9 @@ static std::pair, jl_llvm_functions_t> find_next_stmt(cursor + 1); continue; } - if (expr && expr->head == goto_ifnot_sym) { - jl_value_t **args = (jl_value_t**)jl_array_data(expr->args); - jl_value_t *cond = args[0]; - int lname = jl_unbox_long(args[1]); + if (jl_is_gotoifnot(stmt)) { + jl_value_t *cond = jl_gotoifnot_cond(stmt); + int lname = jl_gotoifnot_label(stmt); Value *isfalse = emit_condition(ctx, cond, "if"); mallocVisitStmt(debuginfoloc, nullptr); come_from_bb[cursor+1] = ctx.builder.GetInsertBlock(); diff --git a/src/dump.c b/src/dump.c index 35303932bc161..06d7042f86072 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2570,7 +2570,7 @@ void jl_init_serializer(void) void *vals[] = { jl_emptysvec, jl_emptytuple, jl_false, jl_true, jl_nothing, jl_any_type, call_sym, invoke_sym, goto_ifnot_sym, return_sym, jl_symbol("tuple"), - unreachable_sym, jl_an_empty_string, jl_an_empty_vec_any, + jl_an_empty_string, jl_an_empty_vec_any, // empirical list of very common symbols #include "common_symbols1.inc" @@ -2582,7 +2582,6 @@ void jl_init_serializer(void) jl_box_int32(12), jl_box_int32(13), jl_box_int32(14), jl_box_int32(15), jl_box_int32(16), jl_box_int32(17), jl_box_int32(18), jl_box_int32(19), jl_box_int32(20), - jl_box_int32(21), jl_box_int64(0), jl_box_int64(1), jl_box_int64(2), jl_box_int64(3), jl_box_int64(4), jl_box_int64(5), @@ -2591,7 +2590,7 @@ void jl_init_serializer(void) jl_box_int64(12), jl_box_int64(13), jl_box_int64(14), jl_box_int64(15), jl_box_int64(16), jl_box_int64(17), jl_box_int64(18), jl_box_int64(19), jl_box_int64(20), - jl_box_int64(21), jl_box_int64(22), + jl_box_int64(21), jl_bool_type, jl_linenumbernode_type, jl_pinode_type, jl_upsilonnode_type, jl_type_type, jl_bottom_type, jl_ref_type, @@ -2641,6 +2640,9 @@ void jl_init_serializer(void) deser_tag[TAG_UNIONALL] = (jl_value_t*)jl_unionall_type; deser_tag[TAG_GOTONODE] = (jl_value_t*)jl_gotonode_type; deser_tag[TAG_QUOTENODE] = (jl_value_t*)jl_quotenode_type; + deser_tag[TAG_GOTOIFNOT] = (jl_value_t*)jl_gotoifnot_type; + deser_tag[TAG_RETURNNODE] = (jl_value_t*)jl_returnnode_type; + deser_tag[TAG_ARGUMENT] = (jl_value_t*)jl_argument_type; intptr_t i = 0; while (vals[i] != NULL) { diff --git a/src/interpreter.c b/src/interpreter.c index 7f52019e90d38..0f753346e07d7 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -168,7 +168,7 @@ static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) else return s->locals[jl_source_nslots(src) + id]; } - if (jl_is_slot(e)) { + if (jl_is_slot(e) || jl_is_argument(e)) { ssize_t n = jl_slot_number(e); if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL) jl_error("access to invalid slot number"); @@ -211,7 +211,7 @@ static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) else if (head == isdefined_sym) { jl_value_t *sym = args[0]; int defined = 0; - if (jl_is_slot(sym)) { + if (jl_is_slot(sym) || jl_is_argument(sym)) { ssize_t n = jl_slot_number(sym); if (src == NULL || n > jl_source_nslots(src) || n < 1 || s->locals == NULL) jl_error("access to invalid slot number"); @@ -407,6 +407,18 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, if (jl_is_gotonode(stmt)) { next_ip = jl_gotonode_label(stmt) - 1; } + else if (jl_is_gotoifnot(stmt)) { + jl_value_t *cond = eval_value(jl_gotoifnot_cond(stmt), s); + if (cond == jl_false) { + next_ip = jl_gotoifnot_label(stmt) - 1; + } + else if (cond != jl_true) { + jl_type_error("if", (jl_value_t*)jl_bool_type, cond); + } + } + else if (jl_is_returnnode(stmt)) { + return eval_value(jl_returnnode_value(stmt), s); + } else if (jl_is_upsilonnode(stmt)) { jl_value_t *val = jl_fieldref_noalloc(stmt, 0); if (val) @@ -419,11 +431,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, else if (jl_is_expr(stmt)) { // Most exprs are allowed to end a BB by fall through jl_sym_t *head = ((jl_expr_t*)stmt)->head; - assert(head != unreachable_sym); - if (head == return_sym) { - return eval_value(jl_exprarg(stmt, 0), s); - } - else if (head == assign_sym) { + if (head == assign_sym) { jl_value_t *lhs = jl_exprarg(stmt, 0); jl_value_t *rhs = eval_value(jl_exprarg(stmt, 1), s); if (jl_is_slot(lhs)) { @@ -449,15 +457,6 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, JL_GC_POP(); } } - else if (head == goto_ifnot_sym) { - jl_value_t *cond = eval_value(jl_exprarg(stmt, 0), s); - if (cond == jl_false) { - next_ip = jl_unbox_long(jl_exprarg(stmt, 1)) - 1; - } - else if (cond != jl_true) { - jl_type_error("if", (jl_value_t*)jl_bool_type, cond); - } - } else if (head == enter_sym) { jl_enter_handler(&__eh); // This is a bit tricky, but supports the implementation of PhiC nodes. diff --git a/src/ircode.c b/src/ircode.c index 4f6f8f8bd50dd..a3f602dd2f2e1 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -207,8 +207,17 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) write_uint8(s->s, TAG_GOTONODE); jl_encode_value(s, jl_get_nth_field(v, 0)); } - else if (jl_is_quotenode(v)) { - write_uint8(s->s, TAG_QUOTENODE); + else if (jl_is_gotoifnot(v)) { + write_uint8(s->s, TAG_GOTOIFNOT); + jl_encode_value(s, jl_get_nth_field(v, 0)); + jl_encode_value(s, jl_get_nth_field(v, 1)); + } + else if (jl_is_argument(v)) { + write_uint8(s->s, TAG_ARGUMENT); + jl_encode_value(s, jl_get_nth_field(v, 0)); + } + else if (jl_is_returnnode(v)) { + write_uint8(s->s, TAG_RETURNNODE); jl_encode_value(s, jl_get_nth_field(v, 0)); } else if (jl_typeis(v, jl_int64_type)) { @@ -592,6 +601,19 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED v = jl_new_struct_uninit(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type); set_nth_field(tag == TAG_GOTONODE ? jl_gotonode_type : jl_quotenode_type, (void*)v, 0, jl_decode_value(s)); return v; + case TAG_GOTOIFNOT: + v = jl_new_struct_uninit(jl_gotoifnot_type); + set_nth_field(jl_gotoifnot_type, (void*)v, 0, jl_decode_value(s)); + set_nth_field(jl_gotoifnot_type, (void*)v, 1, jl_decode_value(s)); + return v; + case TAG_ARGUMENT: + v = jl_new_struct_uninit(jl_argument_type); + set_nth_field(jl_argument_type, (void*)v, 0, jl_decode_value(s)); + return v; + case TAG_RETURNNODE: + v = jl_new_struct_uninit(jl_returnnode_type); + set_nth_field(jl_returnnode_type, (void*)v, 0, jl_decode_value(s)); + return v; case TAG_SHORTER_INT64: v = jl_box_int64((int16_t)read_uint16(s->s)); return v; diff --git a/src/jltypes.c b/src/jltypes.c index 0e9b911f7d22d..23cff09e68bb6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -34,6 +34,7 @@ jl_datatype_t *jl_ssavalue_type; jl_datatype_t *jl_abstractslot_type; jl_datatype_t *jl_slotnumber_type; jl_datatype_t *jl_typedslot_type; +jl_datatype_t *jl_argument_type; jl_datatype_t *jl_simplevector_type; jl_typename_t *jl_tuple_typename; jl_datatype_t *jl_anytuple_type; @@ -93,6 +94,8 @@ jl_datatype_t *jl_expr_type; jl_datatype_t *jl_globalref_type; jl_datatype_t *jl_linenumbernode_type; jl_datatype_t *jl_gotonode_type; +jl_datatype_t *jl_gotoifnot_type; +jl_datatype_t *jl_returnnode_type; jl_datatype_t *jl_pinode_type; jl_datatype_t *jl_phinode_type; jl_datatype_t *jl_phicnode_type; @@ -2059,6 +2062,10 @@ void jl_init_types(void) JL_GC_DISABLED jl_perm_symsvec(2, "id", "typ"), jl_svec(2, jl_long_type, jl_any_type), 0, 0, 2); + jl_argument_type = jl_new_datatype(jl_symbol("Argument"), core, jl_any_type, jl_emptysvec, + jl_perm_symsvec(1, "n"), + jl_svec1(jl_long_type), 0, 0, 1); + jl_init_int32_int64_cache(); jl_bool_type = NULL; @@ -2177,6 +2184,16 @@ void jl_init_types(void) JL_GC_DISABLED jl_perm_symsvec(1, "label"), jl_svec(1, jl_long_type), 0, 0, 1); + jl_gotoifnot_type = + jl_new_datatype(jl_symbol("GotoIfNot"), core, jl_any_type, jl_emptysvec, + jl_perm_symsvec(2, "cond", "dest"), + jl_svec(2, jl_any_type, jl_long_type), 0, 0, 2); + + jl_returnnode_type = + jl_new_datatype(jl_symbol("ReturnNode"), core, jl_any_type, jl_emptysvec, + jl_perm_symsvec(1, "val"), + jl_svec(1, jl_any_type), 0, 0, 0); + jl_pinode_type = jl_new_datatype(jl_symbol("PiNode"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(2, "val", "typ"), @@ -2484,13 +2501,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_compute_field_offsets(jl_uniontype_type); jl_compute_field_offsets(jl_tvar_type); jl_compute_field_offsets(jl_methtable_type); - jl_compute_field_offsets(jl_expr_type); - jl_compute_field_offsets(jl_linenumbernode_type); - jl_compute_field_offsets(jl_lineinfonode_type); - jl_compute_field_offsets(jl_gotonode_type); - jl_compute_field_offsets(jl_quotenode_type); - jl_compute_field_offsets(jl_pinode_type); - jl_compute_field_offsets(jl_phinode_type); jl_compute_field_offsets(jl_module_type); jl_compute_field_offsets(jl_method_instance_type); jl_compute_field_offsets(jl_code_instance_type); diff --git a/src/julia.h b/src/julia.h index 692f9aeb87652..1536ed465f4d5 100644 --- a/src/julia.h +++ b/src/julia.h @@ -573,6 +573,7 @@ extern JL_DLLEXPORT jl_datatype_t *jl_ssavalue_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_abstractslot_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_slotnumber_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_typedslot_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_datatype_t *jl_argument_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_simplevector_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_typename_t *jl_tuple_typename JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_typename_t *jl_vecelement_typename JL_GLOBALLY_ROOTED; @@ -653,6 +654,8 @@ extern JL_DLLEXPORT jl_datatype_t *jl_expr_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_globalref_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_linenumbernode_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_gotonode_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_datatype_t *jl_gotoifnot_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_datatype_t *jl_returnnode_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_phinode_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_pinode_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_phicnode_type JL_GLOBALLY_ROOTED; @@ -918,9 +921,12 @@ STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) JL_NOTSAFEPO #define jl_slot_number(x) (((intptr_t*)(x))[0]) #define jl_typedslot_get_type(x) (((jl_value_t**)(x))[1]) #define jl_gotonode_label(x) (((intptr_t*)(x))[0]) +#define jl_gotoifnot_cond(x) (((jl_value_t**)(x))[0]) +#define jl_gotoifnot_label(x) (((intptr_t*)(x))[1]) #define jl_globalref_mod(s) (*(jl_module_t**)(s)) #define jl_globalref_name(s) (((jl_sym_t**)(s))[1]) #define jl_quotenode_value(x) (((jl_value_t**)x)[0]) +#define jl_returnnode_value(x) (((jl_value_t**)x)[0]) #define jl_nparams(t) jl_svec_len(((jl_datatype_t*)(t))->parameters) #define jl_tparam0(t) jl_svecref(((jl_datatype_t*)(t))->parameters, 0) @@ -1073,6 +1079,9 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #define jl_is_expr(v) jl_typeis(v,jl_expr_type) #define jl_is_globalref(v) jl_typeis(v,jl_globalref_type) #define jl_is_gotonode(v) jl_typeis(v,jl_gotonode_type) +#define jl_is_gotoifnot(v) jl_typeis(v,jl_gotoifnot_type) +#define jl_is_returnnode(v) jl_typeis(v,jl_returnnode_type) +#define jl_is_argument(v) jl_typeis(v,jl_argument_type) #define jl_is_pinode(v) jl_typeis(v,jl_pinode_type) #define jl_is_phinode(v) jl_typeis(v,jl_phinode_type) #define jl_is_phicnode(v) jl_typeis(v,jl_phicnode_type) diff --git a/src/julia_internal.h b/src/julia_internal.h index 0d027f530b013..9b697f6f1a61c 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1142,7 +1142,7 @@ extern jl_sym_t *export_sym; extern jl_sym_t *import_sym; extern jl_sym_t *toplevel_sym; extern jl_sym_t *quote_sym; extern jl_sym_t *line_sym; extern jl_sym_t *jl_incomplete_sym; extern jl_sym_t *goto_sym; extern jl_sym_t *goto_ifnot_sym; -extern jl_sym_t *return_sym; extern jl_sym_t *unreachable_sym; +extern jl_sym_t *return_sym; extern jl_sym_t *lambda_sym; extern jl_sym_t *assign_sym; extern jl_sym_t *globalref_sym; extern jl_sym_t *do_sym; extern jl_sym_t *method_sym; extern jl_sym_t *core_sym; diff --git a/src/method.c b/src/method.c index 303fe34269a54..4d9ab188c960d 100644 --- a/src/method.c +++ b/src/method.c @@ -28,6 +28,23 @@ static jl_value_t *resolve_globals(jl_value_t *expr, jl_module_t *module, jl_sve return expr; return jl_module_globalref(module, (jl_sym_t*)expr); } + else if (jl_is_returnnode(expr)) { + jl_value_t *val = resolve_globals(jl_returnnode_value(expr), module, sparam_vals, binding_effects, eager_resolve); + JL_GC_PUSH1(&val); + expr = jl_new_struct(jl_returnnode_type, val); + JL_GC_POP(); + return expr; + } + else if (jl_is_gotoifnot(expr)) { + jl_value_t *cond = resolve_globals(jl_gotoifnot_cond(expr), module, sparam_vals, binding_effects, eager_resolve); + intptr_t label = jl_gotoifnot_label(expr); + JL_GC_PUSH1(&cond); + expr = jl_new_struct_uninit(jl_gotoifnot_type); + set_nth_field(jl_gotoifnot_type, expr, 0, cond); + jl_gotoifnot_label(expr) = label; + JL_GC_POP(); + return expr; + } else if (jl_is_expr(expr)) { jl_expr_t *e = (jl_expr_t*)expr; if (e->head == global_sym && binding_effects) { @@ -245,6 +262,9 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) else jl_array_del_end(meta, na - ins); } + else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == return_sym) { + jl_array_ptr_set(body, j, jl_new_struct(jl_returnnode_type, jl_exprarg(st, 0))); + } } jl_array_t *vinfo = (jl_array_t*)jl_exprarg(ir, 1); jl_array_t *vis = (jl_array_t*)jl_array_ptr_ref(vinfo, 0); @@ -513,7 +533,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) size_t j; for (j = 1; j < nargs; j++) { jl_value_t *aj = jl_exprarg(st, j); - if (!jl_is_slot(aj)) + if (!jl_is_slot(aj) && !jl_is_argument(aj)) continue; int sn = (int)jl_slot_number(aj) - 2; if (sn < 0) // @nospecialize on self is valid but currently ignored diff --git a/src/serialize.h b/src/serialize.h index d7e6216a55ad3..07ff46fdf96db 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -59,8 +59,11 @@ extern "C" { #define TAG_GOTONODE 51 #define TAG_QUOTENODE 52 #define TAG_GENERAL 53 +#define TAG_GOTOIFNOT 54 +#define TAG_RETURNNODE 55 +#define TAG_ARGUMENT 56 -#define LAST_TAG 53 +#define LAST_TAG 56 #define write_uint8(s, n) ios_putc((n), (s)) #define read_uint8(s) ((uint8_t)ios_getc(s)) diff --git a/src/staticdata.c b/src/staticdata.c index 4211df93d4f7f..d2e66d8cbd648 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -39,7 +39,7 @@ static void *const _tags[] = { &jl_expr_type, &jl_globalref_type, &jl_string_type, &jl_module_type, &jl_tvar_type, &jl_method_instance_type, &jl_method_type, &jl_code_instance_type, &jl_linenumbernode_type, &jl_lineinfonode_type, - &jl_gotonode_type, &jl_quotenode_type, + &jl_gotonode_type, &jl_quotenode_type, &jl_gotoifnot_type, &jl_argument_type, &jl_returnnode_type, &jl_pinode_type, &jl_phinode_type, &jl_phicnode_type, &jl_upsilonnode_type, &jl_type_type, &jl_bottom_type, &jl_ref_type, &jl_pointer_type, &jl_llvmpointer_type, &jl_vararg_type, &jl_abstractarray_type, diff --git a/src/toplevel.c b/src/toplevel.c index 19924f20ee2b1..d8d2a930f5792 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -395,11 +395,9 @@ static void body_attributes(jl_array_t *body, int *has_intrinsics, int *has_defs if (jl_gotonode_label(stmt) <= i) *has_loops = 1; } - else if (jl_is_expr(stmt)) { - if (((jl_expr_t*)stmt)->head == goto_ifnot_sym) { - if (jl_unbox_long(jl_exprarg(stmt,1)) <= i) - *has_loops = 1; - } + else if (jl_is_gotoifnot(stmt)) { + if (jl_gotoifnot_label(stmt) <= i) + *has_loops = 1; } } expr_attributes(stmt, has_intrinsics, has_defs); diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index be4c1ea42d538..4f3ef7e1bd201 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -276,7 +276,7 @@ let m = which(f_broken_code, ()) let src = Base.uncompressed_ast(m) src.code = Any[ Expr(:meta, :noinline) - Expr(:return, Expr(:invalid)) + Core.ReturnNode(Expr(:invalid)) ] m.source = src end diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index d1665a73b2fa8..53d5458d6a7ac 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -31,7 +31,7 @@ Serializer(io::IO) = Serializer{typeof(io)}(io) const n_int_literals = 33 const n_reserved_slots = 24 -const n_reserved_tags = 10 +const n_reserved_tags = 8 const TAGS = Any[ Symbol, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, @@ -58,6 +58,7 @@ const TAGS = Any[ Symbol, # HEADER_TAG Symbol, # IDDICT_TAG Symbol, # SHARED_REF_TAG + ReturnNode, GotoIfNot, fill(Symbol, n_reserved_tags)..., (), Bool, Any, Bottom, Core.TypeofBottom, Type, svec(), Tuple{}, false, true, nothing, @@ -77,7 +78,7 @@ const TAGS = Any[ @assert length(TAGS) == 255 -const ser_version = 10 # do not make changes without bumping the version #! +const ser_version = 11 # do not make changes without bumping the version #! const NTAGS = length(TAGS) @@ -1048,7 +1049,20 @@ end function deserialize(s::AbstractSerializer, ::Type{CodeInfo}) ci = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) deserialize_cycle(s, ci) - ci.code = deserialize(s)::Vector{Any} + code = deserialize(s)::Vector{Any} + ci.code = code + # allow older-style IR with return and gotoifnot Exprs + for i in 1:length(code) + stmt = code[i] + if isa(stmt, Expr) + ex = stmt::Expr + if ex.head === :return + code[i] = ReturnNode(isempty(ex.args) ? nothing : ex.args[1]) + elseif ex.head === :gotoifnot + code[i] = GotoIfNot(ex.args[1], ex.args[2]) + end + end + end ci.codelocs = deserialize(s)::Vector{Int32} _x = deserialize(s) if _x isa Array || _x isa Int diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 302dcaa8d0223..034d107fd0b6d 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -5,7 +5,7 @@ module MiniCassette # fancy features, but sufficient to exercise this code path in the compiler. using Core.Compiler: method_instances, retrieve_code_info, CodeInfo, - MethodInstance, SSAValue, GotoNode, Slot, SlotNumber, quoted, + MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, Slot, SlotNumber, quoted, signature_type using Base: _methods_by_ftype using Base.Meta: isexpr @@ -20,10 +20,12 @@ module MiniCassette transform(expr) = transform_expr(expr, map_slot_number, map_ssa_value, sparams) if isexpr(expr, :call) return Expr(:call, overdub, SlotNumber(2), map(transform, expr.args)...) - elseif isexpr(expr, :gotoifnot) - return Expr(:gotoifnot, transform(expr.args[1]), map_ssa_value(SSAValue(expr.args[2])).id) + elseif isa(expr, GotoIfNot) + return GotoIfNot(transform(expr.cond), map_ssa_value(SSAValue(expr.dest)).id) elseif isexpr(expr, :static_parameter) return quoted(sparams[expr.args[1]]) + elseif isa(expr, ReturnNode) + return ReturnNode(transform(expr.val)) elseif isa(expr, Expr) return Expr(expr.head, map(transform, expr.args)...) elseif isa(expr, GotoNode) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 5eef0d1a125c2..4e0287818a72d 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # tests for Core.Compiler correctness and precision -import Core.Compiler: Const, Conditional, ⊑ +import Core.Compiler: Const, Conditional, ⊑, ReturnNode, GotoIfNot isdispatchelem(@nospecialize x) = !isa(x, Type) || Core.Compiler.isdispatchelem(x) using Random, Core.IR @@ -886,9 +886,8 @@ end f21175() = 902221 @test code_typed(f21175, ())[1].second === Int # call again, so that the AST is built on-demand -let e = code_typed(f21175, ())[1].first.code[1]::Expr - @test e.head === :return - @test e.args[1] ∈ (902221, Core.QuoteNode(902221)) +let e = code_typed(f21175, ())[1].first.code[1]::ReturnNode + @test e.val ∈ (902221, Core.QuoteNode(902221)) end # issue #10207 @@ -1165,17 +1164,17 @@ function test_const_return(@nospecialize(f), @nospecialize(t), @nospecialize(val for ex in ast.code::Vector{Any} if isa(ex, LineNumberNode) continue + elseif isa(ex, ReturnNode) + # multiple returns + @test !ret_found + ret_found = true + ret = ex.val + # return value mismatch + @test ret === val || (isa(ret, QuoteNode) && (ret::QuoteNode).value === val) + continue elseif isa(ex, Expr) if Core.Compiler.is_meta_expr_head(ex.head) continue - elseif ex.head === :return - # multiple returns - @test !ret_found - ret_found = true - ret = ex.args[1] - # return value mismatch - @test ret === val || (isa(ret, QuoteNode) && (ret::QuoteNode).value === val) - continue end end @test false || "Side effect expressions found $ex" @@ -1660,7 +1659,7 @@ end opt25261 = code_typed(foo25261, Tuple{}, optimize=false)[1].first.code i = 1 # Skip to after the branch -while !Meta.isexpr(opt25261[i], :gotoifnot); global i += 1; end +while !isa(opt25261[i], GotoIfNot); global i += 1; end foundslot = false for expr25261 in opt25261[i:end] if expr25261 isa TypedSlot && expr25261.typ === Tuple{Int, Int} @@ -2033,7 +2032,7 @@ worklist = Int[] let i for i in 1:length(code28279) stmt = code28279[i] - if Meta.isexpr(stmt, :gotoifnot) + if isa(stmt, GotoIfNot) push!(worklist, i) ssachangemap[i] = 1 if i < length(code28279) @@ -2048,13 +2047,13 @@ offset = 1 let i for i in 1:length(code28279) if i == length(code28279) - @test Meta.isexpr(code28279[i], :return) - @test Meta.isexpr(oldcode28279[i], :return) - @test code28279[i].args[1].id == (oldcode28279[i].args[1].id + offset - 1) - elseif Meta.isexpr(code28279[i], :gotoifnot) - @test Meta.isexpr(oldcode28279[i], :gotoifnot) - @test code28279[i].args[1] == oldcode28279[i].args[1] - @test code28279[i].args[2] == (oldcode28279[i].args[2] + offset) + @test isa(code28279[i], ReturnNode) + @test isa(oldcode28279[i], ReturnNode) + @test code28279[i].val.id == (oldcode28279[i].val.id + offset - 1) + elseif isa(code28279[i], GotoIfNot) + @test isa(oldcode28279[i], GotoIfNot) + @test code28279[i].cond == oldcode28279[i].cond + @test code28279[i].dest == (oldcode28279[i].dest + offset) global offset += 1 else @test code28279[i] == oldcode28279[i] diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 0fa46b4554a61..4c3812ddc04e5 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -2,6 +2,7 @@ using Test using Base.Meta +using Core: ReturnNode """ Helper to walk the AST and call a function on every node. @@ -87,11 +88,11 @@ end @inline Base.getindex(v::s21074, i::Integer) = v.x[i] @eval f21074() = $(s21074((1,2))).x[1] let (src, _) = code_typed(f21074, ())[1] - @test src.code[end] == Expr(:return, 1) + @test src.code[end] == ReturnNode(1) end @eval g21074() = $(s21074((1,2)))[1] let (src, _) = code_typed(g21074, ())[1] - @test src.code[end] == Expr(:return, 1) + @test src.code[end] == ReturnNode(1) end # issue #21311 @@ -235,7 +236,7 @@ function f_subtype() end let code = code_typed(f_subtype, Tuple{})[1][1].code @test length(code) == 1 - @test code[1] == Expr(:return, false) + @test code[1] == ReturnNode(false) end # check that pointerref gets deleted if unused @@ -261,7 +262,7 @@ function foo_apply_apply_type_svec() Core.apply_type(A..., B.types...) end let ci = code_typed(foo_apply_apply_type_svec, Tuple{})[1].first - @test length(ci.code) == 1 && ci.code[1] == Expr(:return, NTuple{3, Float32}) + @test length(ci.code) == 1 && ci.code[1] == ReturnNode(NTuple{3, Float32}) end # The that inlining doesn't drop ambiguity errors (#30118) @@ -277,8 +278,8 @@ f34900(x::Int, y) = x f34900(x, y::Int) = y f34900(x::Int, y::Int) = invoke(f34900, Tuple{Int, Any}, x, y) let ci = code_typed(f34900, Tuple{Int, Int})[1].first - @test length(ci.code) == 1 && isexpr(ci.code[1], :return) && - ci.code[1].args[1].id == 2 + @test length(ci.code) == 1 && isa(ci.code[1], ReturnNode) && + ci.code[1].val.n == 2 end @testset "check jl_ir_flag_inlineable for inline macro" begin diff --git a/test/compiler/interpreter_exec.jl b/test/compiler/interpreter_exec.jl index f4bb47261bbbf..6e7fcd6fce465 100644 --- a/test/compiler/interpreter_exec.jl +++ b/test/compiler/interpreter_exec.jl @@ -2,6 +2,7 @@ # tests that interpreter matches codegen using Test +using Core: GotoIfNot, ReturnNode # test that interpreter correctly handles PhiNodes (#29262) let m = Meta.@lower 1 + 1 @@ -12,11 +13,11 @@ let m = Meta.@lower 1 + 1 QuoteNode(:a), QuoteNode(:b), GlobalRef(@__MODULE__, :test29262), - Expr(:gotoifnot, Core.SSAValue(3), 6), + GotoIfNot(Core.SSAValue(3), 6), # block 2 Core.PhiNode(Any[4], Any[Core.SSAValue(1)]), Core.PhiNode(Any[4, 5], Any[Core.SSAValue(2), Core.SSAValue(5)]), - Expr(:return, Core.SSAValue(6)), + ReturnNode(Core.SSAValue(6)), ] nstmts = length(src.code) src.ssavaluetypes = Any[ Any for _ = 1:nstmts ] @@ -51,12 +52,12 @@ let m = Meta.@lower 1 + 1 Core.PhiNode(Any[], Any[]), # NULL, NULL Core.PhiNode(Any[17, 8], Any[Core.SSAValue(2), Core.SSAValue(8)]), # NULL, :c, [:b] Core.PhiNode(Any[], Any[]), # NULL, NULL - Expr(:gotoifnot, Core.SSAValue(5), 5), + GotoIfNot(Core.SSAValue(5), 5), # block 4 - Expr(:gotoifnot, Core.SSAValue(10), 9), + GotoIfNot(Core.SSAValue(10), 9), # block 5 Expr(:call, GlobalRef(Core, :tuple), Core.SSAValue(6), Core.SSAValue(7), Core.SSAValue(8), Core.SSAValue(14)), - Expr(:return, Core.SSAValue(18)), + ReturnNode(Core.SSAValue(18)), ] nstmts = length(src.code) src.ssavaluetypes = Any[ Any for _ = 1:nstmts ] @@ -83,7 +84,7 @@ let m = Meta.@lower 1 + 1 Core.UpsilonNode(), Core.UpsilonNode(), Core.UpsilonNode(Core.SSAValue(2)), - Expr(:gotoifnot, Core.SSAValue(3), 10), + GotoIfNot(Core.SSAValue(3), 10), # block 4 Core.UpsilonNode(Core.SSAValue(1)), # block 5 @@ -93,7 +94,7 @@ let m = Meta.@lower 1 + 1 Core.PhiCNode(Any[Core.SSAValue(6)]), # NULL Expr(:leave, 1), # block 7 - Expr(:return, Core.SSAValue(11)), + ReturnNode(Core.SSAValue(11)), ] nstmts = length(src.code) src.ssavaluetypes = Any[ Any for _ = 1:nstmts ] diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 9e252ad0230c8..be7539cac4d9f 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -2,7 +2,7 @@ using Test using Base.Meta -using Core: PhiNode, SSAValue, GotoNode, PiNode, QuoteNode +using Core: PhiNode, SSAValue, GotoNode, PiNode, QuoteNode, ReturnNode, GotoIfNot # Tests for domsort @@ -13,20 +13,20 @@ let m = Meta.@lower 1 + 1 src.code = Any[ # block 1 Expr(:call, :opaque), - Expr(:gotoifnot, Core.SSAValue(1), 10), + GotoIfNot(Core.SSAValue(1), 10), # block 2 Core.PhiNode(Any[8], Any[Core.SSAValue(7)]), # <- This phi must not get replaced by %7 Core.PhiNode(Any[2, 8], Any[true, false]), - Expr(:gotoifnot, Core.SSAValue(1), 7), + GotoIfNot(Core.SSAValue(1), 7), # block 3 Expr(:call, :+, Core.SSAValue(3), 1), # block 4 Core.PhiNode(Any[5, 6], Any[0, Core.SSAValue(6)]), Expr(:call, >, Core.SSAValue(7), 10), - Expr(:gotoifnot, Core.SSAValue(8), 3), + GotoIfNot(Core.SSAValue(8), 3), # block 5 Core.PhiNode(Any[2, 8], Any[0, Core.SSAValue(7)]), - Expr(:return, Core.SSAValue(10)), + ReturnNode(Core.SSAValue(10)), ] nstmts = length(src.code) src.ssavaluetypes = nstmts @@ -49,11 +49,11 @@ let m = Meta.@lower 1 + 1 N = 2^15 for i in 1:2:N push!(code, Expr(:call, :opaque)) - push!(code, Expr(:gotoifnot, Core.SSAValue(i), N+2)) # skip one block + push!(code, GotoIfNot(Core.SSAValue(i), N+2)) # skip one block end # all goto here push!(code, Expr(:call, :opaque)) - push!(code, Expr(:return)) + push!(code, ReturnNode(nothing)) src.code = code nstmts = length(src.code) @@ -136,7 +136,7 @@ struct FooPartial f_partial(x) = new(x, 2).x end let ci = code_typed(f_partial, Tuple{Float64})[1].first - @test length(ci.code) == 1 && isexpr(ci.code[1], :return) + @test length(ci.code) == 1 && isa(ci.code[1], ReturnNode) end # A SSAValue after the compaction line @@ -147,9 +147,9 @@ let m = Meta.@lower 1 + 1 # block 1 nothing, # block 2 - PhiNode(Any[1, 7], Any[Core.SlotNumber(2), SSAValue(9)]), + PhiNode(Any[1, 7], Any[Core.Argument(2), SSAValue(9)]), Expr(:call, isa, SSAValue(2), UnionAll), - Expr(:gotoifnot, Core.SSAValue(3), 11), + GotoIfNot(Core.SSAValue(3), 11), # block 3 nothing, nothing, @@ -160,7 +160,7 @@ let m = Meta.@lower 1 + 1 # the phinode here GotoNode(2), # block 5 - Expr(:return, Core.SSAValue(2)), + ReturnNode(Core.SSAValue(2)), ] src.ssavaluetypes = Any[ Nothing, @@ -214,7 +214,7 @@ let m = Meta.@lower 1 + 1 Core.Compiler.GotoNode(5), Core.Compiler.GotoNode(6), Core.Compiler.GotoNode(7), - Expr(:return, 2) + ReturnNode(2) ] nstmts = length(src.code) src.ssavaluetypes = nstmts @@ -235,14 +235,14 @@ let m = Meta.@lower 1 + 1 src.code = Any[ Core.Compiler.GotoIfNot(Core.Compiler.Argument(2), 3), Core.Compiler.GotoNode(4), - Expr(:return, 1), + ReturnNode(1), Core.Compiler.GotoNode(5), Core.Compiler.GotoIfNot(Core.Compiler.Argument(2), 7), # This fall through block of the previous GotoIfNot # must be moved up along with it, when we merge it # into the goto 4 block. - Expr(:return, 2), - Expr(:return, 3) + ReturnNode(2), + ReturnNode(3) ] nstmts = length(src.code) src.ssavaluetypes = nstmts diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index ca104f1d81f0a..97a6d3f95e060 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -5,11 +5,11 @@ const Compiler = Core.Compiler # TODO: this test is broken #let code = Any[ -# Expr(:gotoifnot, SlotNumber(2), 4), +# GotoIfNot(SlotNumber(2), 4), # Expr(:(=), SlotNumber(3), 2), # # Test a SlotNumber as a value of a PhiNode # PhiNode(Any[2,3], Any[1, SlotNumber(3)]), -# Expr(:return, SSAValue(3)) +# ReturnNode(SSAValue(3)) # ] # # ci = eval(Expr(:new, CodeInfo, diff --git a/test/compiler/validation.jl b/test/compiler/validation.jl index a63f5ddc57d71..4055acd0f3d2e 100644 --- a/test/compiler/validation.jl +++ b/test/compiler/validation.jl @@ -50,12 +50,12 @@ end c.code[1] = Expr(:(=), SlotNumber(2), GotoNode(1)) c.code[2] = Expr(:(=), SlotNumber(2), LineNumberNode(2)) i = 2 - for h in (:gotoifnot, :line, :const, :meta) + for h in (:line, :const, :meta) c.code[i+=1] = Expr(:(=), SlotNumber(2), Expr(h)) end errors = Core.Compiler.validate_code(c) - @test length(errors) == 6 - @test count(e.kind === Core.Compiler.INVALID_RVALUE for e in errors) == 6 + @test length(errors) == 5 + @test count(e.kind === Core.Compiler.INVALID_RVALUE for e in errors) == 5 end @testset "INVALID_CALL_ARG" begin @@ -64,12 +64,12 @@ end c.code[2] = Expr(:call, GlobalRef(Base,:-), Expr(:call, GlobalRef(Base,:sin), GotoNode(2)), 3) c.code[3] = Expr(:call, LineNumberNode(2)) i = 3 - for h in (:gotoifnot, :line, :const, :meta) + for h in (:line, :const, :meta) c.code[i+=1] = Expr(:call, GlobalRef(@__MODULE__,:f), Expr(h)) end errors = Core.Compiler.validate_code(c) - @test length(errors) == 7 - @test count(e.kind === Core.Compiler.INVALID_CALL_ARG for e in errors) == 7 + @test length(errors) == 6 + @test count(e.kind === Core.Compiler.INVALID_CALL_ARG for e in errors) == 6 end @testset "EMPTY_SLOTNAMES" begin diff --git a/test/core.jl b/test/core.jl index 790ea3cd08165..82ebb5481694d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -7138,7 +7138,7 @@ let code = code_lowered(FieldConvert)[1].code @test code[8] == Expr(:call, GlobalRef(Core, :fieldtype), Core.SSAValue(1), 5) @test code[9] == Expr(:call, GlobalRef(Base, :convert), Core.SSAValue(8), Core.SlotNumber(6)) @test code[10] == Expr(:new, Core.SSAValue(1), Core.SSAValue(3), Core.SSAValue(5), Core.SlotNumber(4), Core.SSAValue(7), Core.SSAValue(9)) - @test code[11] == Expr(:return, Core.SSAValue(10)) + @test code[11] == Core.ReturnNode(Core.SSAValue(10)) end # Issue #32820 diff --git a/test/syntax.jl b/test/syntax.jl index 35178057b1614..2a333de82d079 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -678,8 +678,8 @@ function count_meta_loc(exprs) return push_count end -function is_return_ssavalue(ex::Expr) - ex.head === :return && isa(ex.args[1], Core.SSAValue) +function is_return_ssavalue(ex) + ex isa Core.ReturnNode && ex.val isa Core.SSAValue end function is_pop_loc(ex::Expr) From ca4a895cb04ddf51150bffac4c6e2490aceb1d5b Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 19 Jun 2020 13:05:40 +0200 Subject: [PATCH 204/232] deleteat!(::BitVector, inds) : check bounds for the first passed index --- base/bitarray.jl | 2 +- test/bitarray.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/base/bitarray.jl b/base/bitarray.jl index 42b299f3b65ec..4cbb13a1c54bf 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -967,11 +967,11 @@ function deleteat!(B::BitVector, inds) n = new_l = length(B) y = iterate(inds) y === nothing && return B - n == 0 && throw(BoundsError(B, inds)) Bc = B.chunks (p, s) = y + checkbounds(B, p) q = p+1 new_l -= 1 y = iterate(inds, s) diff --git a/test/bitarray.jl b/test/bitarray.jl index a12d93cbef5b0..16a7700f91f28 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -666,6 +666,8 @@ timesofar("indexing") b1 = bitrand(v1) @test_throws ArgumentError deleteat!(b1, [1, 1, 2]) @test_throws BoundsError deleteat!(b1, [1, length(b1)+1]) + @test_throws BoundsError deleteat!(b1, [length(b1)+rand(1:100)]) + @test_throws BoundsError deleteat!(bitrand(1), [-1, 0, 1]) @test_throws BoundsError deleteat!(BitVector(), 1) @test_throws BoundsError deleteat!(BitVector(), [1]) From 73df43ab77cdadbe5080d90ab852c42d16678089 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 19 Jun 2020 10:59:13 -0400 Subject: [PATCH 205/232] add some helpful type assertions to `size` and `rand` --- base/array.jl | 2 +- stdlib/Random/src/Random.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/array.jl b/base/array.jl index bd6807853c93e..c401e5e1a27eb 100644 --- a/base/array.jl +++ b/base/array.jl @@ -154,7 +154,7 @@ end size(a::Array, d::Integer) = arraysize(a, convert(Int, d)) size(a::Vector) = (arraysize(a,1),) size(a::Matrix) = (arraysize(a,1), arraysize(a,2)) -size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(N))) +size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(N))::Dims) asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index 707e90d67c08a..ef71cb32caea8 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -253,7 +253,7 @@ rand(rng::AbstractRNG, ::UniformT{T}) where {T} = rand(rng, T) rand(rng::AbstractRNG, X) = rand(rng, Sampler(rng, X, Val(1))) # this is needed to disambiguate rand(rng::AbstractRNG, X::Dims) = rand(rng, Sampler(rng, X, Val(1))) -rand(rng::AbstractRNG=default_rng(), ::Type{X}=Float64) where {X} = rand(rng, Sampler(rng, X, Val(1))) +rand(rng::AbstractRNG=default_rng(), ::Type{X}=Float64) where {X} = rand(rng, Sampler(rng, X, Val(1)))::X rand(X) = rand(default_rng(), X) rand(::Type{X}) where {X} = rand(default_rng(), X) From 96ff7f0af72ae192ed0c6e62189f46e553189268 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 19 Jun 2020 11:06:35 -0400 Subject: [PATCH 206/232] make `tmerge` more precise by keeping common parameters --- base/compiler/typelimits.jl | 15 ++++++++++++++- test/compiler/inference.jl | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 88948421f1c01..aea5fb224f0b3 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -391,7 +391,20 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb)) widen = tuplemerge(unwrap_unionall(ti)::DataType, unwrap_unionall(tj)::DataType) widen = rewrap_unionall(rewrap_unionall(widen, ti), tj) else - widen = typenames[i].wrapper + wr = typenames[i].wrapper + uw = unwrap_unionall(wr)::DataType + ui = unwrap_unionall(ti)::DataType + uj = unwrap_unionall(tj)::DataType + merged = wr + for k = 1:length(uw.parameters) + ui_k = ui.parameters[k] + if ui_k === uj.parameters[k] && !has_free_typevars(ui_k) + merged = merged{ui_k} + else + merged = merged{uw.parameters[k]} + end + end + widen = rewrap_unionall(merged, wr) end types[i] = Union{} typenames[i] = Any.name diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 4e0287818a72d..788469aba82b3 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -95,6 +95,7 @@ tmerge_test(Tuple{}, Tuple{Complex, Vararg{Union{ComplexF32, ComplexF64}}}, Union{Nothing, Tuple{Vararg{ComplexF32}}} @test Core.Compiler.tmerge(Union{Nothing, Tuple{ComplexF32}}, Union{Nothing, Tuple{ComplexF32, ComplexF32}}) == Union{Nothing, Tuple{Vararg{ComplexF32}}} +@test Core.Compiler.tmerge(Vector{Int}, Core.Compiler.tmerge(Vector{String}, Vector{Bool})) == Vector # issue 9770 @noinline x9770() = false From baa9b2733c0ee0c16eb8f0c67af9540a64c1eb18 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 19 Jun 2020 11:07:24 -0400 Subject: [PATCH 207/232] set default max_methods to 3 --- base/compiler/types.jl | 4 ++-- test/worlds.jl | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 068574b80d0c9..d371b6a1c8786 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -58,7 +58,7 @@ struct OptimizationParams inline_cost_threshold::Int = 100, inline_nonleaf_penalty::Int = 1000, inline_tupleret_bonus::Int = 400, - max_methods::Int = 4, + max_methods::Int = 3, tuple_splat::Int = 32, union_splitting::Int = 4, ) @@ -107,7 +107,7 @@ struct InferenceParams function InferenceParams(; ipo_constant_propagation::Bool = true, aggressive_constant_propagation::Bool = false, - max_methods::Int = 4, + max_methods::Int = 3, union_splitting::Int = 4, apply_union_enum::Int = 8, tupletype_depth::Int = 3, diff --git a/test/worlds.jl b/test/worlds.jl index 92ee5866e1517..878dd24adadb4 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -275,11 +275,12 @@ applyf35855(Any[1]) wany3 = worlds(instance(applyf35855, (Vector{Any},))) src3 = code_typed(applyf35855, (Vector{Any},))[1] @test (wany3 == wany2) == equal(src3, src2) # don't invalidate unless you also change the code -f35855(::AbstractVector) = 4 # next test would pass if this were ::Vector{Int} +f35855(::AbstractVector) = 4 applyf35855(Any[1]) wany4 = worlds(instance(applyf35855, (Vector{Any},))) src4 = code_typed(applyf35855, (Vector{Any},))[1] -@test_broken (wany4 == wany3) == equal(src4, src3) +# this passes when max_methods == 3, fails when set to 4 +@test (wany4 == wany3) == equal(src4, src3) f35855(::Dict) = 5 applyf35855(Any[1]) wany5 = worlds(instance(applyf35855, (Vector{Any},))) @@ -291,6 +292,18 @@ wany6 = worlds(instance(applyf35855, (Vector{Any},))) src6 = code_typed(applyf35855, (Vector{Any},))[1] @test (wany6 == wany5) == equal(src6, src5) +applyf35855_2(c) = f35855_2(c[1]) +f35855_2(::Int) = 1 +f35855_2(::Float64) = 2 +applyf35855_2(Any[1]) +wany3 = worlds(instance(applyf35855_2, (Vector{Any},))) +src3 = code_typed(applyf35855_2, (Vector{Any},))[1] +f35855_2(::AbstractVector) = 4 # next test would pass if this were ::Vector{Int} +applyf35855_2(Any[1]) +wany4 = worlds(instance(applyf35855_2, (Vector{Any},))) +src4 = code_typed(applyf35855_2, (Vector{Any},))[1] +@test_broken (wany4 == wany3) == equal(src4, src3) + ## ambiguities do not trigger invalidation using Printf Printf.gen("%f") From 3e2effd02b0c2238bf850e49381e88bea497d72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Jun 2020 18:00:10 +0200 Subject: [PATCH 208/232] [ci skip] Typo fix: emove space before comma (#36357) --- base/set.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/set.jl b/base/set.jl index b98e65c844a1c..b287daf3a3138 100644 --- a/base/set.jl +++ b/base/set.jl @@ -232,7 +232,7 @@ end unique!(f, A::AbstractVector) Selects one value from `A` for each unique value produced by `f` applied to -elements of `A` , then return the modified A. +elements of `A`, then return the modified A. !!! compat "Julia 1.1" This method is available as of Julia 1.1. From eb2bcb1f5da10013e21a6103516621e9417534af Mon Sep 17 00:00:00 2001 From: Harry Scholes Date: Fri, 19 Jun 2020 21:45:04 +0100 Subject: [PATCH 209/232] Use haslength to check for HasShape and HasLength traits (#36356) --- base/asyncmap.jl | 3 +-- base/dict.jl | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/base/asyncmap.jl b/base/asyncmap.jl index b3e947667d721..8f2b394372763 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -124,8 +124,7 @@ function verify_ntasks(iterable, ntasks) end if ntasks == 0 - chklen = IteratorSize(iterable) - if (chklen isa HasLength) || (chklen isa HasShape) + if haslength(iterable) ntasks = max(1,min(100, length(iterable))) else ntasks = 100 diff --git a/base/dict.jl b/base/dict.jl index 7e785930c6d36..1b8471d5ad5dc 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -99,10 +99,7 @@ mutable struct Dict{K,V} <: AbstractDict{K,V} end function Dict{K,V}(kv) where V where K h = Dict{K,V}() - chklen = IteratorSize(kv) - if (chklen isa HasLength) || (chklen isa HasShape) - sizehint!(h, length(kv)) - end + haslength(kv) && sizehint!(h, length(kv)) for (k,v) in kv h[k] = v end From 00c41ccee7ebf1b4f8884f4c39ec1158b531f441 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sat, 20 Jun 2020 01:14:51 +0200 Subject: [PATCH 210/232] Remove address spaces during IR printing. (#36358) Respect the strip_ir_metadata/raw flags. --- src/aotcompile.cpp | 10 ---------- src/disasm.cpp | 16 +++++++++++++++- src/jitlayers.h | 2 ++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index c2b0d64318b44..cf8fb7bc39db1 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -825,14 +825,6 @@ void *jl_get_llvmf_defn(jl_method_instance_t *mi, size_t world, char getwrapper, PM = new legacy::PassManager(); addTargetPasses(PM, jl_TargetMachine); addOptimizationPasses(PM, jl_options.opt_level); - PM->add(createRemoveJuliaAddrspacesPass()); - } - - static legacy::PassManager *PM_minimal; - if (!PM_minimal) { - PM_minimal = new legacy::PassManager(); - addTargetPasses(PM_minimal, jl_TargetMachine); - PM_minimal->add(createRemoveJuliaAddrspacesPass()); } // get the source code for this function @@ -874,8 +866,6 @@ void *jl_get_llvmf_defn(jl_method_instance_t *mi, size_t world, char getwrapper, // if compilation succeeded, prepare to return the result if (optimize) PM->run(*m.get()); - else - PM_minimal->run(*m.get()); const std::string *fname; if (decls.functionObject == "jl_fptr_args" || decls.functionObject == "jl_fptr_sparam") getwrapper = false; diff --git a/src/disasm.cpp b/src/disasm.cpp index d251510698450..09feb37ad8e30 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -63,9 +63,11 @@ #include #include #include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/LegacyPassManager.h" #include "julia.h" #include "julia_internal.h" +#include "jitlayers.h" #include "processor.h" using namespace llvm; @@ -431,6 +433,13 @@ void jl_strip_llvm_debug(Module *m) jl_strip_llvm_debug(m, false, NULL); } +void jl_strip_llvm_addrspaces(Module *m) +{ + legacy::PassManager PM; + PM.add(createRemoveJuliaAddrspacesPass()); + PM.run(*m); +} + // print an llvm IR acquired from jl_get_llvmf // warning: this takes ownership of, and destroys, f->getParent() extern "C" JL_DLLEXPORT @@ -452,8 +461,13 @@ jl_value_t *jl_dump_function_ir(void *f, char strip_ir_metadata, char dump_modul } else { Module *m = llvmf->getParent(); - if (strip_ir_metadata) + if (strip_ir_metadata) { + std::string llvmfn = llvmf->getName(); jl_strip_llvm_debug(m, true, &AAW); + jl_strip_llvm_addrspaces(m); + // rewriting the function type creates a new function, so look it up again + llvmf = m->getFunction(llvmfn); + } if (dump_module) { m->print(stream, &AAW); } diff --git a/src/jitlayers.h b/src/jitlayers.h index 91ced69b210a2..eea8a8e215824 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -17,6 +17,8 @@ #include #include "julia_assert.h" +using namespace llvm; + extern "C" { extern int globalUnique; } From 35c1f87176fecab5a8e8077fb3a62275363a5aa5 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 19 Jun 2020 19:16:11 -0400 Subject: [PATCH 211/232] fix #36230, more efficient lowering of `if` with a chain of `&&` (#36355) --- base/expr.jl | 4 ++-- base/meta.jl | 5 +++-- src/julia-syntax.scm | 25 +++++++++++++++++++++---- test/compiler/inference.jl | 10 ++++++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/base/expr.jl b/base/expr.jl index db2d6333d01c1..3a1acc1a7d77e 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -347,9 +347,9 @@ function findmeta_block(exargs, argsmatch=args->true) for i = 1:length(exargs) a = exargs[i] if isa(a, Expr) - if (a::Expr).head === :meta && argsmatch((a::Expr).args) + if a.head === :meta && argsmatch(a.args) return i, exargs - elseif (a::Expr).head === :block + elseif a.head === :block idx, exa = findmeta_block(a.args, argsmatch) if idx != 0 return idx, exa diff --git a/base/meta.jl b/base/meta.jl index 4cf2ac3676dcd..560d66ea37311 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -62,8 +62,9 @@ true ``` """ isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head -isexpr(@nospecialize(ex), heads::Union{Set,Vector,Tuple}) = isa(ex, Expr) && in(ex.head, heads) -isexpr(@nospecialize(ex), heads, n::Int) = isexpr(ex, heads) && length((ex::Expr).args) == n +isexpr(@nospecialize(ex), heads) = isa(ex, Expr) && in(ex.head, heads) +isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n +isexpr(@nospecialize(ex), heads, n::Int) = isa(ex, Expr) && in(ex.head, heads) && length(ex.args) == n """ Meta.show_sexpr([io::IO,], ex) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index b292ee5bae8fb..67fa499cc9045 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1895,6 +1895,13 @@ (else (error (string "invalid " syntax-str " \"" (deparse el) "\"")))))))) +(define (expand-if e) + (if (and (pair? (cadr e)) (eq? (car (cadr e)) '&&)) + (let ((clauses (cdr (flatten-ex '&& (cadr e))))) + `(if (&& ,@(map expand-forms clauses)) + ,@(map expand-forms (cddr e)))) + (cons (car e) (map expand-forms (cdr e))))) + ;; move an assignment into the last statement of a block to keep more statements at top level (define (sink-assignment lhs rhs) (if (and (pair? rhs) (eq? (car rhs) 'block)) @@ -2230,6 +2237,9 @@ ,(expand-forms (cadr e)) ,(expand-forms (caddr e))) (map expand-forms e))) + 'if expand-if + 'elseif expand-if + 'while (lambda (e) `(break-block loop-exit @@ -3709,7 +3719,8 @@ f(x) = yt(x) (handler-level 0) ;; exception handler nesting depth (catch-token-stack '())) ;; tokens identifying handler enter for current catch blocks (define (emit c) - (set! code (cons c code))) + (set! code (cons c code)) + c) (define (make-label) (begin0 label-counter (set! label-counter (+ 1 label-counter)))) @@ -3957,15 +3968,21 @@ f(x) = yt(x) (compile (cadr e) break-labels value tail) #f)) ((if elseif) - (let ((test `(gotoifnot ,(compile-cond (cadr e) break-labels) _)) + (let ((tests (map (lambda (clause) + (emit `(gotoifnot ,(compile-cond clause break-labels) _))) + (if (and (pair? (cadr e)) (eq? (car (cadr e)) '&&)) + (cdadr e) + (list (cadr e))))) (end-jump `(goto _)) (val (if (and value (not tail)) (new-mutable-var) #f))) - (emit test) (let ((v1 (compile (caddr e) break-labels value tail))) (if val (emit-assignment val v1)) (if (and (not tail) (or (length> e 3) val)) (emit end-jump)) - (set-car! (cddr test) (make&mark-label)) + (let ((elselabel (make&mark-label))) + (for-each (lambda (test) + (set-car! (cddr test) elselabel)) + tests)) (let ((v2 (if (length> e 3) (compile (cadddr e) break-labels value tail) '(null)))) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 788469aba82b3..321ae26f02968 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2650,3 +2650,13 @@ end f(n) = depth(n, 1) end @test Base.return_types(TestConstPropRecursion.f, (TestConstPropRecursion.Node,)) == Any[Int] + +# issue #36230, keeping implications of all conditions in a && chain +function symcmp36230(vec) + a, b = vec[1], vec[2] + if isa(a, Symbol) && isa(b, Symbol) + return a == b + end + return false +end +@test Base.return_types(symcmp36230, (Vector{Any},)) == Any[Bool] From f6d34c3c32c535422491bfe980b1ebdb6bc123db Mon Sep 17 00:00:00 2001 From: binarygcd <63384853+binarygcd@users.noreply.github.com> Date: Sat, 20 Jun 2020 15:54:21 +0800 Subject: [PATCH 212/232] fix miscellaneous doc typos (#36370) --- base/regex.jl | 4 ++-- base/strings/basic.jl | 4 ++-- base/strings/io.jl | 2 +- stdlib/REPL/docs/src/index.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/base/regex.jl b/base/regex.jl index 1c94e245e3189..62b23008eb07d 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -582,8 +582,8 @@ end """ eachmatch(r::Regex, s::AbstractString; overlap::Bool=false) -Search for all matches of a the regular expression `r` in `s` and return a iterator over the -matches. If overlap is `true`, the matching sequences are allowed to overlap indices in the +Search for all matches of the regular expression `r` in `s` and return an iterator over the +matches. If `overlap` is `true`, the matching sequences are allowed to overlap indices in the original string, otherwise they must be from distinct character ranges. # Examples diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 37c4a0bd2a433..ccbb2ef16cfbc 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -72,7 +72,7 @@ ncodeunits(s::AbstractString) Return the code unit type of the given string object. For ASCII, Latin-1, or UTF-8 encoded strings, this would be `UInt8`; for UCS-2 and UTF-16 it would be -`UInt16`; for UTF-32 it would be `UInt32`. The unit code type need not be +`UInt16`; for UTF-32 it would be `UInt32`. The code unit type need not be limited to these three types, but it's hard to think of widely used string encodings that don't use one of these units. `codeunit(s)` is the same as `typeof(codeunit(s,1))` when `s` is a non-empty string. @@ -113,7 +113,7 @@ character in `s` or not. If `isvalid(s, i)` is true then `s[i]` will return the character whose encoding starts at that index, if it's false, then `s[i]` will raise an invalid index error or a bounds error depending on if `i` is in bounds. In order for `isvalid(s, i)` to be an O(1) function, the encoding of `s` must be -[self-synchronizing](https://en.wikipedia.org/wiki/Self-synchronizing_code) this +[self-synchronizing](https://en.wikipedia.org/wiki/Self-synchronizing_code). This is a basic assumption of Julia's generic string support. See also: [`getindex`](@ref), [`iterate`](@ref), [`thisind`](@ref), diff --git a/base/strings/io.jl b/base/strings/io.jl index 22a8d109cf0e7..e9037ed02d44d 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -256,7 +256,7 @@ IOBuffer(s::SubString{String}) = IOBuffer(view(unsafe_wrap(Vector{UInt8}, s.stri Join an array of `strings` into a single string, inserting the given delimiter (if any) between adjacent strings. If `last` is given, it will be used instead of `delim` between the last -two strings. If `io` is given, the result is written to `io` rather than returned as +two strings. If `io` is given, the result is written to `io` rather than returned as a `String`. `strings` can be any iterable over elements `x` which are convertible to strings diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 7a7eaf64ada8f..6f6ce0431e0a1 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -45,7 +45,7 @@ julia> ans In Julia mode, the REPL supports something called *prompt pasting*. This activates when pasting text that starts with `julia> ` into the REPL. In that case, only expressions starting with -`julia> ` are parsed, others are removed. This makes it is possible to paste a chunk of code +`julia> ` are parsed, others are removed. This makes it possible to paste a chunk of code that has been copied from a REPL session without having to scrub away prompts and outputs. This feature is enabled by default but can be disabled or enabled at will with `REPL.enable_promptpaste(::Bool)`. If it is enabled, you can try it out by pasting the code block above this paragraph straight into From 76a2e36ecc34db345ae82e0358abe3108830d289 Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Mon, 22 Jun 2020 03:29:23 -0700 Subject: [PATCH 213/232] Allow single option with REPL.TerminalMenus (#36369) --- stdlib/REPL/docs/src/index.md | 1 + stdlib/REPL/src/TerminalMenus/AbstractMenu.jl | 31 ++++++++++++------- .../REPL/src/TerminalMenus/MultiSelectMenu.jl | 4 +-- stdlib/REPL/src/TerminalMenus/RadioMenu.jl | 6 ++-- stdlib/REPL/src/TerminalMenus/config.jl | 11 ++++++- .../legacytests/old_multiselect_menu.jl | 6 ++-- .../legacytests/old_radio_menu.jl | 8 ++--- .../test/TerminalMenus/multiselect_menu.jl | 6 ++-- stdlib/REPL/test/TerminalMenus/radio_menu.jl | 8 ++--- 9 files changed, 48 insertions(+), 33 deletions(-) diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 6f6ce0431e0a1..ecdb7595fb62e 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -610,6 +610,7 @@ Aside from the overall `charset` option, for `RadioMenu` the configurable option - `cursor::Char='>'|'→'`: character to use for cursor - `up_arrow::Char='^'|'↑'`: character to use for up arrow - `down_arrow::Char='v'|'↓'`: character to use for down arrow + - `updown_arrow::Char='I'|'↕'`: character to use for up/down arrow in one-line page - `scroll_wrap::Bool=false`: optionally wrap-around at the beginning/end of a menu - `ctrl_c_interrupt::Bool=true`: If `false`, return empty on ^C, if `true` throw InterruptException() on ^C diff --git a/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl b/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl index ebbebd83671d3..2b5676cb77706 100644 --- a/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl @@ -256,7 +256,8 @@ end function move_down!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) if cursor < lastoption cursor += 1 # move selection down - if m.pagesize + m.pageoffset <= cursor < lastoption + pagepos = m.pagesize + m.pageoffset + if pagepos <= cursor && pagepos < lastoption m.pageoffset += 1 # scroll page down end elseif scroll_wrap(m) @@ -315,17 +316,23 @@ function printmenu(out, m::AbstractMenu, cursoridx::Int; oldstate=nothing, init: # clearline print(buf, "\x1b[2K") - if i == firstline && m.pageoffset > 0 - print_arrow(buf, m, up_arrow(m)) - elseif i == lastline && i != lastoption - print_arrow(buf, m, down_arrow(m)) + upscrollable = i == firstline && m.pageoffset > 0 + downscrollable = i == lastline && i != lastoption + + if upscrollable && downscrollable + print(buf, updown_arrow(m)) + elseif upscrollable + print(buf, up_arrow(m)) + elseif downscrollable + print(buf, down_arrow(m)) else - printcursor(buf, m, i == cursoridx) + print(buf, ' ') end + printcursor(buf, m, i == cursoridx) writeline(buf, m, i, i == cursoridx) - i != lastline && print(buf, "\r\n") + (firstline == lastline || i != lastline) && print(buf, "\r\n") end newstate = lastline-firstline # final line doesn't have `\n` @@ -362,10 +369,12 @@ down_arrow(c::AbstractConfig) = down_arrow(c.config) down_arrow(c::Config) = c.down_arrow down_arrow(::AbstractMenu) = CONFIG[:down_arrow] -print_arrow(buf, ::ConfiguredMenu, c::Char) = print(buf, c, " ") -print_arrow(buf, ::AbstractMenu, c::Char) = print(buf, c) +updown_arrow(m::ConfiguredMenu) = updown_arrow(m.config) +updown_arrow(c::AbstractConfig) = updown_arrow(c.config) +updown_arrow(c::Config) = c.updown_arrow +updown_arrow(::AbstractMenu) = CONFIG[:updown_arrow] -printcursor(buf, m::ConfiguredMenu, iscursor::Bool) = print(buf, ' ', iscursor ? cursor(m.config) : ' ', ' ') +printcursor(buf, m::ConfiguredMenu, iscursor::Bool) = print(buf, iscursor ? cursor(m.config) : ' ', ' ') cursor(c::AbstractConfig) = cursor(c.config) cursor(c::Config) = c.cursor -printcursor(buf, ::AbstractMenu, ::Bool) = print(buf, ' ') # `writeLine` is expected to do the printing (get from CONFIG[:cursor]) +printcursor(buf, ::AbstractMenu, ::Bool) = nothing # `writeLine` is expected to do the printing (get from CONFIG[:cursor]) diff --git a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl index 9bbc658085c90..b68255c3ecef2 100644 --- a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl @@ -53,14 +53,14 @@ were selected by the user. Any additional keyword arguments will be passed to [`TerminalMenus.MultiSelectConfig`](@ref). """ function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, selected=Int[], warn::Bool=true, kwargs...) - length(options) < 2 && error("MultiSelectMenu must have at least two options") + length(options) < 1 && error("MultiSelectMenu must have at least one option") # if pagesize is -1, use automatic paging pagesize = pagesize == -1 ? length(options) : pagesize # pagesize shouldn't be bigger than options pagesize = min(length(options), pagesize) # after other checks, pagesize must be greater than 2 - pagesize < 2 && error("pagesize must be >= 2") + pagesize < 1 && error("pagesize must be >= 1") pageoffset = 0 _selected = Set{Int}() diff --git a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl index 6b777e058e975..c8bdc557377b9 100644 --- a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl @@ -44,14 +44,14 @@ user. Any additional keyword arguments will be passed to [`TerminalMenus.Config`](@ref). """ function RadioMenu(options::Array{String,1}; pagesize::Int=10, warn::Bool=true, kwargs...) - length(options) < 2 && error("RadioMenu must have at least two options") + length(options) < 1 && error("RadioMenu must have at least one option") # if pagesize is -1, use automatic paging pagesize = pagesize == -1 ? length(options) : pagesize # pagesize shouldn't be bigger than options pagesize = min(length(options), pagesize) - # after other checks, pagesize must be greater than 2 - pagesize < 2 && error("pagesize must be >= 2") + # after other checks, pagesize must be greater than 1 + pagesize < 1 && error("pagesize must be >= 1") pageoffset = 0 selected = -1 # none diff --git a/stdlib/REPL/src/TerminalMenus/config.jl b/stdlib/REPL/src/TerminalMenus/config.jl index 9aded1ee0a186..9c8f001be6c5f 100644 --- a/stdlib/REPL/src/TerminalMenus/config.jl +++ b/stdlib/REPL/src/TerminalMenus/config.jl @@ -6,6 +6,7 @@ struct Config <: AbstractConfig cursor::Char up_arrow::Char down_arrow::Char + updown_arrow::Char scroll_wrap::Bool ctrl_c_interrupt::Bool end @@ -46,6 +47,7 @@ function Config(; cursor::Char = '\0', up_arrow::Char = '\0', down_arrow::Char = '\0', + updown_arrow::Char = '\0', scroll_wrap::Bool = false, ctrl_c_interrupt::Bool = true) charset === :ascii || charset === :unicode || @@ -59,7 +61,10 @@ function Config(; if down_arrow == '\0' down_arrow = charset === :ascii ? 'v' : '↓' end - return Config(cursor, up_arrow, down_arrow, scroll_wrap, ctrl_c_interrupt) + if updown_arrow == '\0' + updown_arrow = charset === :ascii ? 'I' : '↕' + end + return Config(cursor, up_arrow, down_arrow, updown_arrow, scroll_wrap, ctrl_c_interrupt) end """ @@ -133,6 +138,7 @@ function config(;charset::Symbol = :na, cursor::Char = '\0', up_arrow::Char = '\0', down_arrow::Char = '\0', + updown_arrow::Char = '\0', checked::String = "", unchecked::String = "", supress_output::Union{Nothing, Bool}=nothing, # typo was documented, unfortunately @@ -142,12 +148,14 @@ function config(;charset::Symbol = :na, cursor = '>' up_arrow = '^' down_arrow = 'v' + updown_arrow = 'I' checked = "[X]" unchecked = "[ ]" elseif charset === :unicode cursor = '→' up_arrow = '↑' down_arrow = '↓' + updown_arrow = '↕' checked = "✓" unchecked = "⬚" elseif charset === :na @@ -162,6 +170,7 @@ function config(;charset::Symbol = :na, cursor != '\0' && (CONFIG[:cursor] = cursor) up_arrow != '\0' && (CONFIG[:up_arrow] = up_arrow) down_arrow != '\0' && (CONFIG[:down_arrow] = down_arrow) + updown_arrow != '\0' && (CONFIG[:updown_arrow] = updown_arrow) checked != "" && (CONFIG[:checked] = checked) unchecked != "" && (CONFIG[:unchecked] = unchecked) supress_output isa Bool && (CONFIG[:supress_output] = supress_output) diff --git a/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl b/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl index 0c1bd08940289..fad96980d53d7 100644 --- a/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/legacytests/old_multiselect_menu.jl @@ -5,10 +5,6 @@ # Check to make sure types are imported properly @test MultiSelectMenu <: TerminalMenus.AbstractMenu -# Invalid Menu Params -@test_throws ErrorException MultiSelectMenu(["one"], warn=false) -@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1, warn=false) - # Constructor @test MultiSelectMenu(["one", "two", "three"], warn=false).pagesize == 3 @test MultiSelectMenu(string.(1:30), pagesize=-1, warn=false).pagesize == 30 @@ -37,3 +33,5 @@ TerminalMenus.writeLine(buf, multi_menu, 1, true) # Test SDTIN multi_menu = MultiSelectMenu(string.(1:10), warn=false) @test simulate_input(Set([1,2]), multi_menu, :enter, :down, :enter, 'd') +multi_menu = MultiSelectMenu(["single option"], warn=false) +@test simulate_input(Set([1]), multi_menu, :up, :up, :down, :enter, 'd') diff --git a/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl b/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl index 05038af460835..84f07ebca8812 100644 --- a/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/legacytests/old_radio_menu.jl @@ -5,10 +5,6 @@ # Check to make sure types are imported properly @test RadioMenu <: TerminalMenus.AbstractMenu -# Invalid Menu Params -@test_throws ErrorException RadioMenu(["one"], warn=false) -@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1, warn=false) - # Constructor @test RadioMenu(["one", "two", "three"], warn=false).pagesize == 3 @test RadioMenu(string.(1:30), pagesize=-1, warn=false).pagesize == 30 @@ -40,3 +36,7 @@ TerminalMenus.writeLine(buf, radio_menu, 1, true) # Test using stdin radio_menu = RadioMenu(string.(1:10), warn=false) @test simulate_input(3, radio_menu, :down, :down, :enter) +radio_menu = RadioMenu(["single option"], warn=false) +@test simulate_input(1, radio_menu, :up, :up, :down, :up, :enter) +radio_menu = RadioMenu(string.(1:3), pagesize=1, warn=false) +@test simulate_input(3, radio_menu, :down, :down, :down, :down, :enter) diff --git a/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl b/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl index bfeb27590baf0..d47d6285be3ce 100644 --- a/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl @@ -6,10 +6,6 @@ # Check to make sure types are imported properly @test MultiSelectMenu{TerminalMenus.MultiSelectConfig} <: TerminalMenus.ConfiguredMenu # TODO Julia 2.0: delete parameter -# Invalid Menu Params -@test_throws ErrorException MultiSelectMenu(["one"], charset=:ascii) -@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1, charset=:ascii) - # Constructor @test MultiSelectMenu(["one", "two", "three"], charset=:ascii).pagesize == 3 @test MultiSelectMenu(string.(1:30), pagesize=-1, charset=:ascii).pagesize == 30 @@ -57,3 +53,5 @@ end # Test SDTIN multi_menu = MultiSelectMenu(string.(1:10), charset=:ascii) @test simulate_input(Set([1,2]), multi_menu, :enter, :down, :enter, 'd') +multi_menu = MultiSelectMenu(["single option"], charset=:ascii) +@test simulate_input(Set([1]), multi_menu, :up, :up, :down, :enter, 'd') diff --git a/stdlib/REPL/test/TerminalMenus/radio_menu.jl b/stdlib/REPL/test/TerminalMenus/radio_menu.jl index a45bb39acb7b1..e0645afe40371 100644 --- a/stdlib/REPL/test/TerminalMenus/radio_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/radio_menu.jl @@ -6,10 +6,6 @@ # Check to make sure types are imported properly @test RadioMenu{TerminalMenus.Config} <: TerminalMenus.ConfiguredMenu # TODO Julia 2.0: delete parameter -# Invalid Menu Params -@test_throws ErrorException RadioMenu(["one"]; charset=:ascii) -@test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1, charset=:ascii) - # Constructor @test RadioMenu(["one", "two", "three"]; charset=:ascii).pagesize == 3 @test RadioMenu(string.(1:30), pagesize=-1, charset=:ascii).pagesize == 30 @@ -42,3 +38,7 @@ end # Test using stdin radio_menu = RadioMenu(string.(1:10); charset=:ascii) @test simulate_input(3, radio_menu, :down, :down, :enter) +radio_menu = RadioMenu(["single option"], charset=:ascii) +@test simulate_input(1, radio_menu, :up, :up, :down, :up, :enter) +radio_menu = RadioMenu(string.(1:3), pagesize=1, charset=:ascii) +@test simulate_input(3, radio_menu, :down, :down, :down, :down, :enter) From e0babe8215885eafd888fa7f295c169afca6e6c3 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 22 Jun 2020 20:19:10 -0400 Subject: [PATCH 214/232] fix return type of `get!` on `IdDict` (#36383) --- base/iddict.jl | 30 +++++++++++++++++++++++------- test/dict.jl | 3 ++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/base/iddict.jl b/base/iddict.jl index 23ba65799a395..93e0409cbd5c4 100644 --- a/base/iddict.jl +++ b/base/iddict.jl @@ -85,8 +85,9 @@ function get(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where { val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, default) val === default ? default : val::V end + function getindex(d::IdDict{K,V}, @nospecialize(key)) where {K, V} - val = get(d, key, secret_table_token) + val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) val === secret_table_token && throw(KeyError(key)) return val::V end @@ -134,23 +135,38 @@ length(d::IdDict) = d.count copy(d::IdDict) = typeof(d)(d) -get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} = (d[key] = get(d, key, default))::V +function get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} + val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) + if val === secret_table_token + val = isa(default, V) ? default : convert(V, default) + setindex!(d, val, key) + return val + else + return val::V + end +end function get(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V} - val = get(d, key, secret_table_token) + val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) if val === secret_table_token - val = default() + return default() + else + return val::V end - return val end function get!(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V} - val = get(d, key, secret_table_token) + val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) if val === secret_table_token val = default() + if !isa(val, V) + val = convert(V, val) + end setindex!(d, val, key) + return val + else + return val::V end - return val end in(@nospecialize(k), v::KeySet{<:Any,<:IdDict}) = get(v.dict, k, secret_table_token) !== secret_table_token diff --git a/test/dict.jl b/test/dict.jl index 176314671f937..de455576b2bc4 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -554,7 +554,8 @@ end @test delete!(d, "a") === d @test !haskey(d, "a") @test_throws ArgumentError get!(IdDict{Symbol,Any}(), 2, "b") - + @test get!(IdDict{Int,Int}(), 1, 2.0) === 2 + @test get!(()->2.0, IdDict{Int,Int}(), 1) === 2 # sizehint! & rehash! d = IdDict() From fd6eee7da75f39e09cba2e38c496b14182e394d5 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 23 Jun 2020 11:31:46 -0400 Subject: [PATCH 215/232] Refactor `abstract_eval` to separate out statements and values (#36350) In preparation for adding the ability for statements to return additional info, while values will only ever have a type. --- base/compiler/abstractinterpretation.jl | 67 ++++++++++++++++--------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 2faffbadb6e50..861758576aedd 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -970,7 +970,7 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) end function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::VarTable, sv::InferenceState) - f = abstract_eval(interp, e.args[2], vtypes, sv) + f = abstract_eval_value(interp, e.args[2], vtypes, sv) # rt = sp_type_rewrap(e.args[3], sv.linfo, true) at = Any[ sp_type_rewrap(argt, sv.linfo, false) for argt in e.args[4]::SimpleVector ] pushfirst!(at, f) @@ -981,7 +981,21 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V nothing end -function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) +function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::VarTable, sv::InferenceState) + if e.head === :static_parameter + n = e.args[1] + t = Any + if 1 <= n <= length(sv.sptypes) + t = sv.sptypes[n] + end + elseif e.head === :boundscheck + return Bool + else + return Any + end +end + +function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) if isa(e, QuoteNode) return AbstractEvalConstant((e::QuoteNode).value) elseif isa(e, SSAValue) @@ -992,16 +1006,29 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va return abstract_eval_global(e.mod, e.name) end + return AbstractEvalConstant(e) +end + +function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) + if isa(e, Expr) + return abstract_eval_value_expr(interp, e, vtypes, sv) + else + return abstract_eval_special_value(interp, e, vtypes, sv) + end +end + +function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState) if !isa(e, Expr) - return AbstractEvalConstant(e) + return abstract_eval_special_value(interp, e, vtypes, sv) end + e = e::Expr if e.head === :call ea = e.args n = length(ea) argtypes = Vector{Any}(undef, n) @inbounds for i = 1:n - ai = abstract_eval(interp, ea[i], vtypes, sv) + ai = abstract_eval_value(interp, ea[i], vtypes, sv) if ai === Bottom return Bottom end @@ -1009,14 +1036,14 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va end t = abstract_call(interp, ea, argtypes, vtypes, sv) elseif e.head === :new - t = instanceof_tfunc(abstract_eval(interp, e.args[1], vtypes, sv))[1] + t = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv))[1] if isconcretetype(t) && !t.mutable args = Vector{Any}(undef, length(e.args)-1) ats = Vector{Any}(undef, length(e.args)-1) anyconst = false allconst = true for i = 2:length(e.args) - at = abstract_eval(interp, e.args[i], vtypes, sv) + at = abstract_eval_value(interp, e.args[i], vtypes, sv) if !anyconst anyconst = has_nontrivial_const_info(at) end @@ -1046,9 +1073,9 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va end end elseif e.head === :splatnew - t = instanceof_tfunc(abstract_eval(interp, e.args[1], vtypes, sv))[1] + t = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv))[1] if length(e.args) == 2 && isconcretetype(t) && !t.mutable - at = abstract_eval(interp, e.args[2], vtypes, sv) + at = abstract_eval_value(interp, e.args[2], vtypes, sv) n = fieldcount(t) if isa(at, Const) && isa(at.val, Tuple) && n == length(at.val) && _all(i->at.val[i] isa fieldtype(t, i), 1:n) @@ -1059,10 +1086,10 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va end end elseif e.head === :foreigncall - abstract_eval(interp, e.args[1], vtypes, sv) + abstract_eval_value(interp, e.args[1], vtypes, sv) t = sp_type_rewrap(e.args[2], sv.linfo, true) for i = 3:length(e.args) - if abstract_eval(interp, e.args[i], vtypes, sv) === Bottom + if abstract_eval_value(interp, e.args[i], vtypes, sv) === Bottom t = Bottom end end @@ -1070,24 +1097,16 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va t = e.args[1] isa(t, Type) || (t = Any) abstract_eval_cfunction(interp, e, vtypes, sv) - elseif e.head === :static_parameter - n = e.args[1] - t = Any - if 1 <= n <= length(sv.sptypes) - t = sv.sptypes[n] - end elseif e.head === :method t = (length(e.args) == 1) ? Any : Nothing elseif e.head === :copyast - t = abstract_eval(interp, e.args[1], vtypes, sv) + t = abstract_eval_value(interp, e.args[1], vtypes, sv) if t isa Const && t.val isa Expr # `copyast` makes copies of Exprs t = Expr end elseif e.head === :invoke error("type inference data-flow error: tried to double infer a function") - elseif e.head === :boundscheck - return Bool elseif e.head === :isdefined sym = e.args[1] t = Bool @@ -1116,7 +1135,7 @@ function abstract_eval(interp::AbstractInterpreter, @nospecialize(e), vtypes::Va end end else - t = Any + return abstract_eval_value_expr(interp, e, vtypes, sv) end @assert !isa(t, TypeVar) if isa(t, DataType) && isdefined(t, :instance) @@ -1175,7 +1194,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) elseif isa(stmt, GotoNode) pc´ = (stmt::GotoNode).label elseif isa(stmt, GotoIfNot) - condt = abstract_eval(interp, stmt.cond, s[pc], frame) + condt = abstract_eval_value(interp, stmt.cond, s[pc], frame) if condt === Bottom break end @@ -1209,7 +1228,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end elseif isa(stmt, ReturnNode) pc´ = n + 1 - rt = widenconditional(abstract_eval(interp, stmt.val, s[pc], frame)) + rt = widenconditional(abstract_eval_value(interp, stmt.val, s[pc], frame)) if !isa(rt, Const) && !isa(rt, Type) && !isa(rt, PartialStruct) # only propagate information we know we can store # and is valid inter-procedurally @@ -1254,7 +1273,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end else if hd === :(=) - t = abstract_eval(interp, stmt.args[2], changes, frame) + t = abstract_eval_statement(interp, stmt.args[2], changes, frame) t === Bottom && break frame.src.ssavaluetypes[pc] = t lhs = stmt.args[1] @@ -1269,7 +1288,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) elseif hd === :inbounds || hd === :meta || hd === :loopinfo || hd == :code_coverage_effect # these do not generate code else - t = abstract_eval(interp, stmt, changes, frame) + t = abstract_eval_statement(interp, stmt, changes, frame) t === Bottom && break if !isempty(frame.ssavalue_uses[pc]) record_ssa_assign(pc, t, frame) From 8db94953f2c0d0d8de70e1c08cf0e1fe840ac070 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 23 Jun 2020 11:36:57 -0400 Subject: [PATCH 216/232] fix documentation typo ("Ingeger") --- stdlib/SparseArrays/src/sparsematrix.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 78ad80101dd8e..8b53bca15c7bd 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -3526,7 +3526,7 @@ end """ spdiagm(kv::Pair{<:Integer,<:AbstractVector}...) - spdiagm(m::Integer, n::Ingeger, kv::Pair{<:Integer,<:AbstractVector}...) + spdiagm(m::Integer, n::Integer, kv::Pair{<:Integer,<:AbstractVector}...) Construct a sparse diagonal matrix from `Pair`s of vectors and diagonals. Each vector `kv.second` will be placed on the `kv.first` diagonal. By From b59417235f56f251a74fb2878267238155b19f75 Mon Sep 17 00:00:00 2001 From: Gautam Mishra Date: Tue, 23 Jun 2020 23:00:10 +0530 Subject: [PATCH 217/232] Add doctest: eachslice (#36386) * add doctests for eachslice * update doctest eachslice --- base/abstractarraymath.jl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl index e8d9348da6767..05ab7fe699e24 100644 --- a/base/abstractarraymath.jl +++ b/base/abstractarraymath.jl @@ -514,6 +514,28 @@ See also [`eachrow`](@ref), [`eachcol`](@ref), and [`selectdim`](@ref). !!! compat "Julia 1.1" This function requires at least Julia 1.1. + +# Example + +```jldoctest +julia> M = [1 2 3; 4 5 6; 7 8 9] +3×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + 7 8 9 + +julia> first(eachslice(M, dims=1)) +3-element view(::Array{Int64,2}, 1, :) with eltype Int64: + 1 + 2 + 3 + +julia> collect(eachslice(M, dims=2)) +3-element Array{SubArray{Int64,1,Array{Int64,2},Tuple{Base.Slice{Base.OneTo{Int64}},Int64},true},1}: + [1, 4, 7] + [2, 5, 8] + [3, 6, 9] +``` """ @inline function eachslice(A::AbstractArray; dims) length(dims) == 1 || throw(ArgumentError("only single dimensions are supported")) From 9b2c6b2cb2cae4db862986b02467fae58987530d Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 23 Jun 2020 21:16:55 +0200 Subject: [PATCH 218/232] fix showing methods with unicode gensymed variable names (#36396) --- base/methodshow.jl | 2 +- test/show.jl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/base/methodshow.jl b/base/methodshow.jl index 2762acfb52a5c..ea5f14f9c1ab1 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -13,7 +13,7 @@ function argtype_decl(env, n, sig::DataType, i::Int, nargs, isva::Bool) # -> (ar s = string(n) i = findfirst(isequal('#'), s) if i !== nothing - s = s[1:i-1] + s = s[1:prevind(s, i)] end if t === Any && !isempty(s) return s, "" diff --git a/test/show.jl b/test/show.jl index ac628739fd5bf..1a398974addd1 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1985,3 +1985,12 @@ end @test sprint(show, skipmissing([1,2,missing])) == "skipmissing(Union{Missing, $Int}[1, 2, missing])" @test sprint(show, skipmissing((missing,1.0,'a'))) == "skipmissing((missing, 1.0, 'a'))" end + +@testset "unicode in method table" begin + αsym = gensym(:α) + ℓsym = gensym(:ℓ) + eval(:(foo($αsym) = $αsym)) + eval(:(bar($ℓsym) = $ℓsym)) + @test contains(string(methods(foo)), "foo(α)") + @test contains(string(methods(bar)), "bar(ℓ)") +end From 6cd329c371c1db3d9876bc337e82e274e50420e8 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Tue, 23 Jun 2020 22:23:39 +0200 Subject: [PATCH 219/232] add sincospi (#35816) --- base/exports.jl | 1 + base/math.jl | 2 +- base/special/trig.jl | 130 +++++++++++++++++++++++++++++++++++++++++-- test/math.jl | 37 ++++++++---- 4 files changed, 151 insertions(+), 19 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index f47e1f719cbf2..d695714665aab 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -329,6 +329,7 @@ export sinc, sincos, sincosd, + sincospi, sind, sinh, sinpi, diff --git a/base/math.jl b/base/math.jl index 768c556e0fcb4..3868cbfa67898 100644 --- a/base/math.jl +++ b/base/math.jl @@ -5,7 +5,7 @@ module Math export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot, sech, csch, coth, asech, acsch, acoth, - sinpi, cospi, sinc, cosc, + sinpi, cospi, sincospi, sinc, cosc, cosd, cotd, cscd, secd, sind, tand, sincosd, acosd, acotd, acscd, asecd, asind, atand, rad2deg, deg2rad, diff --git a/base/special/trig.jl b/base/special/trig.jl index c848ebfe492d1..0ebf2c7f6c358 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -777,8 +777,8 @@ function sinpi(x::T) where T<:AbstractFloat end end -# Integers and Rationals -function sinpi(x::T) where T<:Union{Integer,Rational} +# Rationals +function sinpi(x::T) where T<:Rational Tf = float(T) if !isfinite(x) throw(DomainError(x, "`x` must be finite.")) @@ -836,8 +836,8 @@ function cospi(x::T) where T<:AbstractFloat end end -# Integers and Rationals -function cospi(x::T) where T<:Union{Integer,Rational} +# Rationals +function cospi(x::T) where T<:Rational if !isfinite(x) throw(DomainError(x, "`x` must be finite.")) end @@ -860,10 +860,79 @@ function cospi(x::T) where T<:Union{Integer,Rational} end end +""" + sincospi(x) + +Simultaneously compute `sinpi(x)` and `cospi(x)`, where the `x` is in radians. +""" +function sincospi(x::T) where T<:AbstractFloat + if !isfinite(x) + isnan(x) && return x, x + throw(DomainError(x, "`x` cannot be infinite.")) + end + + ax = abs(x) + s = maxintfloat(T) + ax >= s && return (copysign(zero(T), x), one(T)) # even integer-valued + + # reduce to interval [-1,1] + # assumes RoundNearest rounding mode + t = 3*(s/2) + rx = x-((x+t)-t) # zeros may be incorrectly signed + arx = abs(rx) + + # same selection scheme as sinpi and cospi + if (arx == 0) | (arx == 1) + return copysign(zero(T), x), ifelse(ax % 2 == 0, one(T), -one(T)) + elseif arx < 0.25 + return sincos_kernel(mulpi_ext(rx)) + elseif arx < 0.75 + y = mulpi_ext(T(0.5) - arx) + return copysign(cos_kernel(y), rx), sin_kernel(y) + else + y_si = mulpi_ext(copysign(one(T), rx) - rx) + y_co = mulpi_ext(one(T) - arx) + return sin_kernel(y_si), -cos_kernel(y_co) + end +end + +# Rationals +function sincospi(x::T) where T<:Rational + Tf = float(T) + if !isfinite(x) + throw(DomainError(x, "`x` must be finite.")) + end + + # until we get an IEEE remainder function (#9283) + rx = rem(x,2) + if rx > 1 + rx -= 2 + elseif rx < -1 + rx += 2 + end + arx = abs(rx) + + # same selection scheme as sinpi and cospi + if (arx == 0) | (arx == 1) + return copysign(zero(Tf),x), ifelse(iseven(numerator(x)), one(Tf), -one(Tf)) + elseif arx < 0.25 + return sincos_kernel(mulpi_ext(rx)) + elseif arx < 0.75 + y = mulpi_ext(T(0.5) - arx) + return copysign(cos_kernel(y), rx), sin_kernel(y) + else + y_si = mulpi_ext(copysign(one(T), rx) - rx) + y_co = mulpi_ext(one(T) - arx) + return sin_kernel(y_si), -cos_kernel(y_co) + end +end + sinpi(x::Integer) = x >= 0 ? zero(float(x)) : -zero(float(x)) cospi(x::Integer) = isodd(x) ? -one(float(x)) : one(float(x)) +sincospi(x::Integer) = (sinpi(x), cospi(x)) sinpi(x::Real) = sinpi(float(x)) cospi(x::Real) = cospi(float(x)) +sincospi(x::Real) = sincospi(float(x)) function sinpi(z::Complex{T}) where T F = float(T) @@ -893,7 +962,8 @@ function sinpi(z::Complex{T}) where T end else pizi = pi*zi - Complex(sinpi(zr)*cosh(pizi), cospi(zr)*sinh(pizi)) + sipi, copi = sincospi(zr) + Complex(sipi*cosh(pizi), copi*sinh(pizi)) end end @@ -928,7 +998,55 @@ function cospi(z::Complex{T}) where T end else pizi = pi*zi - Complex(cospi(zr)*cosh(pizi), -sinpi(zr)*sinh(pizi)) + sipi, copi = sincospi(zr) + Complex(copi*cosh(pizi), -sipi*sinh(pizi)) + end +end + +function sincospi(z::Complex{T}) where T + F = float(T) + zr, zi = reim(z) + if isinteger(zr) + # zr = ...,-2,-1,0,1,2,... + # sin(pi*zr) == ±0 + # cos(pi*zr) == ±1 + # cosh(pi*zi) > 0 + s = copysign(zero(F),zr) + c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2) + pizi = pi*zi + sh, ch = sinh(pizi), cosh(pizi) + ( + Complex(s, c_pos ? sh : -sh), + Complex(c_pos ? ch : -ch, isnan(zi) ? s : -flipsign(s,zi)), + ) + elseif isinteger(2*zr) + # zr = ...,-1.5,-0.5,0.5,1.5,2.5,... + # sin(pi*zr) == ±1 + # cos(pi*zr) == +0 + # sign(sinh(pi*zi)) == sign(zi) + s_pos = isinteger((2*zr-1)/4) + pizi = pi*zi + sh, ch = sinh(pizi), cosh(pizi) + ( + Complex(s_pos ? ch : -ch, isnan(zi) ? zero(F) : copysign(zero(F),zi)), + Complex(zero(F), s_pos ? -sh : sh), + ) + elseif !isfinite(zr) + if zi == 0 + Complex(F(NaN), F(zi)), Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) + elseif isinf(zi) + Complex(F(NaN), F(zi)), Complex(F(Inf), F(NaN)) + else + Complex(F(NaN), F(NaN)), Complex(F(NaN), F(NaN)) + end + else + pizi = pi*zi + sipi, copi = sincospi(zr) + sihpi, cohpi = sinh(pizi), cosh(pizi) + ( + Complex(sipi*cohpi, copi*sihpi), + Complex(copi*cohpi, -sipi*sihpi), + ) end end diff --git a/test/math.jl b/test/math.jl index d98da02958941..cd86ec9061668 100644 --- a/test/math.jl +++ b/test/math.jl @@ -408,8 +408,11 @@ end @test sincosd(convert(T, 270))::fTsc === ( -one(fT), zero(fT) ) end - @testset "sinpi and cospi" begin - for x = -3:0.3:3 + @testset "$name" for (name, (sinpi, cospi)) in ( + "sinpi and cospi" => (sinpi, cospi), + "sincospi" => (x->sincospi(x)[1], x->sincospi(x)[2]) + ) + @testset "pi * $x" for x = -3:0.3:3 @test sinpi(convert(T,x))::fT ≈ convert(fT,sin(pi*x)) atol=eps(pi*convert(fT,x)) @test cospi(convert(T,x))::fT ≈ convert(fT,cos(pi*x)) atol=eps(pi*convert(fT,x)) end @@ -433,10 +436,13 @@ end @test cosd(convert(T,60)) == 0.5 @test sind(convert(T,150)) == 0.5 @test sinpi(one(T)/convert(T,6)) == 0.5 + @test sincospi(one(T)/convert(T,6))[1] == 0.5 @test_throws DomainError sind(convert(T,Inf)) @test_throws DomainError cosd(convert(T,Inf)) T != Float32 && @test cospi(one(T)/convert(T,3)) == 0.5 + T != Float32 && @test sincospi(one(T)/convert(T,3))[2] == 0.5 T == Rational{Int} && @test sinpi(5//6) == 0.5 + T == Rational{Int} && @test sincospi(5//6)[1] == 0.5 end end scdm = sincosd(missing) @@ -445,10 +451,12 @@ end end @testset "Integer args to sinpi/cospi/sinc/cosc" begin - @test sinpi(1) == 0 - @test sinpi(-1) == -0 - @test cospi(1) == -1 - @test cospi(2) == 1 + for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2])) + @test sinpi(1) == 0 + @test sinpi(-1) == -0 + @test cospi(1) == -1 + @test cospi(2) == 1 + end @test sinc(1) == 0 @test sinc(complex(1,0)) == 0 @@ -462,20 +470,25 @@ end @testset "Irrational args to sinpi/cospi/sinc/cosc" begin for x in (pi, ℯ, Base.MathConstants.golden) - @test sinpi(x) ≈ Float64(sinpi(big(x))) - @test cospi(x) ≈ Float64(cospi(big(x))) + for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2])) + @test sinpi(x) ≈ Float64(sinpi(big(x))) + @test cospi(x) ≈ Float64(cospi(big(x))) + @test sinpi(complex(x, x)) ≈ Complex{Float64}(sinpi(complex(big(x), big(x)))) + @test cospi(complex(x, x)) ≈ Complex{Float64}(cospi(complex(big(x), big(x)))) + end @test sinc(x) ≈ Float64(sinc(big(x))) @test cosc(x) ≈ Float64(cosc(big(x))) - @test sinpi(complex(x, x)) ≈ Complex{Float64}(sinpi(complex(big(x), big(x)))) - @test cospi(complex(x, x)) ≈ Complex{Float64}(cospi(complex(big(x), big(x)))) @test sinc(complex(x, x)) ≈ Complex{Float64}(sinc(complex(big(x), big(x)))) @test cosc(complex(x, x)) ≈ Complex{Float64}(cosc(complex(big(x), big(x)))) end end @testset "trig function type stability" begin - @testset "$T $f" for T = (Float32,Float64,BigFloat), f = (sind,cosd,sinpi,cospi) - @test Base.return_types(f,Tuple{T}) == [T] + @testset "$T $f" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16), f = (sind,cosd,sinpi,cospi) + @test Base.return_types(f,Tuple{T}) == [float(T)] + end + @testset "$T sincospi" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16) + @test Base.return_types(sincospi,Tuple{T}) == [Tuple{float(T),float(T)}] end end From 1ba71f90adbf0e6bd2f51187eb6361169a34934e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Farneb=C3=A4ck?= Date: Wed, 24 Jun 2020 18:58:21 +0200 Subject: [PATCH 220/232] Fix spelling of readdir. (#36409) --- base/file.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/file.jl b/base/file.jl index 1b21f72f5604b..f392f0d9007b4 100644 --- a/base/file.jl +++ b/base/file.jl @@ -710,7 +710,7 @@ back, call `readdir` with an absolute directory path and `join` set to true. By default, `readdir` sorts the list of names it returns. If you want to skip sorting the names and get them in the order that the file system lists them, -you can use `readir(dir, sort=false)` to opt out of sorting. +you can use `readdir(dir, sort=false)` to opt out of sorting. !!! compat "Julia 1.4" The `join` and `sort` keyword arguments require at least Julia 1.4. From 3604a253518e3914087b97474d12bfc825950251 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 24 Jun 2020 16:56:27 -0400 Subject: [PATCH 221/232] add versions of `code_typed` and `which` that accept tuple types (#36389) --- base/reflection.jl | 42 ++++++++++++++++++++++++++++++++++++++---- test/reflection.jl | 6 ++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 96035b178ffde..9b6ae00e153f3 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1081,10 +1081,31 @@ function code_typed(@nospecialize(f), @nospecialize(types=Tuple); debuginfo::Symbol=:default, world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) - ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) end + ft = Core.Typeof(f) + if isa(types, Type) + u = unwrap_unionall(types) + tt = rewrap_unionall(Tuple{ft, u.parameters...}, types) + else + tt = Tuple{ft, types...} + end + return code_typed_by_type(tt; optimize, debuginfo, world, interp) +end + +""" + code_typed_by_type(types::Type{<:Tuple}; ...) + +Similar to [`code_typed`](@ref), except the argument is a tuple type describing +a full signature to query. +""" +function code_typed_by_type(@nospecialize(tt::Type); + optimize=true, + debuginfo::Symbol=:default, + world = get_world_counter(), + interp = Core.Compiler.NativeInterpreter(world)) + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default @@ -1093,10 +1114,14 @@ function code_typed(@nospecialize(f), @nospecialize(types=Tuple); if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end - types = to_tuple_type(types) + tt = to_tuple_type(tt) + meths = _methods_by_ftype(tt, -1, world) + if meths === false + error("signature does not correspond to a generic function") + end asts = [] - for x in _methods(f, types, -1, world) - meth = func_for_method_checked(x[3], types, x[2]) + for x in meths + meth = func_for_method_checked(x[3], tt, x[2]) (code, ty) = Core.Compiler.typeinf_code(interp, meth, x[1], x[2], optimize) code === nothing && error("inference not successful") # inference disabled? debuginfo === :none && remove_linenums!(code) @@ -1135,6 +1160,15 @@ function which(@nospecialize(f), @nospecialize(t)) end t = to_tuple_type(t) tt = signature_type(f, t) + return which(tt) +end + +""" + which(types::Type{<:Tuple}) + +Returns the method that would be called by the given type signature (as a tuple type). +""" +function which(@nospecialize(tt::Type)) m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) if m === nothing error("no unique matching method found for the specified argument types") diff --git a/test/reflection.jl b/test/reflection.jl index ecdf788584bd2..8758f7ca65940 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -525,6 +525,11 @@ let @test !isdefined(mi.cache, :next) end +# code_typed_by_type +@test Base.code_typed_by_type(Tuple{Type{<:Val}})[1][2] == Val +@test Base.code_typed_by_type(Tuple{typeof(sin), Float64})[1][2] === Float64 +@test_throws ErrorException("signature does not correspond to a generic function") Base.code_typed_by_type(Tuple{Any}) + # New reflection methods in 0.6 struct ReflectionExample{T<:AbstractFloat, N} x::Tuple{T, N} @@ -820,6 +825,7 @@ f20872(::Val, ::Val) = false @test which(f20872, Tuple{Val,Val}).sig == Tuple{typeof(f20872), Val, Val} @test which(f20872, Tuple{Val,Val{N}} where N).sig == Tuple{typeof(f20872), Val, Val} @test_throws ErrorException which(f20872, Tuple{Any,Val{N}} where N) +@test which(Tuple{typeof(f20872), Val{1}, Val{2}}).sig == Tuple{typeof(f20872), Val, Val} module M29962 end # make sure checking if a binding is deprecated does not resolve it From 52c55d7934f71c5b2d9f6e6fa98cb48817def57c Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 24 Jun 2020 16:13:27 -0500 Subject: [PATCH 222/232] Check axes in Array(::AbstractArray) (fixes #36220) (#36397) --- base/abstractarray.jl | 6 ++++++ base/array.jl | 4 ++-- test/hashing.jl | 8 +++++--- test/offsetarray.jl | 1 + 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 95fb7fdcad943..1d6dc4c36a0c0 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -940,6 +940,12 @@ function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::A return B end +function copyto_axcheck!(dest, src) + @noinline checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs")) + + checkaxs(axes(dest), axes(src)) + copyto!(dest, src) +end """ copymutable(a) diff --git a/base/array.jl b/base/array.jl index c401e5e1a27eb..af1c8dd264893 100644 --- a/base/array.jl +++ b/base/array.jl @@ -561,8 +561,8 @@ promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(p if nameof(@__MODULE__) === :Base # avoid method overwrite # constructors should make copies -Array{T,N}(x::AbstractArray{S,N}) where {T,N,S} = copyto!(Array{T,N}(undef, size(x)), x) -AbstractArray{T,N}(A::AbstractArray{S,N}) where {T,N,S} = copyto!(similar(A,T), A) +Array{T,N}(x::AbstractArray{S,N}) where {T,N,S} = copyto_axcheck!(Array{T,N}(undef, size(x)), x) +AbstractArray{T,N}(A::AbstractArray{S,N}) where {T,N,S} = copyto_axcheck!(similar(A,T), A) end ## copying iterators to containers diff --git a/test/hashing.jl b/test/hashing.jl index fa9b2bc4fdde0..c2b3ed27f6a51 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -111,10 +111,12 @@ end for a in vals a isa AbstractArray || continue - if keys(a) == keys(Array(a)) - @test hash(a) == hash(Array(a)) == hash(Array{Any}(a)) + aa = copyto!(Array{eltype(a)}(undef, size(a)), a) + aaa = copyto!(Array{Any}(undef, size(a)), a) + if keys(a) == keys(aa) + @test hash(a) == hash(aa) == hash(aaa) else - @test hash(a) == hash(OffsetArray(Array(a), (first.(axes(a)).-1)...)) == hash(OffsetArray(Array{Any}(a), (first.(axes(a)).-1)...)) + @test hash(a) == hash(OffsetArray(aa, (first.(axes(a)).-1)...)) == hash(OffsetArray(aaa, (first.(axes(a)).-1)...)) end end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 7479821be8268..e284371cb12cf 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -21,6 +21,7 @@ h = OffsetArray([-1,1,-2,2,0], (-3,)) @test axes(v) == (-2:1,) @test size(v) == (4,) @test size(v, 1) == 4 +@test_throws DimensionMismatch Array(v) A0 = [1 3; 2 4] A = OffsetArray(A0, (-1,2)) # IndexLinear From b15f6adbceefd7adb6b359fa382fd4e97022e396 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Thu, 25 Jun 2020 10:45:44 +0200 Subject: [PATCH 223/232] Add news and manual entry for sincospi (#36403) --- NEWS.md | 2 ++ doc/src/base/math.md | 1 + 2 files changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index c9d73854e0f9d..e6076f2933db8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,6 +38,8 @@ New library functions * New function `Base.Threads.foreach(f, channel::Channel)` for multithreaded `Channel` consumption. ([#34543]). * `Iterators.map` is added. It provides another syntax `Iterators.map(f, iterators...)` for writing `(f(args...) for args in zip(iterators...))`, i.e. a lazy `map` ([#34352]). +* New function `sincospi` for simultaneously computing `sinpi(x)` and `cospi(x)` more + efficiently ([#35816]). New library features -------------------- diff --git a/doc/src/base/math.md b/doc/src/base/math.md index 511e2e6207fc4..658e8c6f264ed 100644 --- a/doc/src/base/math.md +++ b/doc/src/base/math.md @@ -67,6 +67,7 @@ Base.Math.cosd Base.Math.tand Base.Math.sinpi Base.Math.cospi +Base.Math.sincospi Base.sinh(::Number) Base.cosh(::Number) Base.tanh(::Number) From db94a7db3ba2e2bf13caab4bda6d9c162140e172 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 23 Jun 2020 14:37:55 -0400 Subject: [PATCH 224/232] gf: fix some issues with the move from using a tree to a hash lookup of leaf types (#36413) Addresses an off-by-one in the type lookup, ensures we look at at all entries when doing invalidation, and allows putting any Type{T} object also in the cache (I forgot these always had `simplesig`, so we needed to handle that case too to receive the benefit for them). --- src/gf.c | 47 ++++++++++++++++++++++++++++++++++++++--------- src/jltypes.c | 2 +- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/gf.c b/src/gf.c index 2e8b5c672c4b2..f26d9e89a1456 100644 --- a/src/gf.c +++ b/src/gf.c @@ -951,13 +951,34 @@ JL_DLLEXPORT int jl_isa_compileable_sig( return 1; } + +static int concretesig_equal(jl_value_t *tt, jl_value_t *simplesig) JL_NOTSAFEPOINT +{ + jl_value_t **types = jl_svec_data(((jl_datatype_t*)tt)->parameters); + jl_value_t **sigs = jl_svec_data(((jl_datatype_t*)simplesig)->parameters); + size_t i, lensig = jl_nparams(simplesig); + assert(lensig == jl_nparams(tt)); + assert(lensig > 0 && !jl_is_vararg_type(jl_tparam(simplesig, lensig - 1))); + for (i = 0; i < lensig; i++) { + jl_value_t *decl = sigs[i]; + jl_value_t *a = types[i]; + if (a != decl && decl != (jl_value_t*)jl_any_type) { + if (!(jl_is_type_type(a) && jl_typeof(jl_tparam0(a)) == decl)) + return 0; + } + } + return 1; +} + static inline jl_typemap_entry_t *lookup_leafcache(jl_array_t *leafcache JL_PROPAGATES_ROOT, jl_value_t *tt, size_t world) JL_NOTSAFEPOINT { jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_eqtable_get(leafcache, (jl_value_t*)tt, NULL); if (entry) { do { - if (entry->min_world <= world && world <= entry->max_world) - return entry; + if (entry->min_world <= world && world <= entry->max_world) { + if (entry->simplesig == (void*)jl_nothing || concretesig_equal(tt, (jl_value_t*)entry->simplesig)) + return entry; + } entry = entry->next; } while ((jl_value_t*)entry != jl_nothing); } @@ -1118,7 +1139,7 @@ static jl_method_instance_t *cache_method( jl_typemap_entry_t *newentry = jl_typemap_alloc(cachett, simplett, guardsigs, (jl_value_t*)newmeth, min_valid, max_valid); temp = (jl_value_t*)newentry; - if (mt && cachett == tt && simplett == NULL && jl_svec_len(guardsigs) == 0) { + if (mt && cachett == tt && jl_svec_len(guardsigs) == 0) { if (!jl_has_free_typevars((jl_value_t*)tt) && jl_lookup_cache_type_(tt) == NULL) { // if this type isn't normally in the cache, force it in there now // anyways so that we can depend on it as a token (especially since @@ -1729,9 +1750,13 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho jl_array_t *leafcache = mt->leafcache; size_t i, l = jl_array_len(leafcache); for (i = 1; i < l; i += 2) { - jl_value_t *l = jl_array_ptr_ref(leafcache, i); - if (l && l != jl_nothing) - invalidate_mt_cache((jl_typemap_entry_t*)l, (void*)&mt_cache_env); + jl_typemap_entry_t *oldentry = (jl_typemap_entry_t*)jl_array_ptr_ref(leafcache, i); + if (oldentry) { + while ((jl_value_t*)oldentry != jl_nothing) { + invalidate_mt_cache(oldentry, (void*)&mt_cache_env); + oldentry = oldentry->next; + } + } } // Invalidate the backedges int invalidated = 0; @@ -1828,9 +1853,13 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_array_t *leafcache = mt->leafcache; size_t i, l = jl_array_len(leafcache); for (i = 1; i < l; i += 2) { - jl_value_t *l = jl_array_ptr_ref(leafcache, i); - if (l && l != jl_nothing) - invalidate_mt_cache((jl_typemap_entry_t*)l, (void*)&mt_cache_env); + jl_value_t *entry = jl_array_ptr_ref(leafcache, i); + if (entry) { + while (entry != jl_nothing) { + invalidate_mt_cache((jl_typemap_entry_t*)entry, (void*)&mt_cache_env); + entry = (jl_value_t*)((jl_typemap_entry_t*)entry)->next; + } + } } //TODO: if it's small, might it be better to drop it all? //if (mt != jl_type_type_mt) { diff --git a/src/jltypes.c b/src/jltypes.c index 23cff09e68bb6..c7d5922a1d850 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -689,7 +689,7 @@ static ssize_t lookup_type_idx_linearvalue(jl_svec_t *cache, jl_value_t *key1, j size_t cl = jl_svec_len(cache); ssize_t i; for (i = 0; i < cl; i++) { - jl_datatype_t *tt = jl_atomic_load_relaxed(&data[i - 1]); + jl_datatype_t *tt = jl_atomic_load_relaxed(&data[i]); if (tt == NULL) return ~i; if (typekeyvalue_eq(tt, key1, key, n, 1)) From 0a70d424e7f7e794f5b8797f2dbf2d266bbafe79 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 24 Jun 2020 11:51:15 -0400 Subject: [PATCH 225/232] ml-matches: skip unnecessary work, when possible (#36413) If the environment is trivial (empty) and the type is a dispatch tuple, we know the intersection result is trivial, and can skip it. --- src/gf.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/gf.c b/src/gf.c index f26d9e89a1456..f0b91e6dd8fdb 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2913,18 +2913,23 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; JL_GC_PUSH5(&env.t, &env.matc, &env.match.env, &search.env, &env.match.ti); + // check the leaf cache if this type can be in there if (((jl_datatype_t*)unw)->isdispatchtuple) { jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)type, world); if (entry) { jl_method_instance_t *mi = entry->func.linfo; jl_method_t *meth = mi->def.method; - if (jl_egal((jl_value_t*)type, mi->specTypes)) { + if (!jl_is_unionall(meth->sig)) { + env.match.env = jl_emptysvec; + env.match.ti = unw; + } + else if (jl_egal((jl_value_t*)type, mi->specTypes)) { env.match.env = mi->sparam_vals; env.match.ti = mi->specTypes; } else { - // TODO: should we use jl_subtype_env instead (since we know that `type <: meth->sig` by transitivity) + // this just calls jl_subtype_env (since we know that `type <: meth->sig` by transitivity) env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); } env.matc = jl_svec(3, env.match.ti, env.match.env, meth); @@ -2938,13 +2943,20 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, return env.t; } } + // then check the full cache if it seems profitable if (((jl_datatype_t*)unw)->isdispatchtuple) { jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, &search, jl_cachearg_offset(mt), /*subtype*/1); if (entry && (((jl_datatype_t*)unw)->isdispatchtuple || entry->guardsigs == jl_emptysvec)) { jl_method_instance_t *mi = entry->func.linfo; jl_method_t *meth = mi->def.method; - // TODO: should we use jl_subtype_env instead (since we know that `type <: meth->sig` by transitivity) - env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); + if (!jl_is_unionall(meth->sig) && ((jl_datatype_t*)unw)->isdispatchtuple) { + env.match.env = jl_emptysvec; + env.match.ti = unw; + } + else { + // this just calls jl_subtype_env (since we know that `type <: meth->sig` by transitivity) + env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); + } env.matc = jl_svec(3, env.match.ti, env.match.env, meth); env.t = (jl_value_t*)jl_alloc_vec_any(1); jl_array_ptr_set(env.t, 0, env.matc); From 32b1b1495f5d87b0ce76b7e14463d050d8d43d59 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 25 Jun 2020 13:06:27 -0400 Subject: [PATCH 226/232] fix and de-dup cached calls to `methods_by_ftype` in compiler (#36404) --- base/compiler/abstractinterpretation.jl | 41 +++++++++++++++---------- base/compiler/ssair/inlining.jl | 27 ++++++---------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 861758576aedd..b766c043dc514 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -16,8 +16,25 @@ const _REF_NAME = Ref.body.name call_result_unused(frame::InferenceState, pc::LineNum=frame.currpc) = isexpr(frame.src.code[frame.currpc], :call) && isempty(frame.ssavalue_uses[pc]) +function matching_methods(@nospecialize(atype), cache::IdDict{Any, Tuple{Any, UInt, UInt}}, max_methods::Int, world::UInt) + box = Core.Box(atype) + return get!(cache, atype) do + _min_val = UInt[typemin(UInt)] + _max_val = UInt[typemax(UInt)] + ms = _methods_by_ftype(box.contents, max_methods, world, _min_val, _max_val) + return ms, _min_val[1], _max_val[1] + end +end + +function matching_methods(@nospecialize(atype), cache::IdDict{Any, Tuple{Any, UInt, UInt}}, max_methods::Int, world::UInt, min_valid::Vector{UInt}, max_valid::Vector{UInt}) + ms, minvalid, maxvalid = matching_methods(atype, cache, max_methods, world) + min_valid[1] = max(min_valid[1], minvalid) + max_valid[1] = min(max_valid[1], maxvalid) + return ms +end + function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState, - max_methods = InferenceParams(interp).MAX_METHODS) + max_methods::Int = InferenceParams(interp).MAX_METHODS) atype_params = unwrap_unionall(atype).parameters ft = unwrap_unionall(atype_params[1]) # TODO: ccall jl_method_table_for here isa(ft, DataType) || return Any # the function being called is unknown. can't properly handle this backedge right now @@ -42,22 +59,14 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), splitsigs = switchtupleunion(atype) applicable = Any[] for sig_n in splitsigs - (xapplicable, min_valid[1], max_valid[1]) = - get!(sv.matching_methods_cache, sig_n) do - ms = _methods_by_ftype(sig_n, max_methods, - get_world_counter(interp), min_valid, max_valid) - return (ms, min_valid[1], max_valid[1]) - end + xapplicable = matching_methods(sig_n, sv.matching_methods_cache, max_methods, + get_world_counter(interp), min_valid, max_valid) xapplicable === false && return Any append!(applicable, xapplicable) end else - (applicable, min_valid[1], max_valid[1]) = - get!(sv.matching_methods_cache, atype) do - ms = _methods_by_ftype(atype, max_methods, - get_world_counter(interp), min_valid, max_valid) - return (ms, min_valid[1], max_valid[1]) - end + applicable = matching_methods(atype, sv.matching_methods_cache, max_methods, + get_world_counter(interp), min_valid, max_valid) if applicable === false # this means too many methods matched # (assume this will always be true, so we don't compute / update valid age in this case) @@ -595,7 +604,7 @@ end # do apply(af, fargs...), where af is a function value function abstract_apply(interp::AbstractInterpreter, @nospecialize(itft), @nospecialize(aft), aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, - max_methods = InferenceParams(interp).MAX_METHODS) + max_methods::Int = InferenceParams(interp).MAX_METHODS) aftw = widenconst(aft) if !isa(aft, Const) && (!isType(aftw) || has_free_typevars(aftw)) if !isconcretetype(aftw) || (aftw <: Builtin) @@ -694,7 +703,7 @@ function argtype_tail(argtypes::Vector{Any}, i::Int) end # call where the function is known exactly -function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods = InferenceParams(interp).MAX_METHODS) +function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods::Int = InferenceParams(interp).MAX_METHODS) la = length(argtypes) if isa(f, Builtin) @@ -911,7 +920,7 @@ end # call where the function is any lattice element function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, - vtypes::VarTable, sv::InferenceState, max_methods = InferenceParams(interp).MAX_METHODS) + vtypes::VarTable, sv::InferenceState, max_methods::Int = InferenceParams(interp).MAX_METHODS) #print("call ", e.args[1], argtypes, "\n\n") ft = argtypes[1] if isa(ft, Const) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index d5986c34bee1f..491d228aae9ff 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1041,18 +1041,13 @@ function assemble_inline_todo!(ir::IRCode, sv::OptimizationState) local fully_covered = true for atype in splits # Regular case: Retrieve matching methods from cache (or compute them) - (meth, min_valid, max_valid) = get(sv.matching_methods_cache, atype) do - # World age does not need to be taken into account in the cache - # because it is forwarded from type inference through `sv.params` - # in the case that the cache is nonempty, so it should be unchanged - # The max number of methods should be the same as in inference most - # of the time, and should not affect correctness otherwise. - min_val = UInt[typemin(UInt)] - max_val = UInt[typemax(UInt)] - ms = _methods_by_ftype(atype, sv.params.MAX_METHODS, - sv.world, min_val, max_val) - return (ms, min_val[1], max_val[1]) - end + # World age does not need to be taken into account in the cache + # because it is forwarded from type inference through `sv.params` + # in the case that the cache is nonempty, so it should be unchanged + # The max number of methods should be the same as in inference most + # of the time, and should not affect correctness otherwise. + (meth, min_valid, max_valid) = + matching_methods(atype, sv.matching_methods_cache, sv.params.MAX_METHODS, sv.world) if meth === false # Too many applicable methods too_many = true @@ -1100,12 +1095,10 @@ function assemble_inline_todo!(ir::IRCode, sv::OptimizationState) if signature_fully_covered && length(cases) == 0 && only_method isa Method if length(splits) > 1 # get match information for a single overall match instead of union splits - meth = get(sv.matching_methods_cache, sig.atype) do - ms = _methods_by_ftype(sig.atype, sv.params.MAX_METHODS, - sv.world, UInt[typemin(UInt)], UInt[typemin(UInt)]) - return ms - end + (meth, min_valid, max_valid) = + matching_methods(sig.atype, sv.matching_methods_cache, sv.params.MAX_METHODS, sv.world) @assert length(meth) == 1 + update_valid_age!(min_valid, max_valid, sv) end (metharg, methsp, method) = (meth[1][1]::Type, meth[1][2]::SimpleVector, meth[1][3]::Method) fully_covered = true From 0a661f914d1718522110a17a3ef24e28e596c982 Mon Sep 17 00:00:00 2001 From: kimikage Date: Fri, 26 Jun 2020 02:08:40 +0900 Subject: [PATCH 227/232] Fix broken links in docstring of `repeat` (#36376) This changes the link targets of `^` parseable and more specific. --- base/strings/basic.jl | 2 +- base/strings/string.jl | 3 ++- doc/src/base/strings.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index ccbb2ef16cfbc..4d60bc6aa0a7b 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -686,7 +686,7 @@ reverseind(s::AbstractString, i::Integer) = thisind(s, ncodeunits(s)-i+1) Repeat a string `r` times. This can be written as `s^r`. -See also: [`^`](@ref) +See also: [`^`](@ref :^(::Union{AbstractString, AbstractChar}, ::Integer)) # Examples ```jldoctest diff --git a/base/strings/string.jl b/base/strings/string.jl index c267b263c66c9..6825ac29b0930 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -321,7 +321,8 @@ end """ repeat(c::AbstractChar, r::Integer) -> String -Repeat a character `r` times. This can equivalently be accomplished by calling [`c^r`](@ref ^). +Repeat a character `r` times. This can equivalently be accomplished by calling +[`c^r`](@ref :^(::Union{AbstractString, AbstractChar}, ::Integer)). # Examples ```jldoctest diff --git a/doc/src/base/strings.md b/doc/src/base/strings.md index 22943329af501..3fe9c59282850 100644 --- a/doc/src/base/strings.md +++ b/doc/src/base/strings.md @@ -7,7 +7,7 @@ Base.codepoint Base.length(::AbstractString) Base.sizeof(::AbstractString) Base.:*(::Union{AbstractChar, AbstractString}, ::Union{AbstractChar, AbstractString}...) -Base.:^(::AbstractString, ::Integer) +Base.:^(::Union{AbstractString, AbstractChar}, ::Integer) Base.string Base.repeat(::AbstractString, ::Integer) Base.repeat(::AbstractChar, ::Integer) From a850b7efd3734d5d1f2aa4bbbe42601d7c375e96 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 25 Jun 2020 19:14:53 +0200 Subject: [PATCH 228/232] add a test for #30739 (#36395) --- test/compiler/codegen.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 254548bf6126c..f90ed2a96d815 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -444,3 +444,10 @@ function _handle_message_test() return _handle_progress_test(progress) end @test _handle_message_test() isa Tuple{Base.UUID, String} + +@testset "#30739" begin + ifelsetuple(n::Integer, k::Integer, f, g) = ntuple(i -> (i <= k ? f : g), n) + f(x) = x^2; g(x) = x^3; + a = [1]; b = [2] + @test ifelsetuple(5, 3, a, b) == ([1], [1], [1], [2], [2]) +end From 3bbb5827120be1955f5cc272e7dbbba42148b817 Mon Sep 17 00:00:00 2001 From: kpamnany Date: Thu, 25 Jun 2020 13:16:37 -0400 Subject: [PATCH 229/232] Make compilecache atomic (#36416) When several Julia processes compile the same package concurrently (e.g. during a cluster run), they can conflict on the compile cache file. This change makes a Julia process create a compile cache in a temporary file and atomically rename it to the final cache file. Co-authored-by: Takafumi Arakaki --- NEWS.md | 2 ++ base/loading.jl | 34 ++++++++++++++++++++++++---------- doc/src/manual/faq.md | 16 ++++++++++++++++ src/dump.c | 10 ++-------- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/NEWS.md b/NEWS.md index e6076f2933db8..2c77e9e470921 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,8 @@ Compiler/Runtime improvements This allows executable-relative paths to be embedded within executables on all platforms, not just MacOS, which the syntax is borrowed from. ([#35627]) * Constant propogation now occurs through keyword arguments ([#35976]) +* The precompilation cache is now created atomically ([#36416]). Invoking _n_ + Julia processes simultaneously may create _n_ temporary caches. Command-line option changes --------------------------- diff --git a/base/loading.jl b/base/loading.jl index 523cc1d077442..1ca0b0b800f07 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1301,9 +1301,9 @@ const MAX_NUM_PRECOMPILE_FILES = 10 function compilecache(pkg::PkgId, path::String) # decide where to put the resulting cache file cachefile = compilecache_path(pkg) + cachepath = dirname(cachefile) # prune the directory with cache files if pkg.uuid !== nothing - cachepath = dirname(cachefile) entrypath, entryfile = cache_file_entry(pkg) cachefiles = filter!(x -> startswith(x, entryfile * "_"), readdir(cachepath)) if length(cachefiles) >= MAX_NUM_PRECOMPILE_FILES @@ -1321,20 +1321,34 @@ function compilecache(pkg::PkgId, path::String) # run the expression and cache the result verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug @logmsg verbosity "Precompiling $pkg" - p = create_expr_cache(path, cachefile, concrete_deps, pkg.uuid) - if success(p) - # append checksum to the end of the .ji file: - open(cachefile, "a+") do f - write(f, _crc32c(seekstart(f))) + + # create a temporary file in `cachepath` directory, write the cache in it, + # write the checksum, _and then_ atomically move the file to `cachefile`. + tmppath, tmpio = mktemp(cachepath) + local p + try + close(tmpio) + p = create_expr_cache(path, tmppath, concrete_deps, pkg.uuid) + if success(p) + # append checksum to the end of the .ji file: + open(tmppath, "a+") do f + write(f, _crc32c(seekstart(f))) + end + # inherit permission from the source file + chmod(tmppath, filemode(path) & 0o777) + + # this is atomic according to POSIX: + rename(tmppath, cachefile) + return cachefile end - # inherit permission from the source file - chmod(cachefile, filemode(path) & 0o777) - elseif p.exitcode == 125 + finally + rm(tmppath, force=true) + end + if p.exitcode == 125 return PrecompilableError() else error("Failed to precompile $pkg to $cachefile.") end - return cachefile end module_build_id(m::Module) = ccall(:jl_module_build_id, UInt64, (Any,), m) diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index c3ef33c3d098e..ff55617c157ef 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -935,6 +935,22 @@ Julia compiles and uses its own copy of OpenBLAS, with threads currently capped Modifying OpenBLAS settings or compiling Julia with a different BLAS library, eg [Intel MKL](https://software.intel.com/en-us/mkl), may provide performance improvements. You can use [MKL.jl](https://github.com/JuliaComputing/MKL.jl), a package that makes Julia's linear algebra use Intel MKL BLAS and LAPACK instead of OpenBLAS, or search the discussion forum for suggestions on how to set this up manually. Note that Intel MKL cannot be bundled with Julia, as it is not open source. +## Computing cluster + +### How do I manage precompilation caches in distributed file systems? + +When using `julia` in high-performance computing (HPC) facilities, invoking +_n_ `julia` processes simultaneously creates at most _n_ temporary copies of +precompilation cache files. If this is an issue (slow and/or small distributed +file system), you may: + +1. Use `julia` with `--compiled-modules=no` flag to turn off precompilation. +2. Configure a private writable depot using `pushfirst!(DEPOT_PATH, private_path)` + where `private_path` is a path unique to this `julia` process. This + can also be done by setting environment variable `JULIA_DEPOT_PATH` to + `$private_path:$HOME/.julia`. +3. Create a symlink from `~/.julia/compiled` to a directory in a scratch space. + ## Julia Releases ### Do I want to use the Stable, LTS, or nightly version of Julia? diff --git a/src/dump.c b/src/dump.c index 06d7042f86072..5b8c73965e1fd 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2096,11 +2096,10 @@ JL_DLLEXPORT void jl_init_restored_modules(jl_array_t *init_order) JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist) { JL_TIMING(SAVE_MODULE); - char *tmpfname = strcat(strcpy((char *) alloca(strlen(fname)+8), fname), ".XXXXXX"); ios_t f; jl_array_t *mod_array = NULL, *udeps = NULL; - if (ios_mkstemp(&f, tmpfname) == NULL) { - jl_printf(JL_STDERR, "Cannot open cache file \"%s\" for writing.\n", tmpfname); + if (ios_file(&f, fname, 1, 1, 1, 1) == NULL) { + jl_printf(JL_STDERR, "Cannot open cache file \"%s\" for writing.\n", fname); return 1; } JL_GC_PUSH2(&mod_array, &udeps); @@ -2213,12 +2212,7 @@ JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist) } write_int32(&f, 0); // mark the end of the source text ios_close(&f); - JL_GC_POP(); - if (jl_fs_rename(tmpfname, fname) < 0) { - jl_printf(JL_STDERR, "Cannot write cache file \"%s\".\n", fname); - return 1; - } return 0; } From 5bb10535d073f909d95d778484d405bc06de621e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 25 Jun 2020 12:18:09 -0500 Subject: [PATCH 230/232] Ensure string-hashing is defined before it gets used (#36411) An analysis of invalidations during bootstrap revealed that `log_record_id` calls `hash` on strings, and in principle it is used before the specialized methods are defined. Since the new methods change the value of the hash is expected to change, this could cause subtle bugs. It seems safer to define the hash methods at around the first time they could conceivably be defined. --- base/hashing.jl | 10 ++++++++++ base/hashing2.jl | 11 ----------- base/strings/basic.jl | 4 ++++ base/strings/substring.jl | 5 +++++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/base/hashing.jl b/base/hashing.jl index 7b962a58ad5ad..f40ccb50f0f5b 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -75,3 +75,13 @@ else hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x96d26dc6)) hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x469d72af) end + +## hashing strings ## + +const memhash = UInt === UInt64 ? :memhash_seed : :memhash32_seed +const memhash_seed = UInt === UInt64 ? 0x71e729fd56419c81 : 0x56419c81 + +function hash(s::String, h::UInt) + h += memhash_seed + ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h +end diff --git a/base/hashing2.jl b/base/hashing2.jl index ec670759ca6bd..f7ea3838aa096 100644 --- a/base/hashing2.jl +++ b/base/hashing2.jl @@ -230,14 +230,3 @@ end ## hashing Float16s ## hash(x::Float16, h::UInt) = hash(Float64(x), h) - -## hashing strings ## - -const memhash = UInt === UInt64 ? :memhash_seed : :memhash32_seed -const memhash_seed = UInt === UInt64 ? 0x71e729fd56419c81 : 0x56419c81 - -function hash(s::Union{String,SubString{String}}, h::UInt) - h += memhash_seed - ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h -end -hash(s::AbstractString, h::UInt) = hash(String(s), h) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 4d60bc6aa0a7b..2d9ff43dfcf19 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -347,6 +347,10 @@ cmp(a::Symbol, b::Symbol) = Int(sign(ccall(:strcmp, Int32, (Cstring, Cstring), a isless(a::Symbol, b::Symbol) = cmp(a, b) < 0 +# hashing + +hash(s::AbstractString, h::UInt) = hash(String(s), h) + ## character index arithmetic ## """ diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 73288ea363046..abb22b412f993 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -122,6 +122,11 @@ end pointer(x::SubString{String}) = pointer(x.string) + x.offset pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1) +function hash(s::SubString{String}, h::UInt) + h += memhash_seed + ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h +end + """ reverse(s::AbstractString) -> AbstractString From 59b8dde7c17f3403febe517b5716398197e088e5 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Fri, 26 Jun 2020 04:21:35 -0500 Subject: [PATCH 231/232] Fix pointer to no longer assume contiguity (#36405) * Fix pointer to no longer assume contiguity --- base/abstractarray.jl | 23 +++-- base/permuteddimsarray.jl | 1 + base/subarray.jl | 17 +--- stdlib/LinearAlgebra/src/adjtrans.jl | 3 + test/abstractarray.jl | 141 +++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 22 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 1d6dc4c36a0c0..1fdf83cbc504c 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1007,7 +1007,14 @@ end pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x) function pointer(x::AbstractArray{T}, i::Integer) where T @_inline_meta - unsafe_convert(Ptr{T}, x) + (i - first(LinearIndices(x)))*elsize(x) + unsafe_convert(Ptr{T}, x) + _memory_offset(x, i) +end + +# The distance from pointer(x) to the element at x[I...] in bytes +_memory_offset(x::DenseArray, I...) = (_to_linear_index(x, I...) - first(LinearIndices(x)))*elsize(x) +function _memory_offset(x::AbstractArray, I...) + J = _to_subscript_indices(x, I...) + return sum(map((i, s, o)->s*(i-o), J, strides(x), Tuple(first(CartesianIndices(x)))))*elsize(x) end ## Approach: @@ -1078,10 +1085,10 @@ function _getindex(::IndexLinear, A::AbstractArray, I::Vararg{Int,M}) where M @inbounds r = getindex(A, _to_linear_index(A, I...)) r end -_to_linear_index(A::AbstractArray, i::Int) = i -_to_linear_index(A::AbstractVector, i::Int, I::Int...) = i +_to_linear_index(A::AbstractArray, i::Integer) = i +_to_linear_index(A::AbstractVector, i::Integer, I::Integer...) = i _to_linear_index(A::AbstractArray) = 1 -_to_linear_index(A::AbstractArray, I::Int...) = (@_inline_meta; _sub2ind(A, I...)) +_to_linear_index(A::AbstractArray, I::Integer...) = (@_inline_meta; _sub2ind(A, I...)) ## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints function _getindex(::IndexCartesian, A::AbstractArray, I::Vararg{Int,M}) where M @@ -1094,12 +1101,12 @@ function _getindex(::IndexCartesian, A::AbstractArray{T,N}, I::Vararg{Int, N}) w @_propagate_inbounds_meta getindex(A, I...) end -_to_subscript_indices(A::AbstractArray, i::Int) = (@_inline_meta; _unsafe_ind2sub(A, i)) +_to_subscript_indices(A::AbstractArray, i::Integer) = (@_inline_meta; _unsafe_ind2sub(A, i)) _to_subscript_indices(A::AbstractArray{T,N}) where {T,N} = (@_inline_meta; fill_to_length((), 1, Val(N))) _to_subscript_indices(A::AbstractArray{T,0}) where {T} = () -_to_subscript_indices(A::AbstractArray{T,0}, i::Int) where {T} = () -_to_subscript_indices(A::AbstractArray{T,0}, I::Int...) where {T} = () -function _to_subscript_indices(A::AbstractArray{T,N}, I::Int...) where {T,N} +_to_subscript_indices(A::AbstractArray{T,0}, i::Integer) where {T} = () +_to_subscript_indices(A::AbstractArray{T,0}, I::Integer...) where {T} = () +function _to_subscript_indices(A::AbstractArray{T,N}, I::Integer...) where {T,N} @_inline_meta J, Jrem = IteratorsMD.split(I, Val(N)) _to_subscript_indices(A, J, Jrem) diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl index 348d4ad449a53..7ac87df0ad687 100644 --- a/base/permuteddimsarray.jl +++ b/base/permuteddimsarray.jl @@ -64,6 +64,7 @@ function Base.strides(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} s = strides(parent(A)) ntuple(d->s[perm[d]], Val(N)) end +Base.elsize(::Type{<:PermutedDimsArray{<:Any, <:Any, <:Any, <:Any, P}}) where {P} = Base.elsize(P) @inline function Base.getindex(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) where {T,N,perm,iperm} @boundscheck checkbounds(A, I...) diff --git a/base/subarray.jl b/base/subarray.jl index ba891183924bb..a4cd5920157ad 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -398,23 +398,12 @@ find_extended_inds(::ScalarIndex, I...) = (@_inline_meta; find_extended_inds(I.. find_extended_inds(i1, I...) = (@_inline_meta; (i1, find_extended_inds(I...)...)) find_extended_inds() = () -unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} = - unsafe_convert(Ptr{T}, V.parent) + (first_index(V)-1)*sizeof(T) +function unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} + return unsafe_convert(Ptr{T}, V.parent) + _memory_offset(V.parent, map(first, V.indices)...) +end pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) -pointer(V::SubArray, i::Int) = _pointer(V, i) -_pointer(V::SubArray{<:Any,1}, i::Int) = pointer(V, (i,)) -_pointer(V::SubArray, i::Int) = pointer(V, Base._ind2sub(axes(V), i)) - -function pointer(V::SubArray{T,N,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::Tuple{Vararg{Int}}) where {T,N} - index = first_index(V) - strds = strides(V) - for d = 1:length(is) - index += (is[d]-1)*strds[d] - end - return pointer(V.parent, index) -end # indices are taken from the range/vector # Since bounds-checking is performance-critical and uses diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index e3952d0261836..458beea92604e 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -208,6 +208,9 @@ Base.strides(A::Transpose{<:Any, <:StridedMatrix}) = reverse(strides(A.parent)) Base.unsafe_convert(::Type{Ptr{T}}, A::Adjoint{<:Real, <:StridedVecOrMat}) where {T} = Base.unsafe_convert(Ptr{T}, A.parent) Base.unsafe_convert(::Type{Ptr{T}}, A::Transpose{<:Any, <:StridedVecOrMat}) where {T} = Base.unsafe_convert(Ptr{T}, A.parent) +Base.elsize(::Type{<:Adjoint{<:Real, P}}) where {P<:StridedVecOrMat} = Base.elsize(P) +Base.elsize(::Type{<:Transpose{<:Any, P}}) where {P<:StridedVecOrMat} = Base.elsize(P) + # for vectors, the semantics of the wrapped and unwrapped types differ # so attempt to maintain both the parent and wrapper type insofar as possible similar(A::AdjOrTransAbsVec) = wrapperop(A)(similar(A.parent)) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index a35667e28ba72..0089aac4bc62f 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -978,3 +978,144 @@ end @test Core.sizeof(arrayOfUInt48) == 24 end end + +struct Strider{T,N} <: AbstractArray{T,N} + data::Vector{T} + offset::Int + strides::NTuple{N,Int} + size::NTuple{N,Int} +end +function Strider{T}(strides::NTuple{N}, size::NTuple{N}) where {T,N} + offset = 1-sum(strides .* (strides .< 0) .* (size .- 1)) + data = Array{T}(undef, sum(abs.(strides) .* (size .- 1)) + 1) + return Strider{T, N, Vector{T}}(data, offset, strides, size) +end +function Strider(vec::AbstractArray{T}, strides::NTuple{N}, size::NTuple{N}) where {T,N} + offset = 1-sum(strides .* (strides .< 0) .* (size .- 1)) + @assert length(vec) >= sum(abs.(strides) .* (size .- 1)) + 1 + return Strider{T, N}(vec, offset, strides, size) +end +Base.size(S::Strider) = S.size +function Base.getindex(S::Strider{<:Any,N}, I::Vararg{Int,N}) where {N} + return S.data[sum(S.strides .* (I .- 1)) + S.offset] +end +Base.strides(S::Strider) = S.strides +Base.elsize(::Type{<:Strider{T}}) where {T} = Base.elsize(Vector{T}) +Base.unsafe_convert(::Type{Ptr{T}}, S::Strider{T}) where {T} = pointer(S.data, S.offset) + +@testset "Simple 3d strided views and permutes" for sz in ((5, 3, 2), (7, 11, 13)) + A = collect(reshape(1:prod(sz), sz)) + S = Strider(vec(A), strides(A), sz) + @test pointer(A) == pointer(S) + for i in 1:prod(sz) + @test pointer(A, i) == pointer(S, i) + @test A[i] == S[i] + end + for idxs in ((1:sz[1], 1:sz[2], 1:sz[3]), + (1:sz[1], 2:2:sz[2], sz[3]:-1:1), + (2:2:sz[1]-1, sz[2]:-1:1, sz[3]:-2:2), + (sz[1]:-1:1, sz[2]:-1:1, sz[3]:-1:1), + (sz[1]-1:-3:1, sz[2]:-2:3, 1:sz[3]),) + Ai = A[idxs...] + Av = view(A, idxs...) + Sv = view(S, idxs...) + Ss = Strider{Int, 3}(vec(A), sum((first.(idxs).-1).*strides(A))+1, strides(Av), length.(idxs)) + @test pointer(Av) == pointer(Sv) == pointer(Ss) + for i in 1:length(Av) + @test pointer(Av, i) == pointer(Sv, i) == pointer(Ss, i) + @test Ai[i] == Av[i] == Sv[i] == Ss[i] + end + for perm in ((3, 2, 1), (2, 1, 3), (3, 1, 2)) + P = permutedims(A, perm) + Ap = Base.PermutedDimsArray(A, perm) + Sp = Base.PermutedDimsArray(S, perm) + Ps = Strider{Int, 3}(vec(A), 1, strides(A)[collect(perm)], sz[collect(perm)]) + @test pointer(Ap) == pointer(Sp) == pointer(Ps) + for i in 1:length(Ap) + # This is intentionally disabled due to ambiguity + @test_broken pointer(Ap, i) == pointer(Sp, i) == pointer(Ps, i) + @test P[i] == Ap[i] == Sp[i] == Ps[i] + end + Pv = view(P, idxs[collect(perm)]...) + Pi = P[idxs[collect(perm)]...] + Apv = view(Ap, idxs[collect(perm)]...) + Spv = view(Sp, idxs[collect(perm)]...) + Pvs = Strider{Int, 3}(vec(A), sum((first.(idxs).-1).*strides(A))+1, strides(Apv), size(Apv)) + @test pointer(Apv) == pointer(Spv) == pointer(Pvs) + for i in 1:length(Apv) + @test pointer(Apv, i) == pointer(Spv, i) == pointer(Pvs, i) + @test Pi[i] == Pv[i] == Apv[i] == Spv[i] == Pvs[i] + end + Vp = permutedims(Av, perm) + Ip = permutedims(Ai, perm) + Avp = Base.PermutedDimsArray(Av, perm) + Svp = Base.PermutedDimsArray(Sv, perm) + @test pointer(Avp) == pointer(Svp) + for i in 1:length(Avp) + # This is intentionally disabled due to ambiguity + @test_broken pointer(Avp, i) == pointer(Svp, i) + @test Ip[i] == Vp[i] == Avp[i] == Svp[i] + end + end + end +end + +@testset "simple 2d strided views, permutes, transposes" for sz in ((5, 3), (7, 11)) + A = collect(reshape(1:prod(sz), sz)) + S = Strider(vec(A), strides(A), sz) + @test pointer(A) == pointer(S) + for i in 1:prod(sz) + @test pointer(A, i) == pointer(S, i) + @test A[i] == S[i] + end + for idxs in ((1:sz[1], 1:sz[2]), + (1:sz[1], 2:2:sz[2]), + (2:2:sz[1]-1, sz[2]:-1:1), + (sz[1]:-1:1, sz[2]:-1:1), + (sz[1]-1:-3:1, sz[2]:-2:3),) + Av = view(A, idxs...) + Sv = view(S, idxs...) + Ss = Strider{Int, 2}(vec(A), sum((first.(idxs).-1).*strides(A))+1, strides(Av), length.(idxs)) + @test pointer(Av) == pointer(Sv) == pointer(Ss) + for i in 1:length(Av) + @test pointer(Av, i) == pointer(Sv, i) == pointer(Ss, i) + @test Av[i] == Sv[i] == Ss[i] + end + perm = (2, 1) + P = permutedims(A, perm) + Ap = Base.PermutedDimsArray(A, perm) + At = transpose(A) + Aa = adjoint(A) + Sp = Base.PermutedDimsArray(S, perm) + Ps = Strider{Int, 2}(vec(A), 1, strides(A)[collect(perm)], sz[collect(perm)]) + @test pointer(Ap) == pointer(Sp) == pointer(Ps) == pointer(At) == pointer(Aa) + for i in 1:length(Ap) + # This is intentionally disabled due to ambiguity + @test_broken pointer(Ap, i) == pointer(Sp, i) == pointer(Ps, i) == pointer(At, i) == pointer(Aa, i) + @test pointer(Ps, i) == pointer(At, i) == pointer(Aa, i) + @test P[i] == Ap[i] == Sp[i] == Ps[i] == At[i] == Aa[i] + end + Pv = view(P, idxs[collect(perm)]...) + Apv = view(Ap, idxs[collect(perm)]...) + Atv = view(At, idxs[collect(perm)]...) + Ata = view(Aa, idxs[collect(perm)]...) + Spv = view(Sp, idxs[collect(perm)]...) + Pvs = Strider{Int, 2}(vec(A), sum((first.(idxs).-1).*strides(A))+1, strides(Apv), size(Apv)) + @test pointer(Apv) == pointer(Spv) == pointer(Pvs) == pointer(Atv) == pointer(Ata) + for i in 1:length(Apv) + @test pointer(Apv, i) == pointer(Spv, i) == pointer(Pvs, i) == pointer(Atv, i) == pointer(Ata, i) + @test Pv[i] == Apv[i] == Spv[i] == Pvs[i] == Atv[i] == Ata[i] + end + Vp = permutedims(Av, perm) + Avp = Base.PermutedDimsArray(Av, perm) + Avt = transpose(Av) + Ava = adjoint(Av) + Svp = Base.PermutedDimsArray(Sv, perm) + @test pointer(Avp) == pointer(Svp) == pointer(Avt) == pointer(Ava) + for i in 1:length(Avp) + # This is intentionally disabled due to ambiguity + @test_broken pointer(Avp, i) == pointer(Svp, i) == pointer(Avt, i) == pointer(Ava, i) + @test Vp[i] == Avp[i] == Svp[i] == Avt[i] == Ava[i] + end + end +end From 063525f0d6b15caff9945879f10f0def22d9051a Mon Sep 17 00:00:00 2001 From: jmert <2965436+jmert@users.noreply.github.com> Date: Fri, 26 Jun 2020 04:24:36 -0500 Subject: [PATCH 232/232] Add passthrough for non-Markdown docs (#36091) --- stdlib/REPL/src/docview.jl | 2 ++ stdlib/REPL/test/repl.jl | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index c7219a4b9a6f7..6bf940194d445 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -108,6 +108,8 @@ function Markdown.term(io::IO, msg::Message, columns) printstyled(io, msg.msg; msg.fmt...) end +trimdocs(doc, brief::Bool) = doc + function trimdocs(md::Markdown.MD, brief::Bool) brief || return md md, trimmed = _trimdocs(md, brief) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index d785a8f9c2428..849a897e37687 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1093,13 +1093,31 @@ Short docs Long docs """ f() = nothing +@doc text""" + f_plain() + +Plain text docs +""" +f_plain() = nothing +@doc html""" +

f_html()

+

HTML docs.

+""" +f_html() = nothing end # module BriefExtended + buf = IOBuffer() md = Base.eval(REPL._helpmode(buf, "$(@__MODULE__).BriefExtended.f")) @test length(md.content) == 2 && isa(md.content[2], REPL.Message) buf = IOBuffer() md = Base.eval(REPL._helpmode(buf, "?$(@__MODULE__).BriefExtended.f")) @test length(md.content) == 1 && length(md.content[1].content[1].content) == 4 +buf = IOBuffer() +txt = Base.eval(REPL._helpmode(buf, "$(@__MODULE__).BriefExtended.f_plain")) +@test !isempty(sprint(show, txt)) +buf = IOBuffer() +html = Base.eval(REPL._helpmode(buf, "$(@__MODULE__).BriefExtended.f_html")) +@test !isempty(sprint(show, html)) # PR #27562 fake_repl() do stdin_write, stdout_read, repl