Skip to content

Commit

Permalink
Merge branch 'master' into rf/complex128
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski authored Dec 12, 2017
2 parents 06f22ba + 66b2090 commit 40081a0
Show file tree
Hide file tree
Showing 51 changed files with 3,238 additions and 1,525 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,8 @@ Deprecated or removed
* The aliases `Complex32`, `Complex64` and `Complex128` have been deprecated in favor of `ComplexF16`,
`ComplexF32` and `ComplexF64` respectively (#24647).

* The `sum_kbn` and `cumsum_kbn` functions have been moved to the
[KahanSummation](https://github.com/JuliaMath/KahanSummation.jl) package ([#24869]).

Command-line option changes
---------------------------
Expand Down Expand Up @@ -1711,3 +1713,4 @@ Command-line option changes
[#24396]: https://github.com/JuliaLang/julia/issues/24396
[#24413]: https://github.com/JuliaLang/julia/issues/24413
[#24653]: https://github.com/JuliaLang/julia/issues/24653
[#24869]: https://github.com/JuliaLang/julia/issues/24869
67 changes: 0 additions & 67 deletions base/abstractarraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -238,73 +238,6 @@ function circshift(a::AbstractArray, shiftamt)
circshift!(similar(a), a, map(Integer, (shiftamt...,)))
end

# Uses K-B-N summation
function cumsum_kbn(v::AbstractVector{T}) where T<:AbstractFloat
r = similar(v)
if isempty(v); return r; end

inds = indices(v, 1)
i1 = first(inds)
s = r[i1] = v[i1]
c = zero(T)
for i=i1+1:last(inds)
vi = v[i]
t = s + vi
if abs(s) >= abs(vi)
c += ((s-t) + vi)
else
c += ((vi-t) + s)
end
s = t
r[i] = s+c
end
return r
end

# Uses K-B-N summation
# TODO: Needs a separate IndexCartesian method, this is only fast for IndexLinear

"""
cumsum_kbn(A, dim::Integer)
Cumulative sum along a dimension, using the Kahan-Babuska-Neumaier compensated summation
algorithm for additional accuracy.
"""
function cumsum_kbn(A::AbstractArray{T}, axis::Integer) where T<:AbstractFloat
dimsA = size(A)
ndimsA = ndims(A)
axis_size = dimsA[axis]
axis_stride = 1
for i = 1:(axis-1)
axis_stride *= size(A,i)
end

if axis_size <= 1
return A
end

B = similar(A)
C = similar(A)

for i = 1:length(A)
if div(i-1, axis_stride) % axis_size == 0
B[i] = A[i]
C[i] = zero(T)
else
s = B[i-axis_stride]
Ai = A[i]
B[i] = t = s + Ai
if abs(s) >= abs(Ai)
C[i] = C[i-axis_stride] + ((s-t) + Ai)
else
C[i] = C[i-axis_stride] + ((Ai-t) + s)
end
end
end

return B + C
end

## Other array functions ##

"""
Expand Down
130 changes: 98 additions & 32 deletions base/codevalidation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ const VALID_EXPR_HEADS = ObjectIdDict(
:isdefined => 1:1,
:simdloop => 0:0,
:gc_preserve_begin => 0:typemax(Int),
:gc_preserve_end => 0:typemax(Int)
:gc_preserve_end => 0:typemax(Int),
:thunk => 1:1
)

# @enum isn't defined yet, otherwise I'd use it for this
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_CALL_ARG = "invalid :call argument"
const EMPTY_SLOTNAMES = "slotnames field is empty"
const SLOTFLAGS_MISMATCH = "length(slotnames) != length(slotflags)"
Expand All @@ -40,6 +42,7 @@ const SLOTTYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo slottypes field is no
const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes"
const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues"
const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)"
const NON_TOP_LEVEL_GLOBAL = "encountered `Expr` head `:global` in non-top-level code (i.e. `nargs` > 0)"
const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments"
const SLOTNAMES_NARGS_MISMATCH = "CodeInfo for method contains fewer slotnames than the number of method arguments"

Expand All @@ -56,44 +59,89 @@ InvalidCodeError(kind::AbstractString) = InvalidCodeError(kind, nothing)
Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`.
"""
function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false)
function validate_val!(@nospecialize(x))
if isa(x, Expr)
if x.head === :call || x.head === :invoke
f = x.args[1]
if f isa GlobalRef && (f.name === :llvmcall || f.name === :cglobal) && x.head === :call
# TODO: these are not yet linearized
else
for arg in x.args
if !is_valid_argument(arg)
push!(errors, InvalidCodeError(INVALID_CALL_ARG, arg))
else
validate_val!(arg)
end
end
end
end
elseif isa(x, SSAValue)
id = x.id + 1 # ensures that id > 0 for use with BitSet
!in(id, ssavals) && push!(ssavals, id)
end
end

ssavals = BitSet()
lhs_slotnums = BitSet()
walkast(c.code) do x
for x in c.code
if isa(x, Expr)
!is_top_level && x.head == :method && push!(errors, InvalidCodeError(NON_TOP_LEVEL_METHOD))
narg_bounds = get(VALID_EXPR_HEADS, x.head, -1:-1)
head = x.head
if !is_top_level
head === :method && push!(errors, InvalidCodeError(NON_TOP_LEVEL_METHOD))
head === :global && push!(errors, InvalidCodeError(NON_TOP_LEVEL_GLOBAL))
end
narg_bounds = get(VALID_EXPR_HEADS, head, -1:-1)
nargs = length(x.args)
if narg_bounds == -1:-1
push!(errors, InvalidCodeError(INVALID_EXPR_HEAD, (x.head, x)))
push!(errors, InvalidCodeError(INVALID_EXPR_HEAD, (head, x)))
elseif !in(nargs, narg_bounds)
push!(errors, InvalidCodeError(INVALID_EXPR_NARGS, (x.head, nargs, x)))
elseif x.head == :(=)
push!(errors, InvalidCodeError(INVALID_EXPR_NARGS, (head, nargs, x)))
elseif head === :(=)
lhs, rhs = x.args
if !is_valid_lvalue(lhs)
push!(errors, InvalidCodeError(INVALID_LVALUE, lhs))
elseif isa(lhs, SlotNumber) && !in(lhs.id, lhs_slotnums)
n = lhs.id
push!(lhs_slotnums, n)
end
if !is_valid_rvalue(rhs)
if !is_valid_rvalue(lhs, rhs)
push!(errors, InvalidCodeError(INVALID_RVALUE, rhs))
end
elseif x.head == :call || x.head == :invoke
for arg in x.args
if !is_valid_rvalue(arg)
push!(errors, InvalidCodeError(INVALID_CALL_ARG, arg))
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 === :const || head === :enter ||
head === :leave || head === :method || head === :global || head === :static_parameter ||
head === :new || head === :thunk || head === :simdloop
validate_val!(x)
else
push!(errors, InvalidCodeError("invalid statement", x))
end
elseif isa(x, SSAValue)
id = x.id + 1 # ensures that id > 0 for use with BitSet
!in(id, ssavals) && push!(ssavals, id)
elseif isa(x, NewvarNode)
elseif isa(x, LabelNode)
elseif isa(x, GotoNode)
elseif x === nothing
elseif isa(x, SlotNumber)
elseif isa(x, GlobalRef)
elseif isa(x, LineNumberNode)
else
push!(errors, InvalidCodeError("invalid statement", x))
end
end
nslotnames = length(c.slotnames)
nslotflags = length(c.slotflags)
nssavals = length(ssavals)
nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES))
!is_top_level && nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES))
nslotnames != nslotflags && push!(errors, InvalidCodeError(SLOTFLAGS_MISMATCH, (nslotnames, nslotflags)))
if c.inferred
nslottypes = length(c.slottypes)
Expand All @@ -118,32 +166,50 @@ the `CodeInfo` instance associated with `mi`.
"""
function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance,
c::Union{Void,CodeInfo} = Core.Inference.retrieve_code_info(mi))
m = mi.def::Method
n_sig_params = length(Core.Inference.unwrap_unionall(m.sig).parameters)
if (m.isva ? (n_sig_params < (m.nargs - 1)) : (n_sig_params != m.nargs))
push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, m.nargs)))
is_top_level = mi.def isa Module
if is_top_level
mnargs = 0
else
m = mi.def::Method
mnargs = m.nargs
n_sig_params = length(Core.Inference.unwrap_unionall(m.sig).parameters)
if (m.isva ? (n_sig_params < (mnargs - 1)) : (n_sig_params != mnargs))
push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, mnargs)))
end
end
if isa(c, CodeInfo)
m.nargs > length(c.slotnames) && push!(errors, InvalidCodeError(SLOTNAMES_NARGS_MISMATCH))
validate_code!(errors, c, m.nargs == 0)
mnargs > length(c.slotnames) && push!(errors, InvalidCodeError(SLOTNAMES_NARGS_MISMATCH))
validate_code!(errors, c, is_top_level)
end
return errors
end

validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...)

function walkast(f, stmts::Array)
for stmt in stmts
f(stmt)
isa(stmt, Expr) && walkast(f, stmt.args)
is_valid_lvalue(x) = isa(x, Slot) || isa(x, SSAValue) || isa(x, GlobalRef)

function is_valid_argument(x)
if isa(x, Slot) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) ||
(isa(x,Expr) && (x.head in (:static_parameter, :boundscheck, :copyast))) ||
isa(x, Number) || isa(x, AbstractString) || isa(x, Char) || isa(x, Tuple) ||
isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing
return true
end
# TODO: consider being stricter about what needs to be wrapped with QuoteNode
return !(isa(x,Expr) || isa(x,Symbol) || isa(x,GotoNode) || isa(x,LabelNode) ||
isa(x,LineNumberNode) || isa(x,NewvarNode))
end

is_valid_lvalue(x) = isa(x, SlotNumber) || isa(x, SSAValue) || isa(x, GlobalRef)

function is_valid_rvalue(x)
isa(x, Expr) && return !in(x.head, (:gotoifnot, :line, :const, :meta))
return !isa(x, GotoNode) && !isa(x, LabelNode) && !isa(x, LineNumberNode)
function is_valid_rvalue(lhs, x)
is_valid_argument(x) && return true
if isa(x, Expr) && x.head in (:new, :the_exception, :isdefined, :call, :invoke, :foreigncall, :gc_preserve_begin)
return true
# TODO: disallow `globalref = call` when .typ field is removed
#return isa(lhs, SSAValue) || isa(lhs, Slot)
end
return false
end

is_valid_return(x) = is_valid_argument(x) || (isa(x,Expr) && x.head in (:new, :lambda))

is_flag_set(byte::UInt8, flag::UInt8) = (byte & flag) == flag
5 changes: 4 additions & 1 deletion base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2136,7 +2136,6 @@ end
@deprecate RowVector{T}(n::Tuple{Int,Int}) where {T} RowVector{T}(uninitialized, n)

@deprecate cumsum(A::AbstractArray) cumsum(A, 1)
@deprecate cumsum_kbn(A::AbstractArray) cumsum_kbn(A, 1)
@deprecate cumprod(A::AbstractArray) cumprod(A, 1)

# issue #16307
Expand Down Expand Up @@ -2188,6 +2187,10 @@ end
@deprecate_binding Complex64 ComplexF32
@deprecate_binding Complex128 ComplexF64

# issue #24804
@deprecate_moved sum_kbn "KahanSummation"
@deprecate_moved cumsum_kbn "KahanSummation"

# END 0.7 deprecations

# BEGIN 1.0 deprecations
Expand Down
36 changes: 33 additions & 3 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,19 @@ convert(::Type{T}, x::Tuple{Any, Vararg{Any}}) where {T<:Tuple} =
oftype(x, y)
Convert `y` to the type of `x` (`convert(typeof(x), y)`).
# Examples
```jldoctest
julia> x = 4;
julia> y = 3.;
julia> oftype(x, y)
3
julia> oftype(y, x)
4.0
```
"""
oftype(x, y) = convert(typeof(x), y)

Expand Down Expand Up @@ -417,9 +430,11 @@ esc(@nospecialize(e)) = Expr(:escape, e)
Annotates the expression `blk` as a bounds checking block, allowing it to be elided by [`@inbounds`](@ref).
Note that the function in which `@boundscheck` is written must be inlined into
its caller with [`@inline`](@ref) in order for `@inbounds` to have effect.
!!! note
The function in which `@boundscheck` is written must be inlined into
its caller in order for `@inbounds` to have effect.
# Examples
```jldoctest
julia> @inline function g(A, i)
@boundscheck checkbounds(A, i)
Expand Down Expand Up @@ -557,6 +572,7 @@ getindex(v::SimpleVector, I::AbstractArray) = Core.svec(Any[ v[i] for i in I ]..
Test whether the given array has a value associated with index `i`. Return `false`
if the index is out of bounds, or has an undefined reference.
# Examples
```jldoctest
julia> isassigned(rand(3, 3), 5)
true
Expand Down Expand Up @@ -729,6 +745,20 @@ For an iterator or collection that has keys and values, return an iterator
over the values.
This function simply returns its argument by default, since the elements
of a general iterator are normally considered its "values".
# Examples
```jldoctest
julia> d = Dict("a"=>1, "b"=>2);
julia> values(d)
Base.ValueIterator for a Dict{String,Int64} with 2 entries. Values:
2
1
julia> values([2])
1-element Array{Int64,1}:
2
```
"""
values(itr) = itr

Expand All @@ -753,4 +783,4 @@ const missing = Missing()
Indicate whether `x` is [`missing`](@ref).
"""
ismissing(::Any) = false
ismissing(::Missing) = true
ismissing(::Missing) = true
2 changes: 0 additions & 2 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ export
cumsum!,
accumulate,
accumulate!,
cumsum_kbn,
eachindex,
extrema,
fill!,
Expand Down Expand Up @@ -527,7 +526,6 @@ export
sub2ind,
sum!,
sum,
sum_kbn,
to_indices,
vcat,
vec,
Expand Down
Loading

0 comments on commit 40081a0

Please sign in to comment.