Skip to content

Commit f8e84e1

Browse files
mlechuaviatesk
andauthored
Preserve method-related meta forms through lowering (#90)
Co-authored-by: Shuhei Kadowaki <[email protected]>
1 parent 38e3514 commit f8e84e1

File tree

5 files changed

+116
-37
lines changed

5 files changed

+116
-37
lines changed

src/ast.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,14 @@ end
519519
# the middle of a pass.
520520
const CompileHints = Base.ImmutableDict{Symbol,Any}
521521

522+
function CompileHints(d::Dict{Symbol, Any})
523+
id = CompileHints()
524+
for (k, v) in d
525+
id = CompileHints(id, k, v)
526+
end
527+
id
528+
end
529+
522530
function setmeta!(ex::SyntaxTree; kws...)
523531
@assert length(kws) == 1 # todo relax later ?
524532
key = first(keys(kws))

src/compat.jl

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -459,27 +459,39 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
459459
st_k = K"hygienic_scope"
460460
elseif e.head === :meta
461461
# Messy and undocumented. Only sometimes we want a K"meta".
462-
@assert e.args[1] isa Symbol
463-
if e.args[1] === :nospecialize
464-
if nargs > 2
465-
st_k = K"block"
466-
# Kick the can down the road (should only be simple atoms?)
467-
child_exprs = map(c->Expr(:meta, :nospecialize, c), child_exprs[2:end])
462+
if e.args[1] isa Expr && e.args[1].head === :purity
463+
st_k = K"meta"
464+
child_exprs = [Expr(:quoted_symbol, :purity), Base.EffectsOverride(e.args[1].args...)]
465+
else
466+
@assert e.args[1] isa Symbol
467+
if e.args[1] === :nospecialize
468+
if nargs > 2
469+
st_k = K"block"
470+
# Kick the can down the road (should only be simple atoms?)
471+
child_exprs = map(c->Expr(:meta, :nospecialize, c), child_exprs[2:end])
472+
else
473+
st_id, src = _insert_convert_expr(e.args[2], graph, src)
474+
setmeta!(SyntaxTree(graph, st_id); nospecialize=true)
475+
return st_id, src
476+
end
477+
elseif e.args[1] in (:inline, :noinline, :generated, :generated_only,
478+
:max_methods, :optlevel, :toplevel, :push_loc, :pop_loc,
479+
:no_constprop, :aggressive_constprop, :specialize, :compile, :infer,
480+
:nospecializeinfer, :force_compile, :propagate_inbounds, :doc)
481+
# TODO: Some need to be handled in lowering
482+
for (i, ma) in enumerate(e.args)
483+
if ma isa Symbol
484+
# @propagate_inbounds becomes (meta inline
485+
# propagate_inbounds), but usually(?) only args[1] is
486+
# converted here
487+
child_exprs[i] = Expr(:quoted_symbol, e.args[i])
488+
end
489+
end
468490
else
469-
st_id, src = _insert_convert_expr(e.args[2], graph, src)
470-
setmeta!(SyntaxTree(graph, st_id); nospecialize=true)
471-
return st_id, src
491+
# Can't throw a hard error; it is explicitly tested that meta can take arbitrary keys.
492+
@error("Unknown meta form at $src: `$e`\n$(sprint(dump, e))")
493+
child_exprs[1] = Expr(:quoted_symbol, e.args[1])
472494
end
473-
elseif e.args[1] in (:inline, :noinline, :generated, :generated_only,
474-
:max_methods, :optlevel, :toplevel, :push_loc, :pop_loc,
475-
:no_constprop, :aggressive_constprop, :specialize, :compile, :infer,
476-
:nospecializeinfer, :force_compile, :doc)
477-
# TODO: Some need to be handled in lowering
478-
child_exprs[1] = Expr(:quoted_symbol, e.args[1])
479-
else
480-
# Can't throw a hard error; it is explicitly tested that meta can take arbitrary keys.
481-
@error("Unknown meta form at $src: `$e`\n$(sprint(dump, e))")
482-
child_exprs[1] = Expr(:quoted_symbol, e.args[1])
483495
end
484496
elseif e.head === :scope_layer
485497
@assert nargs === 2

src/eval.jl

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ end
218218

219219
# Convert SyntaxTree to the CodeInfo+Expr data stuctures understood by the
220220
# Julia runtime
221-
function to_code_info(ex::SyntaxTree, slots::Vector{Slot})
221+
function to_code_info(ex::SyntaxTree, slots::Vector{Slot}, meta::CompileHints)
222222
stmts = Any[]
223223

224224
current_codelocs_stack = ir_debug_info_state(ex)
@@ -267,18 +267,22 @@ function to_code_info(ex::SyntaxTree, slots::Vector{Slot})
267267
# - call site @assume_effects
268268
ssaflags = zeros(UInt32, length(stmts))
269269

270-
# TODO: Set true for @propagate_inbounds
271-
propagate_inbounds = false
270+
propagate_inbounds =
271+
get(meta, :propagate_inbounds, false)
272272
# TODO: Set true if there's a foreigncall
273-
has_fcall = false
274-
# TODO: Set for @nospecializeinfer
275-
nospecializeinfer = false
276-
# TODO: Set based on @inline -> 0x01 or @noinline -> 0x02
277-
inlining = 0x00
278-
# TODO: Set based on @constprop :aggressive -> 0x01 or @constprop :none -> 0x02
279-
constprop = 0x00
280-
# TODO: Set based on Base.@assume_effects
281-
purity = 0x0000
273+
has_fcall = false
274+
nospecializeinfer =
275+
get(meta, :nospecializeinfer, false)
276+
inlining =
277+
get(meta, :inline, false) ? 0x01 :
278+
get(meta, :noinline, false) ? 0x02 : 0x00
279+
constprop =
280+
get(meta, :aggressive_constprop, false) ? 0x01 :
281+
get(meta, :no_constprop, false) ? 0x02 : 0x00
282+
purity =
283+
let eo = get(meta, :purity, nothing)
284+
isnothing(eo) ? 0x0000 : Base.encode_effects_override(eo)
285+
end
282286

283287
# The following CodeInfo fields always get their default values for
284288
# uninferred code.
@@ -367,7 +371,7 @@ function _to_lowered_expr(ex::SyntaxTree, stmt_offset::Int)
367371
e1 = ex[1]
368372
getmeta(ex, :as_Expr, false) ? QuoteNode(Expr(e1)) : e1
369373
elseif k == K"code_info"
370-
ir = to_code_info(ex[1], ex.slots)
374+
ir = to_code_info(ex[1], ex.slots, ex.meta)
371375
if ex.is_toplevel_thunk
372376
Expr(:thunk, ir) # TODO: Maybe nice to just return a CodeInfo here?
373377
else

src/linear_ir.jl

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct LinearIRContext{GraphType} <: AbstractLoweringContext
7878
finally_handlers::Vector{FinallyHandler{GraphType}}
7979
symbolic_jump_targets::Dict{String,JumpTarget{GraphType}}
8080
symbolic_jump_origins::Vector{JumpOrigin{GraphType}}
81+
meta::Dict{Symbol, Any}
8182
mod::Module
8283
end
8384

@@ -89,7 +90,7 @@ function LinearIRContext(ctx, is_toplevel_thunk, lambda_bindings, return_type)
8990
is_toplevel_thunk, lambda_bindings, rett,
9091
Dict{String,JumpTarget{GraphType}}(), SyntaxList(ctx), SyntaxList(ctx),
9192
Vector{FinallyHandler{GraphType}}(), Dict{String,JumpTarget{GraphType}}(),
92-
Vector{JumpOrigin{GraphType}}(), ctx.mod)
93+
Vector{JumpOrigin{GraphType}}(), Dict{Symbol, Any}(), ctx.mod)
9394
end
9495

9596
function current_lambda_bindings(ctx::LinearIRContext)
@@ -807,7 +808,17 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos)
807808
nothing
808809
end
809810
elseif k == K"meta"
810-
emit(ctx, ex)
811+
@chk numchildren(ex) >= 1
812+
if ex[1].name_val in ("inline", "noinline", "propagate_inbounds",
813+
"nospecializeinfer", "aggressive_constprop", "no_constprop")
814+
for c in children(ex)
815+
ctx.meta[Symbol(c.name_val)] = true
816+
end
817+
elseif ex[1].name_val === "purity"
818+
ctx.meta[Symbol(ex[1].name_val)] = ex[2].value::Base.EffectsOverride
819+
else
820+
emit(ctx, ex)
821+
end
811822
if needs_value
812823
val = @ast ctx ex "nothing"::K"core"
813824
if in_tail_pos
@@ -1099,10 +1110,9 @@ function compile_lambda(outer_ctx, ex)
10991110
@assert info.kind == :static_parameter
11001111
slot_rewrites[id] = i
11011112
end
1102-
# @info "" @ast ctx ex [K"block" ctx.code...]
11031113
code = renumber_body(ctx, ctx.code, slot_rewrites)
11041114
@ast ctx ex [K"code_info"(is_toplevel_thunk=ex.is_toplevel_thunk,
1105-
slots=slots)
1115+
slots=slots, meta=CompileHints(ctx.meta))
11061116
[K"block"(ex[3])
11071117
code...
11081118
]
@@ -1131,7 +1141,8 @@ loops, etc) to gotos and exception handling to enter/leave. We also convert
11311141
SyntaxList(graph), SyntaxList(graph),
11321142
Vector{FinallyHandler{GraphType}}(),
11331143
Dict{String, JumpTarget{GraphType}}(),
1134-
Vector{JumpOrigin{GraphType}}(), ctx.mod)
1144+
Vector{JumpOrigin{GraphType}}(),
1145+
Dict{Symbol, Any}(), ctx.mod)
11351146
res = compile_lambda(_ctx, reparent(_ctx, ex))
11361147
_ctx, res
11371148
end

test/macros.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,48 @@ end
362362
@test test_mod.X1 isa Enum
363363
end
364364

365+
@testset "macros producing meta forms" begin
366+
function find_method_ci(thunk)
367+
ci = thunk.args[1]::Core.CodeInfo
368+
m = findfirst(x->(x isa Expr && x.head === :method && length(x.args) === 3), ci.code)
369+
ci.code[m].args[3]
370+
end
371+
jlower_e(s) = JuliaLowering.to_lowered_expr(
372+
JuliaLowering.lower(
373+
test_mod, JuliaLowering.parsestmt(
374+
JuliaLowering.SyntaxTree, s);
375+
expr_compat_mode=true))
376+
377+
prog = "Base.@assume_effects :foldable function foo(); end"
378+
ref = Meta.lower(test_mod, Meta.parse(prog))
379+
our = jlower_e(prog)
380+
@test find_method_ci(ref).purity === find_method_ci(our).purity
381+
382+
prog = "Base.@inline function foo(); end"
383+
ref = Meta.lower(test_mod, Meta.parse(prog))
384+
our = jlower_e(prog)
385+
@test find_method_ci(ref).inlining === find_method_ci(our).inlining
386+
387+
prog = "Base.@noinline function foo(); end"
388+
ref = Meta.lower(test_mod, Meta.parse(prog))
389+
our = jlower_e(prog)
390+
@test find_method_ci(ref).inlining === find_method_ci(our).inlining
391+
392+
prog = "Base.@constprop :none function foo(); end"
393+
ref = Meta.lower(test_mod, Meta.parse(prog))
394+
our = jlower_e(prog)
395+
@test find_method_ci(ref).constprop === find_method_ci(our).constprop
396+
397+
prog = "Base.@nospecializeinfer function foo(); end"
398+
ref = Meta.lower(test_mod, Meta.parse(prog))
399+
our = jlower_e(prog)
400+
@test find_method_ci(ref).nospecializeinfer === find_method_ci(our).nospecializeinfer
401+
402+
prog = "Base.@propagate_inbounds function foo(); end"
403+
ref = Meta.lower(test_mod, Meta.parse(prog))
404+
our = jlower_e(prog)
405+
@test find_method_ci(ref).propagate_inbounds === find_method_ci(our).propagate_inbounds
406+
407+
end
408+
365409
end

0 commit comments

Comments
 (0)