• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37632

26 Sep 2023 06:44AM UTC coverage: 86.999% (-0.9%) from 87.914%
#37632

push

local

web-flow
inference: make `throw` block deoptimization concrete-eval friendly (#49235)

The deoptimization can sometimes destroy the effects analysis and
disable [semi-]concrete evaluation that is otherwise possible. This is
because the deoptimization was designed with the type domain
profitability in mind (#35982), and hasn't been adequately considering
the effects domain.

This commit makes the deoptimization aware of the effects domain more
and enables the `throw` block deoptimization only when the effects
already known to be ineligible for concrete-evaluation.

In our current effect system, `ALWAYS_FALSE`/`false` means that the
effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given
user annotation later). Therefore we can enable the `throw` block
deoptimization without hindering the chance of concrete-evaluation when
any of the following conditions are met:
- `effects.consistent === ALWAYS_FALSE`
- `effects.effect_free === ALWAYS_FALSE`
- `effects.terminates === false`
- `effects.nonoverlayed === false`

Here are some numbers:

| Metric | master | this commit | #35982 reverted (set
`unoptimize_throw_blocks=false`) |

|-------------------------|-----------|-------------|--------------------------------------------|
| Base (seconds) | 15.579300 | 15.206645 | 15.296319 |
| Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 |
| Total (seconds) | 33.499279 | 32.874737 | 33.035448 |
| Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 |
| First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M
allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874
seconds (11.64 M allocations)` |
| First time `solve(prob, QNDF())(5.0)` [^2] | `4.469492 seconds (15.32
M allocations)` | `4.499217 seconds (15.41 M allocations)` | `4.470772
seconds (15.38 M allocations)` |

[^1]: With disabling precompilation of Plots.jl.
[^2]: With disabling precompilation of OrdinaryDiffEq.

These numbers ma... (continued)

7 of 7 new or added lines in 1 file covered. (100.0%)

73407 of 84377 relevant lines covered (87.0%)

11275130.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.33
/base/refpointer.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
    Ref{T}
5

6
An object that safely references data of type `T`. This type is guaranteed to point to
7
valid, Julia-allocated memory of the correct type. The underlying data is protected from
8
freeing by the garbage collector as long as the `Ref` itself is referenced.
9

10
In Julia, `Ref` objects are dereferenced (loaded or stored) with `[]`.
11

12
Creation of a `Ref` to a value `x` of type `T` is usually written `Ref(x)`.
13
Additionally, for creating interior pointers to containers (such as Array or Ptr),
14
it can be written `Ref(a, i)` for creating a reference to the `i`-th element of `a`.
15

16
`Ref{T}()` creates a reference to a value of type `T` without initialization.
17
For a bitstype `T`, the value will be whatever currently resides in the memory
18
allocated. For a non-bitstype `T`, the reference will be undefined and attempting to
19
dereference it will result in an error, "UndefRefError: access to undefined reference".
20

21
To check if a `Ref` is an undefined reference, use [`isassigned(ref::RefValue)`](@ref).
22
For example, `isassigned(Ref{T}())` is `false` if `T` is not a bitstype.
23
If `T` is a bitstype, `isassigned(Ref{T}())` will always be true.
24

25
When passed as a `ccall` argument (either as a `Ptr` or `Ref` type), a `Ref`
26
object will be converted to a native pointer to the data it references.
27
For most `T`, or when converted to a `Ptr{Cvoid}`, this is a pointer to the
28
object data. When `T` is an `isbits` type, this value may be safely mutated,
29
otherwise mutation is strictly undefined behavior.
30

31
As a special case, setting `T = Any` will instead cause the creation of a
32
pointer to the reference itself when converted to a `Ptr{Any}`
33
(a `jl_value_t const* const*` if T is immutable, else a `jl_value_t *const *`).
34
When converted to a `Ptr{Cvoid}`, it will still return a pointer to the data
35
region as for any other `T`.
36

37
A `C_NULL` instance of `Ptr` can be passed to a `ccall` `Ref` argument to initialize it.
38

39
# Use in broadcasting
40
`Ref` is sometimes used in broadcasting in order to treat the referenced values as a scalar.
41

42
# Examples
43

44
```jldoctest
45
julia> r = Ref(5) # Create a Ref with an initial value
46
Base.RefValue{Int64}(5)
47

48
julia> r[] # Getting a value from a Ref
49
5
50

51
julia> r[] = 7 # Storing a new value in a Ref
52
7
53

54
julia> r # The Ref now contains 7
55
Base.RefValue{Int64}(7)
56

57
julia> isa.(Ref([1,2,3]), [Array, Dict, Int]) # Treat reference values as scalar during broadcasting
58
3-element BitVector:
59
 1
60
 0
61
 0
62

63
julia> Ref{Function}()  # Undefined reference to a non-bitstype, Function
64
Base.RefValue{Function}(#undef)
65

66
julia> try
67
           Ref{Function}()[] # Dereferencing an undefined reference will result in an error
68
       catch e
69
           println(e)
70
       end
71
UndefRefError()
72

73
julia> Ref{Int64}()[]; # A reference to a bitstype refers to an undetermined value if not given
74

75
julia> isassigned(Ref{Int64}()) # A reference to a bitstype is always assigned
76
true
77
```
78
"""
79
Ref
80

81
# C NUL-terminated string pointers; these can be used in ccall
82
# instead of Ptr{Cchar} and Ptr{Cwchar_t}, respectively, to enforce
83
# a check for embedded NUL chars in the string (to avoid silent truncation).
84
if Int === Int64
85
    primitive type Cstring  64 end
86
    primitive type Cwstring 64 end
87
else
88
    primitive type Cstring  32 end
89
    primitive type Cwstring 32 end
90
end
91

92

93
### General Methods for Ref{T} type
94

95
eltype(x::Type{<:Ref{T}}) where {T} = @isdefined(T) ? T : Any
1,575,844✔
96
convert(::Type{Ref{T}}, x::Ref{T}) where {T} = x
×
97
size(x::Ref) = ()
1✔
98
axes(x::Ref) = ()
1,576,045✔
99
length(x::Ref) = 1
×
100
isempty(x::Ref) = false
×
101
ndims(x::Ref) = 0
1✔
102
ndims(::Type{<:Ref}) = 0
1,576,115✔
103
iterate(r::Ref) = (r[], nothing)
2✔
104
iterate(r::Ref, s) = nothing
2✔
105
IteratorSize(::Type{<:Ref}) = HasShape{0}()
2✔
106

107
# create Ref objects for general object conversion
108
unsafe_convert(::Type{Ref{T}}, x::Ref{T}) where {T} = unsafe_convert(Ptr{T}, x)
83,649,672✔
109
unsafe_convert(::Type{Ref{T}}, x) where {T} = unsafe_convert(Ptr{T}, x)
×
110

111
convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x)::RefValue{T}
26,941,834✔
112

113
### Methods for a Ref object that is backed by an array at index i
114
struct RefArray{T,A<:AbstractArray{T},R} <: Ref{T}
115
    x::A
116
    i::Int
117
    roots::R # should be either ::Nothing or ::Any
118
    RefArray{T,A,R}(x,i,roots=nothing) where {T,A<:AbstractArray{T},R} = new(x,i,roots)
2,270✔
119
end
120
RefArray(x::AbstractArray{T}, i::Int, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, i, roots)
2,233✔
121
RefArray(x::AbstractArray{T}, i::Int=1, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, i, nothing)
74✔
122
RefArray(x::AbstractArray{T}, i::Integer, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, Int(i), roots)
×
123
RefArray(x::AbstractArray{T}, i::Integer, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, Int(i), nothing)
×
124
convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1)
9✔
125

126
function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefArray{T})::P where T
79✔
127
    if allocatedinline(T)
79✔
128
        p = pointer(b.x, b.i)
2,269✔
129
    elseif isconcretetype(T) && ismutabletype(T)
×
130
        p = pointer_from_objref(b.x[b.i])
×
131
    else
132
        # see comment on equivalent branch for RefValue
133
        p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid}))
×
134
    end
135
    return p
79✔
136
end
137
function unsafe_convert(::Type{Ptr{Any}}, b::RefArray{Any})::Ptr{Any}
×
138
    return pointer(b.x, b.i)
×
139
end
140

141
###
142
if is_primary_base_module
143
    Ref(x::Any) = RefValue(x)
11,223,101✔
144
    Ref{T}() where {T} = RefValue{T}() # Ref{T}()
39,141,057✔
145
    Ref{T}(x) where {T} = RefValue{T}(x) # Ref{T}(x)
42,105,373✔
146

147
    Ref(x::Ref, i::Integer) = (i != 1 && error("Ref only has one element"); x)
×
148
    Ref(x::Ptr{T}, i::Integer) where {T} = x + (i - 1) * Core.sizeof(T)
×
149

150
    # convert Arrays to pointer arrays for ccall
151
    function Ref{P}(a::Array{<:Union{Ptr,Cwstring,Cstring}}) where P<:Union{Ptr,Cwstring,Cstring}
×
152
        return RefArray(a) # effectively a no-op
×
153
    end
154
    function Ref{P}(a::Array{T}) where P<:Union{Ptr,Cwstring,Cstring} where T
2,233✔
155
        if (!isbitstype(T) && T <: eltype(P))
×
156
            # this Array already has the right memory layout for the requested Ref
157
            return RefArray(a,1,false) # root something, so that this function is type-stable
×
158
        else
159
            ptrs = Vector{P}(undef, length(a)+1)
2,233✔
160
            roots = Vector{Any}(undef, length(a))
2,233✔
161
            for i = 1:length(a)
4,465✔
162
                root = cconvert(P, a[i])
45,088✔
163
                ptrs[i] = unsafe_convert(P, root)::P
45,088✔
164
                roots[i] = root
45,088✔
165
            end
87,944✔
166
            ptrs[length(a)+1] = C_NULL
2,233✔
167
            return RefArray(ptrs,1,roots)
2,233✔
168
        end
169
    end
170
    Ref(x::AbstractArray, i::Integer) = RefArray(x, i)
28✔
171
end
172

173
cconvert(::Type{Ptr{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a
31✔
174
cconvert(::Type{Ref{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a
×
175
cconvert(::Type{Ptr{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a)
2,190✔
176
cconvert(::Type{Ref{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a)
43✔
177

178
# pass NTuple{N,T} as Ptr{T}/Ref{T}
179
cconvert(::Type{Ref{T}}, t::NTuple{N,T}) where {N,T} = Ref{NTuple{N,T}}(t)
4✔
180
cconvert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} = r
3✔
181
unsafe_convert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} =
7✔
182
    convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r))
183
unsafe_convert(::Type{Ptr{T}}, r::Ref{NTuple{N,T}}) where {N,T} =
36,812✔
184
    convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r))
185
unsafe_convert(::Type{Ptr{T}}, r::Ptr{NTuple{N,T}}) where {N,T} =
×
186
    convert(Ptr{T}, r)
187

188
###
189

190
getindex(b::RefArray) = b.x[b.i]
4✔
191
setindex!(b::RefArray, x) = (b.x[b.i] = x; b)
2✔
192

193
###
194

195
"""
196
    LLVMPtr{T, AS}
197

198
A pointer type that more closely resembles LLVM semantics: It includes the pointer address
199
space, and will be passed as an actual pointer instead of an integer.
200

201
This type is mainly used to interface with code that has strict requirements about pointers,
202
e.g., intrinsics that are selected based on the address space, or back-ends that require
203
pointers to be identifiable by their types.
204
"""
205
Core.LLVMPtr
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc