Skip to content

Commit 856629b

Browse files
authored
Lowering API: Allow for switching to JuliaLowering (#58207)
This PR along with [the associated JuliaLowering PR](c42f/JuliaLowering.jl#10) allow us to experiment with JuliaLowering as the core lowering implementation, mainly for JETLS development at the moment (cc @aviatesk, @c42f). ## Changes JuliaLowering works with `JuliaLowering.SyntaxTree` input instead of Expr. This change allows`SyntaxTree`s out of parsing and into lowering, hopefully without disturbing existing code expecting Expr. The current design wraps `x::SyntaxTree` like `Expr(:syntaxtree, x)` because otherwise `toplevel_eval_flex` wouldn't know what `x` is, but once we're past the experimental phase, it would only make sense for the compiler to actually know about the types it's operating on. ### New: `Core._lower` It (a julia entrypoint), `jl_lower` (C entrypoint), `jl_fl_lower`, and `fl_lower` all mirror the existing parsing API which was added in #35243. <details> <summary> Click for pre-PR call graph </summary> ![graph(6)](https://github.com/user-attachments/assets/a993eb92-5668-4ee7-837f-801feb686cb4) </details> After this change: ![graph(7)](https://github.com/user-attachments/assets/697b93cf-b3df-47b2-bc0e-c358fb0a6dca) Like `Core._parse`, `Core._lower` returns an `svec` with the first element as the expected result, and the remaining elements can hold extra information like the lowered SyntaxTree with provenance info (I've left the specification of extra return values for later, when JuliaLowering is more stable and JETLS knows what it needs, because we may opt to pack provenance into the lowered CodeInfo object instead). Parsing only uses the svec to return an offset, but it could actually make use of the the flexibility to return diagnostics in the future. It's unfortunately not clear what types we can expect to go into and come out of lowering (I got Expr, LineNumberNode, String, Symbol, and Nothing in testing.) ### Remove `jl_lower_expr_mod` I realize this isn't being called from C, and Core._lower is a more sensible entry point from julia. We could probably also remove some uncalled parse and jl_load functions. ## Limitations These belong to JuliaLowering, but are listed here as known issues for anyone who wants to try it out. - `(module ...)` forms don't go through JuliaLowering yet. Modules are lowered to a call to `JuliaLowering.eval_module(parentmod, modname, syntaxtree)`, which is currently a stub function that converts to Expr for lowering and evaluation, which JuliaLowering can't use. - Macros work differently. Defining and calling macros should work, but invoking normal macros defined pre-JuliaLowering will not. - Compilation is currently slow (~30 seconds for me).
2 parents d1ec7d5 + 3988537 commit 856629b

File tree

14 files changed

+80
-44
lines changed

14 files changed

+80
-44
lines changed

base/Base_compiler.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,9 @@ const _return_type = Compiler.return_type
383383
# Enable compiler
384384
Compiler.bootstrap!()
385385

386-
include("flparse.jl")
386+
include("flfrontend.jl")
387387
Core._setparser!(fl_parse)
388+
Core._setlowerer!(fl_lower)
388389

389390
# Further definition of Base will happen in Base.jl if loaded.
390391

base/boot.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,8 +1046,9 @@ function struct_name_shim(@nospecialize(x), name::Symbol, mod::Module, @nospecia
10461046
return x === mod ? t : getfield(x, name)
10471047
end
10481048

1049-
# Binding for the julia parser, called as
1050-
#
1049+
# Bindings for the julia frontend. The internal jl_parse and jl_lower will call
1050+
# Core._parse and Core._lower respectively (if they are not `nothing`.)
1051+
10511052
# Core._parse(text, filename, lineno, offset, options)
10521053
#
10531054
# Parse Julia code from the buffer `text`, starting at `offset` and attributing
@@ -1057,11 +1058,17 @@ end
10571058
#
10581059
# `_parse` must return an `svec` containing an `Expr` and the new offset as an
10591060
# `Int`.
1060-
#
1061-
# The internal jl_parse will call into Core._parse if not `nothing`.
10621061
_parse = nothing
10631062

1063+
# Core._lower(code, module, filename="none", linenum=0, world=0xfff..., warn=false)
1064+
#
1065+
# Lower `code` (usually Expr), returning `svec(e::Any xs::Any...)` where `e` is
1066+
# the lowered code, and `xs` is possible additional information from
1067+
# JuliaLowering (TBD).
1068+
_lower = nothing
1069+
10641070
_setparser!(parser) = setglobal!(Core, :_parse, parser)
1071+
_setlowerer!(lowerer) = setglobal!(Core, :_lower, lowerer)
10651072

10661073
# support for deprecated uses of builtin functions
10671074
_apply(x...) = _apply_iterate(Main.Base.iterate, x...)

base/expr.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1673,7 +1673,8 @@ end
16731673

16741674
# Implementation of generated functions
16751675
function generated_body_to_codeinfo(ex::Expr, defmod::Module, isva::Bool)
1676-
ci = ccall(:jl_lower_expr_mod, Any, (Any, Any), ex, defmod)
1676+
ci = ccall(:jl_fl_lower, Any, (Any, Any, Ptr{UInt8}, Csize_t, Csize_t, Cint),
1677+
ex, defmod, "none", 0, typemax(Csize_t), 0)[1]
16771678
if !isa(ci, CodeInfo)
16781679
if isa(ci, Expr) && ci.head === :error
16791680
msg = ci.args[1]

base/flparse.jl renamed to base/flfrontend.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,10 @@ end
1717
function fl_parse(text::AbstractString, filename::AbstractString, lineno, offset, options)
1818
fl_parse(String(text), String(filename), lineno, offset, options)
1919
end
20+
21+
function fl_lower(ex, mod::Module, filename::Union{String,Ptr{UInt8}}="none",
22+
lineno=0, world::Unsigned=typemax(Csize_t), warn::Bool=false)
23+
warn = warn ? 1 : 0
24+
ccall(:jl_fl_lower, Any, (Any, Any, Ptr{UInt8}, Csize_t, Csize_t, Cint),
25+
ex, mod, filename, lineno, world, warn)
26+
end

base/loading.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2809,7 +2809,11 @@ function include_string(mapexpr::Function, mod::Module, code::AbstractString,
28092809
loc = LineNumberNode(1, Symbol(filename))
28102810
try
28112811
ast = Meta.parseall(code, filename=filename)
2812-
@assert Meta.isexpr(ast, :toplevel)
2812+
if !Meta.isexpr(ast, :toplevel)
2813+
@assert Core._lower != fl_lower
2814+
# Only reached when JuliaLowering and alternate parse functions are activated
2815+
return Core.eval(mod, ast)
2816+
end
28132817
result = nothing
28142818
line_and_ex = Expr(:toplevel, loc, nothing)
28152819
for ex in ast.args

base/meta.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Takes the expression `x` and returns an equivalent expression in lowered form
160160
for executing in module `m`.
161161
See also [`code_lowered`](@ref).
162162
"""
163-
lower(m::Module, @nospecialize(x)) = ccall(:jl_lower_expr_mod, Any, (Any, Any), x, m)
163+
lower(m::Module, @nospecialize(x)) = Core._lower(x, m, "none", 0, typemax(Csize_t), false)[1]
164164

165165
"""
166166
@lower [m] x

doc/src/devdocs/eval.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ the expression. Macro expansion involves a handoff from [`eval()`](@ref) (in Jul
8989
function `jl_macroexpand()` (written in `flisp`) to the Julia macro itself (written in - what
9090
else - Julia) via `fl_invoke_julia_macro()`, and back.
9191

92-
Typically, macro expansion is invoked as a first step during a call to [`Meta.lower()`](@ref)/`jl_lower_expr_mod()`,
92+
Typically, macro expansion is invoked as a first step during a call to [`Meta.lower()`](@ref)/`Core._lower()`,
9393
although it can also be invoked directly by a call to [`macroexpand()`](@ref)/`jl_macroexpand()`.
9494

9595
## [Type Inference](@id dev-type-inference)

src/ast.c

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,13 +1271,12 @@ JL_DLLEXPORT jl_value_t *jl_macroexpand1(jl_value_t *expr, jl_module_t *inmodule
12711271
return expr;
12721272
}
12731273

1274-
// Main entry point to flisp lowering. Most arguments are optional; see `jl_lower_expr_mod`.
12751274
// warn: Print any lowering warnings returned; otherwise ignore
12761275
JL_DLLEXPORT jl_value_t *jl_fl_lower(jl_value_t *expr, jl_module_t *inmodule,
1277-
const char *file, int line, size_t world, bool_t warn)
1276+
const char *filename, int line, size_t world, bool_t warn)
12781277
{
12791278
JL_TIMING(LOWERING, LOWERING);
1280-
jl_timing_show_location(file, line, inmodule, JL_TIMING_DEFAULT_BLOCK);
1279+
jl_timing_show_location(filename, line, inmodule, JL_TIMING_DEFAULT_BLOCK);
12811280
jl_array_t *kwargs = NULL;
12821281
JL_GC_PUSH3(&expr, &kwargs, &inmodule);
12831282
expr = jl_copy_ast(expr);
@@ -1286,7 +1285,7 @@ JL_DLLEXPORT jl_value_t *jl_fl_lower(jl_value_t *expr, jl_module_t *inmodule,
12861285
fl_context_t *fl_ctx = &ctx->fl;
12871286
value_t arg = julia_to_scm(fl_ctx, expr);
12881287
value_t e = fl_applyn(fl_ctx, 3, symbol_value(symbol(fl_ctx, "jl-lower-to-thunk")), arg,
1289-
symbol(fl_ctx, file), fixnum(line));
1288+
symbol(fl_ctx, filename), fixnum(line));
12901289
value_t lwr = car_(e);
12911290
value_t warnings = car_(cdr_(e));
12921291
expr = scm_to_julia(fl_ctx, lwr, inmodule);
@@ -1302,6 +1301,7 @@ JL_DLLEXPORT jl_value_t *jl_fl_lower(jl_value_t *expr, jl_module_t *inmodule,
13021301
jl_error("julia-logmsg: bad argument list - expected "
13031302
":warn level (symbol) group (symbol) id file line msg . kwargs");
13041303
}
1304+
JL_GC_PUSH1(&warning);
13051305
jl_value_t *level = jl_exprarg(warning, 0);
13061306
jl_value_t *group = jl_exprarg(warning, 1);
13071307
jl_value_t *id = jl_exprarg(warning, 2);
@@ -1314,22 +1314,45 @@ JL_DLLEXPORT jl_value_t *jl_fl_lower(jl_value_t *expr, jl_module_t *inmodule,
13141314
}
13151315
JL_TYPECHK(logmsg, long, level);
13161316
jl_log(jl_unbox_long(level), NULL, group, id, file, line, (jl_value_t*)kwargs, msg);
1317+
JL_GC_POP();
13171318
}
1319+
jl_value_t *result = (jl_value_t *)jl_svec1(expr);
13181320
JL_GC_POP();
1319-
return expr;
1321+
return result;
13201322
}
13211323

1322-
// Lower an expression tree into Julia's intermediate-representation.
1324+
// Main C entry point to lowering. Calls jl_fl_lower during bootstrap, and
1325+
// Core._lower otherwise (this is also jl_fl_lower unless we have JuliaLowering)
13231326
JL_DLLEXPORT jl_value_t *jl_lower(jl_value_t *expr, jl_module_t *inmodule,
1324-
const char *file, int line, size_t world, bool_t warn)
1327+
const char *filename, int line, size_t world, bool_t warn)
13251328
{
1326-
// TODO: Allow change of lowerer
1327-
return jl_fl_lower(expr, inmodule, file, line, world, warn);
1328-
}
1329-
1330-
JL_DLLEXPORT jl_value_t *jl_lower_expr_mod(jl_value_t *expr, jl_module_t *inmodule)
1331-
{
1332-
return jl_lower(expr, inmodule, "none", 0, ~(size_t)0, 0);
1329+
jl_value_t *core_lower = NULL;
1330+
if (jl_core_module) {
1331+
core_lower = jl_get_global(jl_core_module, jl_symbol("_lower"));
1332+
}
1333+
if (!core_lower || core_lower == jl_nothing) {
1334+
return jl_fl_lower(expr, inmodule, filename, line, world, warn);
1335+
}
1336+
jl_value_t **args;
1337+
JL_GC_PUSHARGS(args, 7);
1338+
args[0] = core_lower;
1339+
args[1] = expr;
1340+
args[2] = (jl_value_t*)inmodule;
1341+
args[3] = jl_cstr_to_string(filename);
1342+
args[4] = jl_box_ulong(line);
1343+
args[5] = jl_box_ulong(world);
1344+
args[6] = warn ? jl_true : jl_false;
1345+
jl_task_t *ct = jl_current_task;
1346+
size_t last_age = ct->world_age;
1347+
ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
1348+
jl_value_t *result = jl_apply(args, 7);
1349+
ct->world_age = last_age;
1350+
args[0] = result; // root during error check below
1351+
JL_TYPECHK(parse, simplevector, result);
1352+
if (jl_svec_len(result) < 1)
1353+
jl_error("Result from lowering should be `svec(a::Any, x::Any...)`");
1354+
JL_GC_POP();
1355+
return result;
13331356
}
13341357

13351358
jl_code_info_t *jl_outer_ctor_body(jl_value_t *thistype, size_t nfields, size_t nsparams, jl_module_t *inmodule, const char *file, int line)

src/jl_exported_funcs.inc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,6 @@
287287
XX(jl_load_file_string) \
288288
XX(jl_lookup_code_address) \
289289
XX(jl_lower) \
290-
XX(jl_lower_expr_mod) \
291290
XX(jl_lseek) \
292291
XX(jl_lstat) \
293292
XX(jl_macroexpand) \

src/julia.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2258,7 +2258,6 @@ JL_DLLEXPORT jl_value_t *jl_parse_string(const char *text, size_t text_len,
22582258
JL_DLLEXPORT jl_value_t *jl_lower(jl_value_t *expr, jl_module_t *inmodule,
22592259
const char *file, int line, size_t world,
22602260
bool_t warn);
2261-
JL_DLLEXPORT jl_value_t *jl_lower_expr_mod(jl_value_t *expr, jl_module_t *inmodule);
22622261
// deprecated; use jl_parse_all
22632262
JL_DLLEXPORT jl_value_t *jl_parse_input_line(const char *text, size_t text_len,
22642263
const char *filename, size_t filename_len);

0 commit comments

Comments
 (0)