Skip to content

Commit 18205fb

Browse files
committed
lowering: Canonicalize to builtins for global assignment
This adjusts lowering to emit `setglobal!` for assignment to globals, thus making the `=` expr head used only for slots in `CodeInfo` and entirely absent in `IRCode`. The primary reason for this is just to reduce the number of special cases that compiler passes have to reason about. In IRCode, `=` was already essentially equivalent to `setglobal!`, so there's no good reason not to canonicalize. Finally, the `=` syntax form for globals already gets recognized specially to insert `convert` calls to their declared binding type, so this doesn't impose any additional requirements on lowering to distinguish local from global assignments. In general, I'd also like to separate syntax and intermediate forms as much as possible where their semantics differ, which this accomplises by just using the builtin. This change is mostly semantically invisible, except that spliced-in GlobalRefs now declare their binding because they are indistinguishable from ordinary assignments at the stage where I inserted the lowering. If we want to, we can preserve the difference, but it'd be a bit more annoying for not much benefit, because: 1. The spliced in version was only recently made to work anyway, and 2. The semantics of when exactly bindings are declared is still messy on master anyway and will get tweaked shortly in further binding partitions work.
1 parent 1001750 commit 18205fb

File tree

8 files changed

+23
-63
lines changed

8 files changed

+23
-63
lines changed

Compiler/src/abstractinterpretation.jl

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4020,13 +4020,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState, nextr
40204020
end
40214021
effects === nothing || merge_override_effects!(interp, effects, frame)
40224022
if lhs !== nothing && rt !== Bottom
4023-
if isa(lhs, SlotNumber)
4024-
changes = StateUpdate(lhs, VarState(rt, false))
4025-
elseif isa(lhs, GlobalRef)
4026-
handle_global_assignment!(interp, frame, currsaw_latestworld, lhs, rt)
4027-
else
4028-
merge_effects!(interp, frame, EFFECTS_UNKNOWN)
4029-
end
4023+
changes = StateUpdate(lhs::SlotNumber, VarState(rt, false))
40304024
end
40314025
end
40324026
if !has_curr_ssaflag(frame, IR_FLAG_NOTHROW)

Compiler/src/optimize.jl

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,16 +1408,7 @@ function statement_cost(ex::Expr, line::Int, src::Union{CodeInfo, IRCode}, sptyp
14081408
extyp = line == -1 ? Any : argextype(SSAValue(line), src, sptypes)
14091409
return extyp === Union{} ? 0 : UNKNOWN_CALL_COST
14101410
elseif head === :(=)
1411-
if ex.args[1] isa GlobalRef
1412-
cost = UNKNOWN_CALL_COST
1413-
else
1414-
cost = 0
1415-
end
1416-
a = ex.args[2]
1417-
if a isa Expr
1418-
cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, params))
1419-
end
1420-
return cost
1411+
return statement_cost(ex.args[2], -1, src, sptypes, params)
14211412
elseif head === :copyast
14221413
return 100
14231414
end

Compiler/src/ssair/EscapeAnalysis.jl

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -642,13 +642,6 @@ function analyze_escapes(ir::IRCode, nargs::Int, 𝕃ₒ::AbstractLattice, get_e
642642
escape_invoke!(astate, pc, stmt.args)
643643
elseif head === :new || head === :splatnew
644644
escape_new!(astate, pc, stmt.args)
645-
elseif head === :(=)
646-
lhs, rhs = stmt.args
647-
if isa(lhs, GlobalRef) # global store
648-
add_escape_change!(astate, rhs, ⊤)
649-
else
650-
unexpected_assignment!(ir, pc)
651-
end
652645
elseif head === :foreigncall
653646
escape_foreigncall!(astate, pc, stmt.args)
654647
elseif head === :throw_undef_if_not # XXX when is this expression inserted ?
@@ -981,11 +974,6 @@ function escape_unanalyzable_obj!(astate::AnalysisState, @nospecialize(obj), obj
981974
return objinfo
982975
end
983976

984-
@noinline function unexpected_assignment!(ir::IRCode, pc::Int)
985-
@eval Main (ir = $ir; pc = $pc)
986-
error("unexpected assignment found: inspect `Main.pc` and `Main.pc`")
987-
end
988-
989977
is_nothrow(ir::IRCode, pc::Int) = has_flag(ir[SSAValue(pc)], IR_FLAG_NOTHROW)
990978

991979
# NOTE if we don't maintain the alias set that is separated from the lattice state, we can do

Compiler/src/ssair/verify.jl

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,8 @@ function verify_ir(ir::IRCode, print::Bool=true,
363363
isforeigncall = false
364364
if isa(stmt, Expr)
365365
if stmt.head === :(=)
366-
if stmt.args[1] isa SSAValue
367-
@verify_error "SSAValue as assignment LHS"
368-
raise_error()
369-
end
370-
if stmt.args[2] isa GlobalRef
371-
# undefined GlobalRef as assignment RHS is OK
372-
continue
373-
end
366+
@verify_error "Assignment should have been removed during SSA conversion"
367+
raise_error()
374368
elseif stmt.head === :isdefined
375369
if length(stmt.args) > 2 || (length(stmt.args) == 2 && !isa(stmt.args[2], Bool))
376370
@verify_error "malformed isdefined"

Compiler/test/inline.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2111,7 +2111,7 @@ for run_finalizer_escape_test in (run_finalizer_escape_test1, run_finalizer_esca
21112111
global finalizer_escape::Int = 0
21122112

21132113
let src = code_typed1(run_finalizer_escape_test, Tuple{Bool, Bool})
2114-
@test any(x->isexpr(x, :(=)), src.code)
2114+
@test any(iscall((src, Core.setglobal!)), src.code)
21152115
end
21162116

21172117
let

src/interpreter.c

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -569,25 +569,11 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
569569
s->locals[n - 1] = rhs;
570570
}
571571
else {
572-
jl_module_t *modu;
573-
jl_sym_t *sym;
574-
// Plain assignment is allowed to create bindings at
575-
// toplevel and only for the current module
576-
int alloc = toplevel;
577-
if (jl_is_globalref(lhs)) {
578-
modu = jl_globalref_mod(lhs);
579-
sym = jl_globalref_name(lhs);
580-
alloc &= modu == s->module;
581-
}
582-
else {
583-
assert(jl_is_symbol(lhs));
584-
modu = s->module;
585-
sym = (jl_sym_t*)lhs;
586-
}
587-
JL_GC_PUSH1(&rhs);
588-
jl_binding_t *b = jl_get_binding_wr(modu, sym, alloc);
589-
jl_checked_assignment(b, modu, sym, rhs);
590-
JL_GC_POP();
572+
// This is an unmodeled error. Our frontend only generates
573+
// legal `=` expressions, but since GlobalRef used to be legal
574+
// here, give a loud error in case any package is modifying
575+
// internals.
576+
jl_error("Invalid IR: Assignment LHS not a Slot");
591577
}
592578
}
593579
else if (head == jl_leave_sym) {

src/julia-syntax.scm

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4607,14 +4607,20 @@ f(x) = yt(x)
46074607
(cdr cnd)
46084608
(list cnd))))))
46094609
tests))
4610+
(define (emit-assignment-or-setglobal lhs rhs)
4611+
(if (globalref? lhs)
4612+
(begin
4613+
(emit `(global ,lhs))
4614+
(emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs)))
4615+
(emit `(= ,lhs ,rhs))))
46104616
(define (emit-assignment lhs rhs)
46114617
(if rhs
46124618
(if (valid-ir-rvalue? lhs rhs)
4613-
(emit `(= ,lhs ,rhs))
4619+
(emit-assignment-or-setglobal lhs rhs)
46144620
(let ((rr (make-ssavalue)))
46154621
(emit `(= ,rr ,rhs))
4616-
(emit `(= ,lhs ,rr))))
4617-
(emit `(= ,lhs (null)))) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved
4622+
(emit-assignment-or-setglobal lhs rr)))
4623+
(emit-assignment-or-setglobal lhs `(null))) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved
46184624
#f)
46194625
;; the interpreter loop. `break-labels` keeps track of the labels to jump to
46204626
;; for all currently closing break-blocks.
@@ -4693,7 +4699,7 @@ f(x) = yt(x)
46934699
rhs (make-ssavalue))))
46944700
(if (not (eq? rr rhs))
46954701
(emit `(= ,rr ,rhs)))
4696-
(emit `(= ,lhs ,rr))
4702+
(emit-assignment-or-setglobal lhs rr)
46974703
(if tail (emit-return tail rr))
46984704
rr)
46994705
(emit-assignment lhs rhs))))))

test/syntax.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3713,7 +3713,7 @@ end
37133713
module Foreign54607
37143714
# Syntactic, not dynamic
37153715
try_to_create_binding1() = (Foreign54607.foo = 2)
3716-
# GlobalRef is allowed for same-module assignment
3716+
# GlobalRef is allowed for same-module assignment and declares the binding
37173717
@eval try_to_create_binding2() = ($(GlobalRef(Foreign54607, :foo2)) = 2)
37183718
function global_create_binding()
37193719
global bar
@@ -3728,7 +3728,7 @@ module Foreign54607
37283728
end
37293729
@test_throws ErrorException (Foreign54607.foo = 1)
37303730
@test_throws ErrorException Foreign54607.try_to_create_binding1()
3731-
@test_throws ErrorException Foreign54607.try_to_create_binding2()
3731+
Foreign54607.try_to_create_binding2()
37323732
function assign_in_foreign_module()
37333733
(Foreign54607.foo = 1)
37343734
nothing
@@ -3744,6 +3744,7 @@ Foreign54607.global_create_binding()
37443744
@test isdefined(Foreign54607, :baz)
37453745
@test isdefined(Foreign54607, :compiled_assign)
37463746
@test isdefined(Foreign54607, :gr_assign)
3747+
@test isdefined(Foreign54607, :foo2)
37473748
Foreign54607.bar = 8
37483749
@test Foreign54607.bar == 8
37493750
begin

0 commit comments

Comments
 (0)