Skip to content

Commit

Permalink
define and use an internal macro to introduce Expr(:boundscheck) (#…
Browse files Browse the repository at this point in the history
…52077)

To avoid the previous uses of `eval` just to manually introduce
`Expr(:boundscheck)`. The new macro is named `@_boundscheck` and is
intended for internal use only, streamlining our codebase a bit.
  • Loading branch information
aviatesk authored and pull[bot] committed Dec 21, 2023
1 parent c8e15eb commit daaaaaa
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 29 deletions.
2 changes: 2 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Base, is_primary_base_module)
macro inline() Expr(:meta, :inline) end
macro noinline() Expr(:meta, :noinline) end

macro _boundscheck() Expr(:boundscheck) end

# Try to help prevent users from shooting them-selves in the foot
# with ambiguities by defining a few common and critical operations
# (and these don't need the extra convert code)
Expand Down
25 changes: 13 additions & 12 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,12 @@ ccall(:jl_toplevel_eval_in, Any, (Any, Any),
macro nospecialize(x)
_expr(:meta, :nospecialize, x)
end
Expr(@nospecialize args...) = _expr(args...)

_is_internal(__module__) = __module__ === Core
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
macro _foldable_meta()
return _is_internal(__module__) && _expr(:meta, _expr(:purity,
return _is_internal(__module__) && Expr(:meta, Expr(:purity,
#=:consistent=#true,
#=:effect_free=#true,
#=:nothrow=#false,
Expand All @@ -283,6 +284,11 @@ macro _foldable_meta()
#=:noub=#true))
end

macro inline() Expr(:meta, :inline) end
macro noinline() Expr(:meta, :noinline) end

macro _boundscheck() Expr(:boundscheck) end

# n.b. the effects and model of these is refined in inference abstractinterpretation.jl
TypeVar(@nospecialize(n)) = _typevar(n::Symbol, Union{}, Any)
TypeVar(@nospecialize(n), @nospecialize(ub)) = _typevar(n::Symbol, Union{}, ub)
Expand All @@ -309,16 +315,11 @@ kwftype(@nospecialize(t)) = typeof(kwcall)
Union{}(a...) = throw(ArgumentError("cannot construct a value of type Union{} for return result"))
kwcall(kwargs, ::Type{Union{}}, a...) = Union{}(a...)

Expr(@nospecialize args...) = _expr(args...)

abstract type Exception end
struct ErrorException <: Exception
msg::AbstractString
end

macro inline() Expr(:meta, :inline) end
macro noinline() Expr(:meta, :noinline) end

struct BoundsError <: Exception
a::Any
i::Any
Expand Down Expand Up @@ -509,14 +510,14 @@ const undef = UndefInitializer()
const Memory{T} = GenericMemory{:not_atomic, T, CPU}
const MemoryRef{T} = GenericMemoryRef{:not_atomic, T, CPU}
GenericMemoryRef(mem::GenericMemory) = memoryref(mem)
eval(Core, :(GenericMemoryRef(ref::GenericMemoryRef, i::Integer) = memoryref(ref, Int(i), $(Expr(:boundscheck)))))
eval(Core, :(GenericMemoryRef(mem::GenericMemory, i::Integer) = memoryref(memoryref(mem), Int(i), $(Expr(:boundscheck)))))
GenericMemoryRef(ref::GenericMemoryRef, i::Integer) = memoryref(ref, Int(i), @_boundscheck)
GenericMemoryRef(mem::GenericMemory, i::Integer) = memoryref(memoryref(mem), Int(i), @_boundscheck)
MemoryRef(mem::Memory) = memoryref(mem)
eval(Core, :(MemoryRef(ref::MemoryRef, i::Integer) = memoryref(ref, Int(i), $(Expr(:boundscheck)))))
eval(Core, :(MemoryRef(mem::Memory, i::Integer) = memoryref(memoryref(mem), Int(i), $(Expr(:boundscheck)))))
MemoryRef(ref::MemoryRef, i::Integer) = memoryref(ref, Int(i), @_boundscheck)
MemoryRef(mem::Memory, i::Integer) = memoryref(memoryref(mem), Int(i), @_boundscheck)
MemoryRef{T}(mem::Memory{T}) where {T} = memoryref(mem)
eval(Core, :(MemoryRef{T}(ref::MemoryRef{T}, i::Integer) where {T} = memoryref(ref, Int(i), $(Expr(:boundscheck)))))
eval(Core, :(MemoryRef{T}(mem::Memory{T}, i::Integer) where {T} = memoryref(memoryref(mem), Int(i), $(Expr(:boundscheck)))))
MemoryRef{T}(ref::MemoryRef{T}, i::Integer) where {T} = memoryref(ref, Int(i), @_boundscheck)
MemoryRef{T}(mem::Memory{T}, i::Integer) where {T} = memoryref(memoryref(mem), Int(i), @_boundscheck)

# construction helpers for Array
new_as_memoryref(self::Type{GenericMemoryRef{isatomic,T,addrspace}}, m::Int) where {T,isatomic,addrspace} = memoryref(fieldtype(self, :mem)(undef, m))
Expand Down
2 changes: 2 additions & 0 deletions base/compiler/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ include(mod, x) = Core.include(mod, x)
macro inline() Expr(:meta, :inline) end
macro noinline() Expr(:meta, :noinline) end

macro _boundscheck() Expr(:boundscheck) end

convert(::Type{Any}, Core.@nospecialize x) = x
convert(::Type{T}, x::T) where {T} = x

Expand Down
19 changes: 9 additions & 10 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ length(a::Array) = getfield(getfield(getfield(a, :ref), :mem), :length)
length(a::GenericMemory) = getfield(a, :length)
throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I)))

eval(:(getindex(A::GenericMemory{:not_atomic}, i::Int) = memoryrefget(memoryref(memoryref(A), i, $(Expr(:boundscheck))), :not_atomic, false)))
eval(:(getindex(A::GenericMemoryRef{:not_atomic}) = memoryrefget(A, :not_atomic, $(Expr(:boundscheck)))))
getindex(A::GenericMemory{:not_atomic}, i::Int) = memoryrefget(memoryref(memoryref(A), i, @_boundscheck), :not_atomic, false)
getindex(A::GenericMemoryRef{:not_atomic}) = memoryrefget(A, :not_atomic, @_boundscheck)

# multidimensional getindex will be defined later on

Expand Down Expand Up @@ -786,20 +786,19 @@ macro goto(name::Symbol)
end

# linear indexing
eval(:(function getindex(A::Array, i::Int)
@boundscheck ult_int(bitcast(UInt, sub_int(i, 1)), bitcast(UInt, length(A))) || throw_boundserror(A, (i,))
memoryrefget(memoryref(getfield(A, :ref), i, false), :not_atomic, false)
end))
function getindex(A::Array, i::Int)
@boundscheck ult_int(bitcast(UInt, sub_int(i, 1)), bitcast(UInt, length(A))) || throw_boundserror(A, (i,))
memoryrefget(memoryref(getfield(A, :ref), i, false), :not_atomic, false)
end
# simple Array{Any} operations needed for bootstrap
function setindex!(A::Array{Any}, @nospecialize(x), i::Int)
@boundscheck ult_int(bitcast(UInt, sub_int(i, 1)), bitcast(UInt, length(A))) || throw_boundserror(A, (i,))
memoryrefset!(memoryref(getfield(A, :ref), i, false), x, :not_atomic, false)
return A
end
@eval setindex!(A::Memory{Any}, @nospecialize(x), i::Int) = (memoryrefset!(memoryref(memoryref(A), i, $(Expr(:boundscheck))), x, :not_atomic, $(Expr(:boundscheck))); A)
@eval setindex!(A::MemoryRef{T}, x) where {T} = memoryrefset!(A, convert(T, x), :not_atomic, $(Expr(:boundscheck)))
@eval setindex!(A::MemoryRef{Any}, @nospecialize(x)) = memoryrefset!(A, x, :not_atomic, $(Expr(:boundscheck)))

setindex!(A::Memory{Any}, @nospecialize(x), i::Int) = (memoryrefset!(memoryref(memoryref(A), i, @_boundscheck), x, :not_atomic, @_boundscheck); A)
setindex!(A::MemoryRef{T}, x) where {T} = memoryrefset!(A, convert(T, x), :not_atomic, @_boundscheck)
setindex!(A::MemoryRef{Any}, @nospecialize(x)) = memoryrefset!(A, x, :not_atomic, @_boundscheck)

# SimpleVector

Expand Down
8 changes: 3 additions & 5 deletions base/genericmemory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function isassigned(a::Memory, i::Int)
return @inbounds memoryref_isassigned(GenericMemoryRef(a, i), :not_atomic, false)
end

@eval isassigned(a::GenericMemoryRef) = memoryref_isassigned(a, :not_atomic, $(Expr(:boundscheck)))
isassigned(a::GenericMemoryRef) = memoryref_isassigned(a, :not_atomic, @_boundscheck)

## copy ##
function unsafe_copyto!(dest::MemoryRef{T}, src::MemoryRef{T}, n) where {T}
Expand Down Expand Up @@ -182,14 +182,12 @@ getindex(A::Memory, c::Colon) = copy(A)

## Indexing: setindex! ##

@eval begin
function setindex!(A::Memory{T}, x, i1::Int) where {T}
val = x isa T ? x : convert(T,x)::T
ref = memoryref(memoryref(A), i1, $(Expr(:boundscheck)))
memoryrefset!(ref, val, :not_atomic, $(Expr(:boundscheck)))
ref = memoryref(memoryref(A), i1, @_boundscheck)
memoryrefset!(ref, val, :not_atomic, @_boundscheck)
return A
end
end
function setindex!(A::Memory{T}, x, i1::Int, i2::Int, I::Int...) where {T}
@inline
@boundscheck (i2 == 1 && all(==(1), I)) || throw_boundserror(A, (i1, i2, I...))
Expand Down
4 changes: 2 additions & 2 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ firstindex(@nospecialize t::Tuple) = 1
lastindex(@nospecialize t::Tuple) = length(t)
size(@nospecialize(t::Tuple), d::Integer) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d"))
axes(@nospecialize t::Tuple) = (OneTo(length(t)),)
@eval getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, $(Expr(:boundscheck)))
@eval getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), $(Expr(:boundscheck)))
getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, @_boundscheck)
getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), @_boundscheck)
__inbounds_getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, false)
__inbounds_getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), false)
getindex(t::Tuple, r::AbstractArray{<:Any,1}) = (eltype(t)[t[ri] for ri in r]...,)
Expand Down

0 comments on commit daaaaaa

Please sign in to comment.