Skip to content

Commit

Permalink
Manually manage inlining/noinlining of _checkbounds/BoundsError
Browse files Browse the repository at this point in the history
this should help avoiding unnecessary tuple allocations, i.e.
delaying allocation until an error actually occurs
  • Loading branch information
carlobaldassi committed Jan 7, 2015
1 parent 14ea3b6 commit a1ffab5
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 9 deletions.
30 changes: 23 additions & 7 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,29 @@ linearindexing{A<:Array}(::Type{A}) = LinearFast()
linearindexing{A<:Range}(::Type{A}) = LinearFast()

## Bounds checking ##
_checkbounds(sz::Int, i::Int, A::AbstractArray, J...) = 1 <= i <= sz || throw(BoundsError(A, J))
_checkbounds(sz::Int, i::Real, A::AbstractArray, J...) = _checkbounds(sz, to_index(i), A, J...)
_checkbounds(sz::Int, I::AbstractVector{Bool}, A::AbstractArray, J...) = length(I) == sz || throw(BoundsError(A, J))
_checkbounds(sz::Int, r::Range{Int}, A::AbstractArray, J...) = isempty(r) || (minimum(r) >= 1 && maximum(r) <= sz) || throw(BoundsError(A, J))
_checkbounds{T<:Real}(sz::Int, r::Range{T}, A::AbstractArray, J...) = _checkbounds(sz, to_index(r), A, J...)

function _checkbounds{T <: Real}(sz::Int, I::AbstractArray{T}, A::AbstractArray, J...)
# note: we force inlining of all _checkbounds methods in order to force splatting
# of the arguments and avoid allocating tuples (unless a BoundsError is raised).
# we can't use @inline at this stage in the build process, since it indirectly uses
# _checkbounds itself (see expr.jl), so we define a crude version
macro _crude_inline(f)
body = arrayref(f.args, 2)
oldargs = body.args
newargs = Array(Any, arraylen(oldargs) + 1)
arrayset(newargs, convert(Any, Expr(:meta, :inline)), 1)
for i = 1:arraylen(oldargs)
arrayset(newargs, convert(Any, arrayref(oldargs, i)), i+1)
end
body.args = newargs
esc(f)
end

@_crude_inline _checkbounds(sz::Int, i::Int, A::AbstractArray, J...) = 1 <= i <= sz || throw(BoundsError(A, J))
@_crude_inline _checkbounds(sz::Int, i::Real, A::AbstractArray, J...) = _checkbounds(sz, to_index(i), A, J...)
@_crude_inline _checkbounds(sz::Int, I::AbstractVector{Bool}, A::AbstractArray, J...) = length(I) == sz || throw(BoundsError(A, J))
@_crude_inline _checkbounds(sz::Int, r::Range{Int}, A::AbstractArray, J...) = isempty(r) || (minimum(r) >= 1 && maximum(r) <= sz) || throw(BoundsError(A, J))
@_crude_inline _checkbounds{T<:Real}(sz::Int, r::Range{T}, A::AbstractArray, J...) = _checkbounds(sz, to_index(r), A, J...)

@_crude_inline function _checkbounds{T <: Real}(sz::Int, I::AbstractArray{T}, A::AbstractArray, J...)
for i in I
_checkbounds(sz, i, A, J...)
end
Expand Down
13 changes: 11 additions & 2 deletions base/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@ typealias Callable Union(Function,DataType)
const Bottom = Union()

# constructors for Core types in boot.jl
call(T::Type{Expr}, args::ANY...) = _expr(args...)
call(T::Type{BoundsError}) = Core.call(T)
call(T::Type{BoundsError}, args...) = Core.call(T, args...)
# note: manual @noinline to avoid tuple allocations unless an error actually occurs
# the following should do:
# @noinline call(T::Type{BoundsError}, args...) = Core.call(T, args...)
# except we can't use @noinline at this stage
eval(:(function call(T::Type{BoundsError}, args...)
$(Expr(:meta, :noinline))
begin
Core.call(T, args...)
end
end))
call(T::Type{DivideError}) = Core.call(T)
call(T::Type{DomainError}) = Core.call(T)
call(T::Type{OverflowError}) = Core.call(T)
Expand All @@ -22,7 +32,6 @@ call(T::Type{ASCIIString}, d::Array{UInt8,1}) = Core.call(T, d)
call(T::Type{UTF8String}, d::Array{UInt8,1}) = Core.call(T, d)
call(T::Type{TypeVar}, args...) = Core.call(T, args...)
call(T::Type{TypeConstructor}, args...) = Core.call(T, args...)
call(T::Type{Expr}, args::ANY...) = _expr(args...)
call(T::Type{LineNumberNode}, n::Int) = Core.call(T, n)
call(T::Type{LabelNode}, n::Int) = Core.call(T, n)
call(T::Type{GotoNode}, n::Int) = Core.call(T, n)
Expand Down

0 comments on commit a1ffab5

Please sign in to comment.