Skip to content

Commit b0f2c6f

Browse files
authored
Merge branch 'master' into dpa/test-nanosoldier-stdlibs
2 parents 09569be + 8e7ed1d commit b0f2c6f

File tree

119 files changed

+2658
-829
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+2658
-829
lines changed

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ and need to run code with these changes included, you can use `Revise`.
4040
To do so, run `using Revise; Revise.track(Base)` (or Revise.track with the stdlib you modified).
4141
The test system supports doing this automatically (see below).
4242

43+
For instance testing Base changes without rebuilding, using failfast, you can run:
44+
```
45+
JULIA_TEST_FAILFAST=1 ./julia -e 'using Revise; Revise.track(Base); include("test.jl")'
46+
```
47+
4348
## Specific instructions for particular changes
4449

4550
### Doctests

Compiler/src/abstractinterpretation.jl

Lines changed: 25 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2485,7 +2485,7 @@ function abstract_eval_setglobal!(interp::AbstractInterpreter, sv::AbsIntState,
24852485
(rt, exct) = global_assignment_rt_exct(interp, sv, saw_latestworld, gr, v)
24862486
return CallMeta(rt, exct, Effects(setglobal!_effects, nothrow=exct===Bottom), GlobalAccessInfo(convert(Core.Binding, gr)))
24872487
end
2488-
return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo())
2488+
return CallMeta(Union{}, Union{TypeError, ErrorException}, EFFECTS_THROWS, NoCallInfo())
24892489
end
24902490
= partialorder(typeinf_lattice(interp))
24912491
if !(hasintersect(widenconst(M), Module) && hasintersect(widenconst(s), Symbol))
@@ -3480,65 +3480,33 @@ function refine_partial_type(@nospecialize t)
34803480
return t
34813481
end
34823482

3483-
abstract_eval_nonlinearized_foreigncall_name(
3484-
::AbstractInterpreter, @nospecialize(e), ::StatementState, ::IRInterpretationState
3485-
) = nothing
3486-
3487-
function abstract_eval_nonlinearized_foreigncall_name(
3488-
interp::AbstractInterpreter, @nospecialize(e), sstate::StatementState, sv::InferenceState
3489-
)
3490-
if isexpr(e, :call)
3491-
n = length(e.args)
3492-
argtypes = Vector{Any}(undef, n)
3493-
callresult = Future{CallMeta}()
3494-
i::Int = 1
3495-
nextstate::UInt8 = 0x0
3496-
local ai, res
3497-
function evalargs(interp, sv)
3498-
if nextstate === 0x1
3499-
@goto state1
3500-
elseif nextstate === 0x2
3501-
@goto state2
3502-
end
3503-
while i <= n
3504-
ai = abstract_eval_nonlinearized_foreigncall_name(interp, e.args[i], sstate, sv)
3505-
if !isready(ai)
3506-
nextstate = 0x1
3507-
return false
3508-
@label state1
3509-
end
3510-
argtypes[i] = ai[].rt
3511-
i += 1
3512-
end
3513-
res = abstract_call(interp, ArgInfo(e.args, argtypes), sstate, sv)
3514-
if !isready(res)
3515-
nextstate = 0x2
3516-
return false
3517-
@label state2
3518-
end
3519-
callresult[] = res[]
3520-
return true
3521-
end
3522-
evalargs(interp, sv) || push!(sv.tasks, evalargs)
3523-
return callresult
3524-
else
3525-
return Future(abstract_eval_basic_statement(interp, e, sstate, sv))
3526-
end
3527-
end
3528-
35293483
function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, sstate::StatementState, sv::AbsIntState)
35303484
callee = e.args[1]
3531-
if isexpr(callee, :call) && length(callee.args) > 1 && callee.args[1] == GlobalRef(Core, :tuple)
3532-
# NOTE these expressions are not properly linearized
3533-
abstract_eval_nonlinearized_foreigncall_name(interp, callee.args[2], sstate, sv)
3534-
if length(callee.args) > 2
3535-
abstract_eval_nonlinearized_foreigncall_name(interp, callee.args[3], sstate, sv)
3485+
if isexpr(callee, :tuple)
3486+
if length(callee.args) >= 1
3487+
# Evaluate the arguments to constrain the world, effects, and other info for codegen,
3488+
# but note there is an implied `if !=(C_NULL)` branch here that might read data
3489+
# in a different world (the exact cache behavior is unspecified), so we do not use
3490+
# these results to refine reachability of the subsequent foreigncall.
3491+
abstract_eval_value(interp, callee.args[1], sstate, sv)
3492+
if length(callee.args) >= 2
3493+
abstract_eval_value(interp, callee.args[2], sstate, sv)
3494+
#TODO: implement abstract_eval_nonlinearized_foreigncall_name correctly?
3495+
# lib_effects = abstract_call(interp, ArgInfo(e.args, Any[typeof(Libdl.dlopen), lib]), sstate, sv)::Future
3496+
end
35363497
end
35373498
else
35383499
abstract_eval_value(interp, callee, sstate, sv)
35393500
end
35403501
mi = frame_instance(sv)
35413502
t = sp_type_rewrap(e.args[2], mi, true)
3503+
let fptr = e.args[1]
3504+
if !isexpr(fptr, :tuple)
3505+
if !hasintersect(widenconst(abstract_eval_value(interp, fptr, sstate, sv)), Ptr)
3506+
return RTEffects(Bottom, Any, EFFECTS_THROWS)
3507+
end
3508+
end
3509+
end
35423510
for i = 3:length(e.args)
35433511
if abstract_eval_value(interp, e.args[i], sstate, sv) === Bottom
35443512
return RTEffects(Bottom, Any, EFFECTS_THROWS)
@@ -3751,7 +3719,7 @@ end
37513719

37523720
function global_assignment_rt_exct(interp::AbstractInterpreter, sv::AbsIntState, saw_latestworld::Bool, g::GlobalRef, @nospecialize(newty))
37533721
if saw_latestworld
3754-
return Pair{Any,Any}(newty, ErrorException)
3722+
return Pair{Any,Any}(newty, Union{TypeError, ErrorException})
37553723
end
37563724
newty′ = RefValue{Any}(newty)
37573725
(valid_worlds, ret) = scan_partitions(interp, g, sv.world) do interp::AbstractInterpreter, ::Core.Binding, partition::Core.BindingPartition
@@ -3766,15 +3734,16 @@ function global_assignment_binding_rt_exct(interp::AbstractInterpreter, partitio
37663734
if is_some_guard(kind)
37673735
return Pair{Any,Any}(newty, ErrorException)
37683736
elseif is_some_const_binding(kind) || is_some_imported(kind)
3769-
return Pair{Any,Any}(Bottom, ErrorException)
3737+
# N.B.: Backdating should not improve inference in an earlier world
3738+
return Pair{Any,Any}(kind == PARTITION_KIND_BACKDATED_CONST ? newty : Bottom, ErrorException)
37703739
end
37713740
ty = kind == PARTITION_KIND_DECLARED ? Any : partition_restriction(partition)
37723741
wnewty = widenconst(newty)
37733742
if !hasintersect(wnewty, ty)
3774-
return Pair{Any,Any}(Bottom, ErrorException)
3743+
return Pair{Any,Any}(Bottom, TypeError)
37753744
elseif !(wnewty <: ty)
37763745
retty = tmeet(typeinf_lattice(interp), newty, ty)
3777-
return Pair{Any,Any}(retty, ErrorException)
3746+
return Pair{Any,Any}(retty, TypeError)
37783747
end
37793748
return Pair{Any,Any}(newty, Bottom)
37803749
end

Compiler/src/optimize.jl

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -861,10 +861,14 @@ function scan_inconsistency!(inst::Instruction, sv::PostOptAnalysisState)
861861
# Special case: For `getfield` and memory operations, we allow inconsistency of the :boundscheck argument
862862
(; inconsistent, tpdum) = sv
863863
if iscall_with_boundscheck(stmt, sv)
864-
for i = 1:(length(stmt.args)-1)
864+
for i = 1:length(stmt.args)
865865
val = stmt.args[i]
866+
# SSAValue should be the only permitted argument type which can be inconsistent found here.
867+
# Others (e.g. GlobalRef) should have been moved to statement position. See stmt_effect_flags.
866868
if isa(val, SSAValue)
867-
stmt_inconsistent |= val.id in inconsistent
869+
if i < length(stmt.args) # not the boundscheck argument (which is last)
870+
stmt_inconsistent |= val.id in inconsistent
871+
end
868872
count!(tpdum, val)
869873
end
870874
end
@@ -891,7 +895,7 @@ function ((; sv)::ScanStmt)(inst::Instruction, lstmt::Int, bb::Int)
891895
if isa(stmt, EnterNode)
892896
# try/catch not yet modeled
893897
give_up_refinements!(sv)
894-
return nothing
898+
return true # don't bail out early -- can cause tpdum counts to be off
895899
end
896900

897901
scan_non_dataflow_flags!(inst, sv)
@@ -937,36 +941,36 @@ function ((; sv)::ScanStmt)(inst::Instruction, lstmt::Int, bb::Int)
937941
end
938942
end
939943

940-
# bail out early if there are no possibilities to refine the effects
941-
if !any_refinable(sv)
942-
return nothing
943-
end
944+
# Do not bail out early, as this can cause tpdum counts to be off.
945+
# # bail out early if there are no possibilities to refine the effects
946+
# if !any_refinable(sv)
947+
# return nothing
948+
# end
944949

945950
return true
946951
end
947952

948953
function check_inconsistentcy!(sv::PostOptAnalysisState, scanner::BBScanner)
949954
(; ir, inconsistent, tpdum) = sv
950955

956+
sv.all_retpaths_consistent || return
951957
scan!(ScanStmt(sv), scanner, false)
958+
sv.all_retpaths_consistent || return
952959
complete!(tpdum); push!(scanner.bb_ip, 1)
953960
populate_def_use_map!(tpdum, scanner)
954961

955962
stmt_ip = BitSetBoundedMinPrioritySet(length(ir.stmts))
956963
for def in inconsistent
957-
for use in tpdum[def]
958-
if !(use in inconsistent)
959-
push!(inconsistent, use)
960-
append!(stmt_ip, tpdum[use])
961-
end
962-
end
963-
end
964+
append!(stmt_ip, tpdum[def])
965+
end
964966
lazydomtree = LazyDomtree(ir)
965967
while !isempty(stmt_ip)
966968
idx = popfirst!(stmt_ip)
969+
idx in inconsistent && continue # already processed
967970
inst = ir[SSAValue(idx)]
968971
stmt = inst[:stmt]
969972
if iscall_with_boundscheck(stmt, sv)
973+
# recompute inconsistent flags for call while skipping boundscheck (last) argument
970974
any_non_boundscheck_inconsistent = false
971975
for i = 1:(length(stmt.args)-1)
972976
val = stmt.args[i]
@@ -978,19 +982,18 @@ function check_inconsistentcy!(sv::PostOptAnalysisState, scanner::BBScanner)
978982
any_non_boundscheck_inconsistent || continue
979983
elseif isa(stmt, ReturnNode)
980984
sv.all_retpaths_consistent = false
985+
return
981986
elseif isa(stmt, GotoIfNot)
982987
bb = block_for_inst(ir, idx)
983988
cfg = ir.cfg
984989
blockliveness = BlockLiveness(cfg.blocks[bb].succs, nothing)
985990
for succ in iterated_dominance_frontier(cfg, blockliveness, get!(lazydomtree))
986991
visit_bb_phis!(ir, succ) do phiidx::Int
987-
push!(inconsistent, phiidx)
988-
push!(stmt_ip, phiidx)
992+
phiidx in inconsistent || push!(stmt_ip, phiidx)
989993
end
990994
end
991995
end
992-
sv.all_retpaths_consistent || break
993-
append!(inconsistent, tpdum[idx])
996+
push!(inconsistent, idx)
994997
append!(stmt_ip, tpdum[idx])
995998
end
996999
end
@@ -1009,9 +1012,9 @@ function ipo_dataflow_analysis!(interp::AbstractInterpreter, opt::OptimizationSt
10091012
completed_scan = scan!(ScanStmt(sv), scanner, true)
10101013

10111014
if !completed_scan
1012-
if sv.all_retpaths_consistent
1013-
check_inconsistentcy!(sv, scanner)
1014-
else
1015+
# finish scanning for all_retpaths_consistent computation
1016+
check_inconsistentcy!(sv, scanner)
1017+
if !sv.all_retpaths_consistent
10151018
# No longer any dataflow concerns, just scan the flags
10161019
scan!(scanner, false) do inst::Instruction, ::Int, ::Int
10171020
scan_non_dataflow_flags!(inst, sv)
@@ -1439,8 +1442,11 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp
14391442
return params.inline_nonleaf_penalty
14401443
elseif head === :foreigncall
14411444
foreigncall = ex.args[1]
1442-
if foreigncall isa QuoteNode && foreigncall.value === :jl_string_ptr
1443-
return 1
1445+
if isexpr(foreigncall, :tuple, 1)
1446+
foreigncall = foreigncall.args[1]
1447+
if foreigncall isa QuoteNode && foreigncall.value === :jl_string_ptr
1448+
return 1
1449+
end
14441450
end
14451451
return 20
14461452
elseif head === :invoke || head === :invoke_modify

Compiler/src/ssair/EscapeAnalysis.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,8 +1037,10 @@ function escape_foreigncall!(astate::AnalysisState, pc::Int, args::Vector{Any})
10371037
# NOTE array allocations might have been proven as nothrow (https://github.com/JuliaLang/julia/pull/43565)
10381038
nothrow = is_nothrow(astate.ir, pc)
10391039
name_info = nothrow ?: ThrownEscape(pc)
1040-
add_escape_change!(astate, name, name_info)
1041-
add_liveness_change!(astate, name, pc)
1040+
if !isexpr(name, :tuple)
1041+
add_escape_change!(astate, name, name_info)
1042+
add_liveness_change!(astate, name, pc)
1043+
end
10421044
for i = 1:nargs
10431045
# we should escape this argument if it is directly called,
10441046
# otherwise just impose ThrownEscape if not nothrow

Compiler/src/ssair/inlining.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,7 @@ function late_inline_special_case!(ir::IRCode, idx::Int, stmt::Expr, flag::UInt3
17531753
length(stmt.args) == 2 ? Any : stmt.args[end])
17541754
return SomeCase(typevar_call)
17551755
elseif f === UnionAll && length(argtypes) == 3 && (optimizer_lattice(state.interp), argtypes[2], TypeVar)
1756-
unionall_call = Expr(:foreigncall, QuoteNode(:jl_type_unionall), Any, svec(Any, Any),
1756+
unionall_call = Expr(:foreigncall, Expr(:tuple, QuoteNode(:jl_type_unionall)), Any, svec(Any, Any),
17571757
0, QuoteNode(:ccall), stmt.args[2], stmt.args[3])
17581758
return SomeCase(unionall_call)
17591759
elseif is_return_type(f)

Compiler/src/ssair/irinterp.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ end
294294

295295
function populate_def_use_map!(tpdum::TwoPhaseDefUseMap, scanner::BBScanner)
296296
scan!(scanner, false) do inst::Instruction, lstmt::Int, bb::Int
297-
for ur in userefs(inst)
297+
for ur in userefs(inst[:stmt])
298298
val = ur[]
299299
if isa(val, SSAValue)
300300
push!(tpdum[val.id], inst.idx)

Compiler/src/ssair/verify.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
7777
end
7878
elseif isa(op, Expr)
7979
# Only Expr(:boundscheck) is allowed in value position
80-
if isforeigncall && arg_idx == 1 && op.head === :call
81-
# Allow a tuple in symbol position for foreigncall - this isn't actually
82-
# a real call - it's interpreted in global scope by codegen. However,
83-
# we do need to keep this a real use, because it could also be a pointer.
80+
if isforeigncall && arg_idx == 1 && op.head === :tuple
81+
# Allow a tuple literal in symbol position for foreigncall - this
82+
# is syntax for a literal value or globalref - it is interpreted in
83+
# global scope by codegen.
8484
elseif !is_value_pos_expr_head(op.head)
8585
if !allow_frontend_forms || op.head !== :opaque_closure_method
8686
@verify_error "Expr not allowed in value position"

Compiler/src/verifytrim.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,17 @@ function verify_codeinstance!(interp::NativeInterpreter, codeinst::CodeInstance,
279279
error = "unresolved cfunction"
280280
elseif isexpr(stmt, :foreigncall)
281281
foreigncall = stmt.args[1]
282-
if foreigncall isa QuoteNode
283-
if foreigncall.value in runtime_functions
284-
error = "disallowed ccall into a runtime function"
282+
if isexpr(foreigncall, :tuple, 1)
283+
foreigncall = foreigncall.args[1]
284+
if foreigncall isa String
285+
foreigncall = QuoteNode(Symbol(foreigncall))
286+
end
287+
if foreigncall isa QuoteNode
288+
if foreigncall.value in runtime_functions
289+
error = "disallowed ccall into a runtime function"
290+
end
291+
else
292+
error = "disallowed ccall with non-constant name and no library"
285293
end
286294
end
287295
elseif isexpr(stmt, :new_opaque_closure)

Compiler/test/inference.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5219,11 +5219,11 @@ end
52195219
@testset "#45956: non-linearized cglobal needs special treatment for stmt effects" begin
52205220
function foo()
52215221
cglobal((a, ))
5222-
ccall(0, Cvoid, (Nothing,), b)
5222+
ccall(C_NULL, Cvoid, (Nothing,), b)
52235223
end
52245224
@test only(code_typed() do
52255225
cglobal((a, ))
5226-
ccall(0, Cvoid, (Nothing,), b)
5226+
ccall(C_NULL, Cvoid, (Nothing,), b)
52275227
end)[2] === Nothing
52285228
end
52295229

@@ -6458,7 +6458,7 @@ end
64586458
global invalid_setglobal!_exct_modeling::Int
64596459
@test Base.infer_exception_type((Float64,)) do x
64606460
setglobal!(@__MODULE__, :invalid_setglobal!_exct_modeling, x)
6461-
end == ErrorException
6461+
end == TypeError
64626462

64636463
# Issue #58257 - Hang in inference during BindingPartition resolution
64646464
module A58257

Compiler/test/irpasses.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ let # Test for https://github.com/JuliaLang/julia/issues/43402
10711071
end
10721072

10731073
refs = map(Core.SSAValue, findall(@nospecialize(x)->Meta.isexpr(x, :new), src.code))
1074-
some_ccall = findfirst(@nospecialize(x) -> Meta.isexpr(x, :foreigncall) && x.args[1] == :(:some_ccall), src.code)
1074+
some_ccall = findfirst(@nospecialize(x) -> Meta.isexpr(x, :foreigncall) && x.args[1] == Expr(:tuple, :(:some_ccall)), src.code)
10751075
@assert some_ccall !== nothing
10761076
stmt = src.code[some_ccall]
10771077
nccallargs = length(stmt.args[3]::Core.SimpleVector)

0 commit comments

Comments
 (0)