Skip to content

Commit 91b09e0

Browse files
authored
Merge branch 'master' into test_for_no_invalidations
2 parents 8abdb2c + c0ecfe9 commit 91b09e0

File tree

23 files changed

+291
-95
lines changed

23 files changed

+291
-95
lines changed

Compiler/src/ssair/inlining.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,11 @@ function cfg_inline_item!(ir::IRCode, idx::Int, todo::InliningTodo, state::CFGIn
126126
block = block_for_inst(ir, idx)
127127
inline_into_block!(state, block)
128128

129-
if !isempty(inlinee_cfg.blocks[1].preds)
129+
if length(inlinee_cfg.blocks[1].preds) > 1
130130
need_split_before = true
131+
else
132+
@assert inlinee_cfg.blocks[1].preds[1] == 0
131133
end
132-
133134
last_block_idx = last(state.cfg.blocks[block].stmts)
134135
if false # TODO: ((idx+1) == last_block_idx && isa(ir[SSAValue(last_block_idx)], GotoNode))
135136
need_split = false
@@ -166,12 +167,18 @@ function cfg_inline_item!(ir::IRCode, idx::Int, todo::InliningTodo, state::CFGIn
166167
end
167168
new_block_range = (length(state.new_cfg_blocks)-length(inlinee_cfg.blocks)+1):length(state.new_cfg_blocks)
168169

169-
# Fixup the edges of the newely added blocks
170+
# Fixup the edges of the newly added blocks
170171
for (old_block, new_block) in enumerate(bb_rename_range)
171172
if old_block != 1 || need_split_before
172173
p = state.new_cfg_blocks[new_block].preds
173174
let bb_rename_range = bb_rename_range
174175
map!(p, p) do old_pred_block
176+
# the meaning of predecessor 0 depends on the block we encounter it:
177+
# - in the first block, it represents the function entry and so needs to be re-mapped
178+
if old_block == 1 && old_pred_block == 0
179+
return first(bb_rename_range) - 1
180+
end
181+
# - elsewhere, it represents external control-flow from a caught exception which is un-affected by inlining
175182
return old_pred_block == 0 ? 0 : bb_rename_range[old_pred_block]
176183
end
177184
end
@@ -186,10 +193,6 @@ function cfg_inline_item!(ir::IRCode, idx::Int, todo::InliningTodo, state::CFGIn
186193
end
187194
end
188195

189-
if need_split_before
190-
push!(state.new_cfg_blocks[first(bb_rename_range)].preds, first(bb_rename_range)-1)
191-
end
192-
193196
any_edges = false
194197
for (old_block, new_block) in enumerate(bb_rename_range)
195198
if (length(state.new_cfg_blocks[new_block].succs) == 0)
@@ -399,7 +402,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
399402
else
400403
bb_offset, post_bb_id = popfirst!(todo_bbs)
401404
# This implements the need_split_before flag above
402-
need_split_before = !isempty(item.ir.cfg.blocks[1].preds)
405+
need_split_before = length(item.ir.cfg.blocks[1].preds) > 1
403406
if need_split_before
404407
finish_current_bb!(compact, 0)
405408
end

Compiler/src/ssair/ir.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ function compute_basic_blocks(stmts::Vector{Any})
105105
end
106106
# Compute successors/predecessors
107107
for (num, b) in enumerate(blocks)
108+
if b.stmts.start == 1
109+
push!(b.preds, 0) # the entry block has a virtual predecessor
110+
end
108111
terminator = stmts[last(b.stmts)]
109112
if isa(terminator, ReturnNode)
110113
# return never has any successors

Compiler/test/ssair.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,3 +825,23 @@ end
825825

826826
@test_throws ErrorException Base.code_ircode(+, (Float64, Float64); optimize_until = "nonexisting pass name")
827827
@test_throws ErrorException Base.code_ircode(+, (Float64, Float64); optimize_until = typemax(Int))
828+
829+
#57153 check that the CFG has a #0 block predecessor and that we don't fail to compile code that observes that
830+
function _worker_task57153()
831+
while true
832+
r = let
833+
try
834+
if @noinline rand(Bool)
835+
return nothing
836+
end
837+
q, m
838+
finally
839+
missing
840+
end
841+
end
842+
r[1]::Bool
843+
end
844+
end
845+
let ir = Base.code_ircode(_worker_task57153, (), optimize_until="CC: COMPACT_2")[1].first
846+
@test findfirst(x->x==0, ir.cfg.blocks[1].preds) !== nothing
847+
end

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ New library features
134134
* `Timer` now has readable `timeout` and `interval` properties, and a more descriptive `show` method ([#57081]).
135135
* `sort` now supports `NTuple`s ([#54494]).
136136
* `map!(f, A)` now stores the results in `A`, like `map!(f, A, A)` or `A .= f.(A)` ([#40632]).
137+
* `setprecision` with a function argument (typically a `do` block) is now thread safe. Other forms
138+
should be avoided, and types should switch to an implementation using `ScopedValue` ([#51362]).
137139

138140
Standard library changes
139141
------------------------

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ Standard library changes
6161

6262
#### Profile
6363

64+
#### Random
65+
66+
* `randperm!` and `randcycle!` now support non-`Array` `AbstractArray` inputs, assuming they are mutable and their indices are one-based ([#58596]).
67+
6468
#### REPL
6569

6670
* The display of `AbstractChar`s in the main REPL mode now includes LaTeX input information like what is shown in help mode ([#58181]).

base/abstractarray.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,11 +1234,19 @@ oneunit(x::AbstractMatrix{T}) where {T} = _one(oneunit(T), x)
12341234
# While the definitions for IndexLinear are all simple enough to inline on their
12351235
# own, IndexCartesian's CartesianIndices is more complicated and requires explicit
12361236
# inlining.
1237-
function iterate(A::AbstractArray, state=(eachindex(A),))
1237+
iterate_starting_state(A) = iterate_starting_state(A, IndexStyle(A))
1238+
iterate_starting_state(A, ::IndexLinear) = firstindex(A)
1239+
iterate_starting_state(A, ::IndexStyle) = (eachindex(A),)
1240+
iterate(A::AbstractArray, state = iterate_starting_state(A)) = _iterate(A, state)
1241+
function _iterate(A::AbstractArray, state::Tuple)
12381242
y = iterate(state...)
12391243
y === nothing && return nothing
12401244
A[y[1]], (state[1], tail(y)...)
12411245
end
1246+
function _iterate(A::AbstractArray, state::Integer)
1247+
checkbounds(Bool, A, state) || return nothing
1248+
@inbounds(A[state]), state + one(state)
1249+
end
12421250

12431251
isempty(a::AbstractArray) = (length(a) == 0)
12441252

base/gmp.jl

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -843,24 +843,29 @@ Base.deepcopy_internal(x::BigInt, stackdict::IdDict) = get!(() -> MPZ.set(x), st
843843

844844
## streamlined hashing for BigInt, by avoiding allocation from shifts ##
845845

846+
Base._hash_shl!(x::BigInt, n) = MPZ.mul_2exp!(x, n)
847+
846848
if Limb === UInt64 === UInt
847849
# On 64 bit systems we can define
848850
# an optimized version for BigInt of hash_integer (used e.g. for Rational{BigInt}),
849851
# and of hash
850852

851-
using .Base: hash_finalizer
853+
using .Base: HASH_SECRET, hash_bytes, hash_finalizer
852854

853855
function hash_integer(n::BigInt, h::UInt)
856+
iszero(n) && return hash_integer(0, h)
854857
GC.@preserve n begin
855858
s = n.size
856-
s == 0 && return hash_integer(0, h)
857-
p = convert(Ptr{UInt64}, n.d)
858-
b = unsafe_load(p)
859-
h ⊻= hash_finalizer(ifelse(s < 0, -b, b) h)
860-
for k = 2:abs(s)
861-
h ⊻= hash_finalizer(unsafe_load(p, k) h)
862-
end
863-
return h
859+
h ⊻= (s < 0)
860+
861+
us = abs(s)
862+
leading_zero_bytes = div(leading_zeros(unsafe_load(n.d, us)), 8)
863+
hash_bytes(
864+
Ptr{UInt8}(n.d),
865+
8 * us - leading_zero_bytes,
866+
h,
867+
HASH_SECRET
868+
)
864869
end
865870
end
866871

@@ -892,23 +897,16 @@ if Limb === UInt64 === UInt
892897
return hash(ldexp(flipsign(Float64(limb), sz), pow), h)
893898
end
894899
h = hash_integer(pow, h)
895-
h ⊻= hash_finalizer(flipsign(limb, sz) h)
896-
for idx = idx+1:asz
897-
if shift == 0
898-
limb = unsafe_load(ptr, idx)
899-
else
900-
limb1 = limb2
901-
if idx == asz
902-
limb = limb1 >> shift
903-
limb == 0 && break # don't hash leading zeros
904-
else
905-
limb2 = unsafe_load(ptr, idx+1)
906-
limb = limb2 << upshift | limb1 >> shift
907-
end
908-
end
909-
h ⊻= hash_finalizer(limb h)
910-
end
911-
return h
900+
901+
h ⊻= (sz < 0)
902+
leading_zero_bytes = div(leading_zeros(unsafe_load(x.d, asz)), 8)
903+
trailing_zero_bytes = div(pow, 8)
904+
return hash_bytes(
905+
Ptr{UInt8}(x.d) + trailing_zero_bytes,
906+
8 * asz - (leading_zero_bytes + trailing_zero_bytes),
907+
h,
908+
HASH_SECRET
909+
)
912910
end
913911
end
914912
end

base/hashing.jl

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,81 @@ hash(x::UInt64, h::UInt) = hash_uint64(hash_mix_linear(x, h))
6969
hash(x::Int64, h::UInt) = hash(bitcast(UInt64, x), h)
7070
hash(x::Union{Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32}, h::UInt) = hash(Int64(x), h)
7171

72-
function hash_integer(n::Integer, h::UInt)
73-
h ⊻= hash_uint((n % UInt) h)
74-
n = abs(n)
75-
n >>>= sizeof(UInt) << 3
76-
while n != 0
77-
h ⊻= hash_uint((n % UInt) h)
78-
n >>>= sizeof(UInt) << 3
72+
hash_integer(x::Integer, h::UInt) = _hash_integer(x, UInt64(h)) % UInt
73+
function _hash_integer(
74+
x::Integer,
75+
seed::UInt64 = HASH_SEED,
76+
secret::NTuple{3, UInt64} = HASH_SECRET
77+
)
78+
seed ⊻= (x < 0)
79+
u = abs(x)
80+
81+
# always left-pad to full byte
82+
buflen = UInt(max(cld(top_set_bit(u), 8), 1))
83+
seed = seed (hash_mix(seed secret[1], secret[2]) buflen)
84+
85+
a = zero(UInt64)
86+
b = zero(UInt64)
87+
88+
if buflen 16
89+
if buflen 4
90+
a = (UInt64(u % UInt32) << 32) |
91+
UInt64((u >>> ((buflen - 4) * 8)) % UInt32)
92+
93+
delta = (buflen & 24) >>> (buflen >>> 3)
94+
95+
b = (UInt64((u >>> (8 * delta)) % UInt32) << 32) |
96+
UInt64((u >>> (8 * (buflen - 4 - delta))) % UInt32)
97+
else # buflen > 0
98+
b0 = u % UInt8
99+
b1 = (u >>> (8 * div(buflen, 2))) % UInt8
100+
b2 = (u >>> (8 * (buflen - 1))) % UInt8
101+
a = (UInt64(b0) << 56) |
102+
(UInt64(b1) << 32) |
103+
UInt64(b2)
104+
end
105+
else
106+
a = (u >>> 8(buflen - 16)) % UInt
107+
b = (u >>> 8(buflen - 8)) % UInt
108+
109+
i = buflen
110+
if i > 48
111+
see1 = seed
112+
see2 = seed
113+
while i 48
114+
l0 = u % UInt; u >>>= 64
115+
l1 = u % UInt; u >>>= 64
116+
l2 = u % UInt; u >>>= 64
117+
l3 = u % UInt; u >>>= 64
118+
l4 = u % UInt; u >>>= 64
119+
l5 = u % UInt; u >>>= 64
120+
121+
seed = hash_mix(l0 secret[1], l1 seed)
122+
see1 = hash_mix(l2 secret[2], l3 see1)
123+
see2 = hash_mix(l4 secret[3], l5 see2)
124+
i -= 48
125+
end
126+
seed = seed see1 see2
127+
end
128+
if i > 16
129+
l0 = u % UInt; u >>>= 64
130+
l1 = u % UInt; u >>>= 64
131+
seed = hash_mix(l0 secret[3], l1 seed secret[2])
132+
if i > 32
133+
l2 = u % UInt; u >>>= 64
134+
l3 = u % UInt; u >>>= 64
135+
seed = hash_mix(l2 secret[3], l3 seed)
136+
end
137+
end
79138
end
80-
return h
139+
140+
a = a secret[2]
141+
b = b seed
142+
b, a = mul_parts(a, b)
143+
return hash_mix(a secret[1] buflen, b secret[2])
81144
end
82145

146+
83147
## efficient value-based hashing of floats ##
84148

85149
const hx_NaN = hash(reinterpret(UInt64, NaN))
@@ -117,6 +181,7 @@ function hash(x::Float16, h::UInt)
117181
end
118182

119183
## generic hashing for rational values ##
184+
_hash_shl!(x, n) = (x << n)
120185
function hash(x::Real, h::UInt)
121186
# decompose x as num*2^pow/den
122187
num, pow, den = decompose(x)
@@ -132,6 +197,7 @@ function hash(x::Real, h::UInt)
132197
den = -den
133198
end
134199
num_z = trailing_zeros(num)
200+
135201
num >>= num_z
136202
den_z = trailing_zeros(den)
137203
den >>= den_z
@@ -156,7 +222,10 @@ function hash(x::Real, h::UInt)
156222
end
157223
# handle generic rational values
158224
h = hash_integer(pow, h)
159-
h = hash_integer(num, h)
225+
226+
# trimming only whole bytes of trailing zeros simplifies greatly
227+
# some specializations for memory-backed bitintegers
228+
h = hash_integer((pow > 0) ? _hash_shl!(num, pow % 8) : num, h)
160229
return h
161230
end
162231

@@ -209,7 +278,7 @@ end
209278
else
210279
pos = 1
211280
i = buflen
212-
while i 48
281+
if i > 48
213282
see1 = seed
214283
see2 = seed
215284
while i 48

base/irrationals.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ isinteger(::AbstractIrrational) = false
182182
iszero(::AbstractIrrational) = false
183183
isone(::AbstractIrrational) = false
184184

185-
hash(x::Irrational, h::UInt) = 3*objectid(x) - h
185+
hash(x::Irrational, h::UInt) = 3h - objectid(x)
186186

187187
widen(::Type{T}) where {T<:Irrational} = T
188188

base/mpfr.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,9 +1183,29 @@ Often used as `setprecision(T, precision) do ... end`
11831183
Note: `nextfloat()`, `prevfloat()` do not use the precision mentioned by
11841184
`setprecision`.
11851185
1186+
!!! warning
1187+
There is a fallback implementation of this method that calls `precision`
1188+
and `setprecision`, but it should no longer be relied on. Instead, you
1189+
should define the 3-argument form directly in a way that uses `ScopedValue`,
1190+
or recommend that callers use `ScopedValue` and `@with` themselves.
1191+
11861192
!!! compat "Julia 1.8"
11871193
The `base` keyword requires at least Julia 1.8.
11881194
"""
1195+
function setprecision(f::Function, ::Type{T}, prec::Integer; kws...) where T
1196+
depwarn("""
1197+
The fallback `setprecision(::Function, ...)` method is deprecated. Packages overloading this method should
1198+
implement their own specialization using `ScopedValue` instead.
1199+
""", :setprecision)
1200+
old_prec = precision(T)
1201+
setprecision(T, prec; kws...)
1202+
try
1203+
return f()
1204+
finally
1205+
setprecision(T, old_prec)
1206+
end
1207+
end
1208+
11891209
function setprecision(f::Function, ::Type{BigFloat}, prec::Integer; base::Integer=2)
11901210
Base.ScopedValues.@with(CURRENT_PRECISION => _convert_precision_from_base(prec, base), f())
11911211
end

0 commit comments

Comments
 (0)