Skip to content

Commit 66b2090

Browse files
authored
Merge pull request #24113 from JuliaLang/jb/linear-ir-1
turn on linear IR
2 parents 224f1e9 + 952c7a5 commit 66b2090

File tree

13 files changed

+2321
-825
lines changed

13 files changed

+2321
-825
lines changed

base/codevalidation.jl

Lines changed: 98 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ const VALID_EXPR_HEADS = ObjectIdDict(
2424
:isdefined => 1:1,
2525
:simdloop => 0:0,
2626
:gc_preserve_begin => 0:typemax(Int),
27-
:gc_preserve_end => 0:typemax(Int)
27+
:gc_preserve_end => 0:typemax(Int),
28+
:thunk => 1:1
2829
)
2930

3031
# @enum isn't defined yet, otherwise I'd use it for this
3132
const INVALID_EXPR_HEAD = "invalid expression head"
3233
const INVALID_EXPR_NARGS = "invalid number of expression args"
3334
const INVALID_LVALUE = "invalid LHS value"
3435
const INVALID_RVALUE = "invalid RHS value"
36+
const INVALID_RETURN = "invalid argument to :return"
3537
const INVALID_CALL_ARG = "invalid :call argument"
3638
const EMPTY_SLOTNAMES = "slotnames field is empty"
3739
const SLOTFLAGS_MISMATCH = "length(slotnames) != length(slotflags)"
@@ -40,6 +42,7 @@ const SLOTTYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo slottypes field is no
4042
const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes"
4143
const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues"
4244
const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)"
45+
const NON_TOP_LEVEL_GLOBAL = "encountered `Expr` head `:global` in non-top-level code (i.e. `nargs` > 0)"
4346
const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments"
4447
const SLOTNAMES_NARGS_MISMATCH = "CodeInfo for method contains fewer slotnames than the number of method arguments"
4548

@@ -56,44 +59,89 @@ InvalidCodeError(kind::AbstractString) = InvalidCodeError(kind, nothing)
5659
Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`.
5760
"""
5861
function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false)
62+
function validate_val!(@nospecialize(x))
63+
if isa(x, Expr)
64+
if x.head === :call || x.head === :invoke
65+
f = x.args[1]
66+
if f isa GlobalRef && (f.name === :llvmcall || f.name === :cglobal) && x.head === :call
67+
# TODO: these are not yet linearized
68+
else
69+
for arg in x.args
70+
if !is_valid_argument(arg)
71+
push!(errors, InvalidCodeError(INVALID_CALL_ARG, arg))
72+
else
73+
validate_val!(arg)
74+
end
75+
end
76+
end
77+
end
78+
elseif isa(x, SSAValue)
79+
id = x.id + 1 # ensures that id > 0 for use with BitSet
80+
!in(id, ssavals) && push!(ssavals, id)
81+
end
82+
end
83+
5984
ssavals = BitSet()
6085
lhs_slotnums = BitSet()
61-
walkast(c.code) do x
86+
for x in c.code
6287
if isa(x, Expr)
63-
!is_top_level && x.head == :method && push!(errors, InvalidCodeError(NON_TOP_LEVEL_METHOD))
64-
narg_bounds = get(VALID_EXPR_HEADS, x.head, -1:-1)
88+
head = x.head
89+
if !is_top_level
90+
head === :method && push!(errors, InvalidCodeError(NON_TOP_LEVEL_METHOD))
91+
head === :global && push!(errors, InvalidCodeError(NON_TOP_LEVEL_GLOBAL))
92+
end
93+
narg_bounds = get(VALID_EXPR_HEADS, head, -1:-1)
6594
nargs = length(x.args)
6695
if narg_bounds == -1:-1
67-
push!(errors, InvalidCodeError(INVALID_EXPR_HEAD, (x.head, x)))
96+
push!(errors, InvalidCodeError(INVALID_EXPR_HEAD, (head, x)))
6897
elseif !in(nargs, narg_bounds)
69-
push!(errors, InvalidCodeError(INVALID_EXPR_NARGS, (x.head, nargs, x)))
70-
elseif x.head == :(=)
98+
push!(errors, InvalidCodeError(INVALID_EXPR_NARGS, (head, nargs, x)))
99+
elseif head === :(=)
71100
lhs, rhs = x.args
72101
if !is_valid_lvalue(lhs)
73102
push!(errors, InvalidCodeError(INVALID_LVALUE, lhs))
74103
elseif isa(lhs, SlotNumber) && !in(lhs.id, lhs_slotnums)
75104
n = lhs.id
76105
push!(lhs_slotnums, n)
77106
end
78-
if !is_valid_rvalue(rhs)
107+
if !is_valid_rvalue(lhs, rhs)
79108
push!(errors, InvalidCodeError(INVALID_RVALUE, rhs))
80109
end
81-
elseif x.head == :call || x.head == :invoke
82-
for arg in x.args
83-
if !is_valid_rvalue(arg)
84-
push!(errors, InvalidCodeError(INVALID_CALL_ARG, arg))
85-
end
110+
validate_val!(lhs)
111+
validate_val!(rhs)
112+
elseif head === :gotoifnot
113+
if !is_valid_argument(x.args[1])
114+
push!(errors, InvalidCodeError(INVALID_CALL_ARG, x.args[1]))
115+
end
116+
validate_val!(x.args[1])
117+
elseif head === :return
118+
if !is_valid_return(x.args[1])
119+
push!(errors, InvalidCodeError(INVALID_RETURN, x.args[1]))
86120
end
121+
validate_val!(x.args[1])
122+
elseif head === :call || head === :invoke || head == :gc_preserve_end || head === :meta ||
123+
head === :inbounds || head === :foreigncall || head === :const || head === :enter ||
124+
head === :leave || head === :method || head === :global || head === :static_parameter ||
125+
head === :new || head === :thunk || head === :simdloop
126+
validate_val!(x)
127+
else
128+
push!(errors, InvalidCodeError("invalid statement", x))
87129
end
88-
elseif isa(x, SSAValue)
89-
id = x.id + 1 # ensures that id > 0 for use with BitSet
90-
!in(id, ssavals) && push!(ssavals, id)
130+
elseif isa(x, NewvarNode)
131+
elseif isa(x, LabelNode)
132+
elseif isa(x, GotoNode)
133+
elseif x === nothing
134+
elseif isa(x, SlotNumber)
135+
elseif isa(x, GlobalRef)
136+
elseif isa(x, LineNumberNode)
137+
else
138+
push!(errors, InvalidCodeError("invalid statement", x))
91139
end
92140
end
93141
nslotnames = length(c.slotnames)
94142
nslotflags = length(c.slotflags)
95143
nssavals = length(ssavals)
96-
nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES))
144+
!is_top_level && nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES))
97145
nslotnames != nslotflags && push!(errors, InvalidCodeError(SLOTFLAGS_MISMATCH, (nslotnames, nslotflags)))
98146
if c.inferred
99147
nslottypes = length(c.slottypes)
@@ -118,32 +166,50 @@ the `CodeInfo` instance associated with `mi`.
118166
"""
119167
function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance,
120168
c::Union{Void,CodeInfo} = Core.Inference.retrieve_code_info(mi))
121-
m = mi.def::Method
122-
n_sig_params = length(Core.Inference.unwrap_unionall(m.sig).parameters)
123-
if (m.isva ? (n_sig_params < (m.nargs - 1)) : (n_sig_params != m.nargs))
124-
push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, m.nargs)))
169+
is_top_level = mi.def isa Module
170+
if is_top_level
171+
mnargs = 0
172+
else
173+
m = mi.def::Method
174+
mnargs = m.nargs
175+
n_sig_params = length(Core.Inference.unwrap_unionall(m.sig).parameters)
176+
if (m.isva ? (n_sig_params < (mnargs - 1)) : (n_sig_params != mnargs))
177+
push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, mnargs)))
178+
end
125179
end
126180
if isa(c, CodeInfo)
127-
m.nargs > length(c.slotnames) && push!(errors, InvalidCodeError(SLOTNAMES_NARGS_MISMATCH))
128-
validate_code!(errors, c, m.nargs == 0)
181+
mnargs > length(c.slotnames) && push!(errors, InvalidCodeError(SLOTNAMES_NARGS_MISMATCH))
182+
validate_code!(errors, c, is_top_level)
129183
end
130184
return errors
131185
end
132186

133187
validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...)
134188

135-
function walkast(f, stmts::Array)
136-
for stmt in stmts
137-
f(stmt)
138-
isa(stmt, Expr) && walkast(f, stmt.args)
189+
is_valid_lvalue(x) = isa(x, Slot) || isa(x, SSAValue) || isa(x, GlobalRef)
190+
191+
function is_valid_argument(x)
192+
if isa(x, Slot) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) ||
193+
(isa(x,Expr) && (x.head in (:static_parameter, :boundscheck, :copyast))) ||
194+
isa(x, Number) || isa(x, AbstractString) || isa(x, Char) || isa(x, Tuple) ||
195+
isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing
196+
return true
139197
end
198+
# TODO: consider being stricter about what needs to be wrapped with QuoteNode
199+
return !(isa(x,Expr) || isa(x,Symbol) || isa(x,GotoNode) || isa(x,LabelNode) ||
200+
isa(x,LineNumberNode) || isa(x,NewvarNode))
140201
end
141202

142-
is_valid_lvalue(x) = isa(x, SlotNumber) || isa(x, SSAValue) || isa(x, GlobalRef)
143-
144-
function is_valid_rvalue(x)
145-
isa(x, Expr) && return !in(x.head, (:gotoifnot, :line, :const, :meta))
146-
return !isa(x, GotoNode) && !isa(x, LabelNode) && !isa(x, LineNumberNode)
203+
function is_valid_rvalue(lhs, x)
204+
is_valid_argument(x) && return true
205+
if isa(x, Expr) && x.head in (:new, :the_exception, :isdefined, :call, :invoke, :foreigncall, :gc_preserve_begin)
206+
return true
207+
# TODO: disallow `globalref = call` when .typ field is removed
208+
#return isa(lhs, SSAValue) || isa(lhs, Slot)
209+
end
210+
return false
147211
end
148212

213+
is_valid_return(x) = is_valid_argument(x) || (isa(x,Expr) && x.head in (:new, :lambda))
214+
149215
is_flag_set(byte::UInt8, flag::UInt8) = (byte & flag) == flag

0 commit comments

Comments
 (0)