Skip to content

Commit 360964b

Browse files
committed
feat: respect ModuleSetup.name in code generation
1 parent faffe86 commit 360964b

File tree

9 files changed

+163
-77
lines changed

9 files changed

+163
-77
lines changed

src/Lean.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ import Lean.Namespace
4343
import Lean.EnvExtension
4444
import Lean.ErrorExplanation
4545
import Lean.DefEqAttrib
46+
import Lean.Shell

src/Lean/Elab/Frontend.lean

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,19 @@ def process (input : String) (env : Environment) (opts : Options) (fileName : Op
132132
let s ← IO.processCommands inputCtx { : Parser.ModuleParserState } (Command.mkState env {} opts)
133133
pure (s.commandState.env, s.commandState.messages)
134134

135-
@[export lean_run_frontend]
136135
def runFrontend
137136
(input : String)
138137
(opts : Options)
139138
(fileName : String)
140139
(mainModuleName : Name)
141140
(trustLevel : UInt32 := 0)
142-
(oleanFileName? : Option String := none)
143-
(ileanFileName? : Option String := none)
141+
(oleanFileName? : Option System.FilePath := none)
142+
(ileanFileName? : Option System.FilePath := none)
144143
(jsonOutput : Bool := false)
145144
(errorOnKinds : Array Name := #[])
146145
(plugins : Array System.FilePath := #[])
147146
(printStats : Bool := false)
148-
(setupFileName? : Option System.FilePath := none)
147+
(setup? : Option ModuleSetup := none)
149148
: IO (Option Environment) := do
150149
let startTime := (← IO.monoNanosNow).toFloat / 1000000000
151150
let inputCtx := Parser.mkInputContext input fileName
@@ -154,8 +153,7 @@ def runFrontend
154153
let opts := Elab.async.setIfNotSet opts true
155154
let ctx := { inputCtx with }
156155
let setup stx := do
157-
if let some file := setupFileName? then
158-
let setup ← ModuleSetup.load file
156+
if let some setup := setup? then
159157
liftM <| setup.dynlibs.forM Lean.loadDynlib
160158
return .ok {
161159
trustLevel
@@ -164,7 +162,7 @@ def runFrontend
164162
imports := setup.imports
165163
plugins := plugins ++ setup.plugins
166164
modules := setup.modules
167-
-- override cmdline options with header options
165+
-- override cmdline options with setup options
168166
opts := opts.mergeBy (fun _ _ hOpt => hOpt) setup.options.toOptions
169167
}
170168
else

src/Lean/Shell.lean

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/-
2+
Copyright (c) 2025 Lean FRO. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: eonardo de Moura, Mac Malone
5+
-/
6+
prelude
7+
import Lean.Elab.Frontend
8+
import Lean.Compiler.IR.EmitC
9+
10+
/- Lean compaion to `shell.cpp` -/
11+
12+
open System
13+
14+
namespace Lean
15+
16+
/- Runs the `main` function of the module with `args` using the Lean interpreter. -/
17+
@[extern "lean_run_main"]
18+
private opaque runMain (env : @& Environment) (opts : @& Options) (args : @& List String) : BaseIO UInt32
19+
20+
/--
21+
Initializes the LLVM subsystem.
22+
If Lean lacks LLVM support, this function will fail with an assertion violation.
23+
-/
24+
@[extern "lean_init_llvm"]
25+
private opaque initLLVM : IO Unit
26+
27+
/--
28+
Emits LLVM bitcode for the module.
29+
Before calling this function, the LLVM subsystem must first be successfuly initialized.
30+
-/
31+
@[extern "lean_emit_llvm"]
32+
private opaque emitLLVM (env : Environment) (modName : Name) (filepath : FilePath) : IO Unit
33+
34+
/-- Print all profiling times (if any) to standard error. -/
35+
@[extern "lean_display_cumulative_profiling_times"]
36+
private opaque displayCumulativeProfilingTimes : BaseIO Unit
37+
38+
/-- Whether Lean was built with an address sanitizer enabled. -/
39+
@[extern "lean_internal_has_address_sanitizer"]
40+
private opaque Internal.hasAddressSanitizer (_ : Unit) : Bool
41+
42+
@[export lean_shell_main]
43+
private def shellMain
44+
(args : List String)
45+
(input : String)
46+
(opts : Options)
47+
(fileName : String)
48+
(mainModuleName : Name)
49+
(trustLevel : UInt32 := 0)
50+
(oleanFileName? : Option System.FilePath := none)
51+
(ileanFileName? : Option System.FilePath := none)
52+
(cFileName? : Option System.FilePath := none)
53+
(bcFileName? : Option System.FilePath := none)
54+
(jsonOutput : Bool := false)
55+
(errorOnKinds : Array Name := #[])
56+
(printStats : Bool := false)
57+
(setupFileName? : Option System.FilePath := none)
58+
(run : Bool := false)
59+
: IO UInt32 := do
60+
let setup? ← setupFileName?.mapM ModuleSetup.load
61+
let mainModuleName := setup?.map (·.name) |>.getD mainModuleName
62+
let env? ← Elab.runFrontend input opts fileName mainModuleName trustLevel
63+
oleanFileName? ileanFileName? jsonOutput errorOnKinds #[] printStats setup?
64+
if let some env := env? then
65+
if run then
66+
return ← runMain env opts args
67+
if let some c := cFileName? then
68+
let .ok out ← IO.FS.Handle.mk c .write |>.toBaseIO
69+
| IO.eprintln s!"failed to create '{c}'"
70+
return 1
71+
profileitIO "C code generation" opts do
72+
let data ← IO.ofExcept <| IR.emitC env mainModuleName
73+
out.write data.toUTF8
74+
if let some bc := bcFileName? then
75+
initLLVM
76+
profileitIO "LLVM code generation" opts do
77+
emitLLVM env mainModuleName bc
78+
displayCumulativeProfilingTimes
79+
if Internal.hasAddressSanitizer () then
80+
return if env?.isSome then 0 else 1
81+
else
82+
-- When not using the address/leak sanitizer, we interrupt execution without garbage collecting.
83+
-- This is useful when profiling improvements to Lean startup time.
84+
IO.Process.exit <| if env?.isSome then 0 else 1

src/library/compiler/ir_interpreter.cpp

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,27 +1050,18 @@ class interpreter {
10501050
return r;
10511051
}
10521052

1053-
uint32 run_main(int argc, char * argv[]) {
1053+
uint32 run_main(list_ref<string_ref> const & args) {
10541054
decl d = get_decl("main");
10551055
array_ref<param> const & params = decl_params(d);
1056-
buffer<object *> args;
1056+
buffer<object *> rargs;
10571057
if (params.size() == 2) { // List String -> IO _
1058-
lean_object * in = lean_box(0);
1059-
int i = argc;
1060-
while (i > 0) {
1061-
i--;
1062-
lean_object * n = lean_alloc_ctor(1, 2, 0);
1063-
lean_ctor_set(n, 0, lean_mk_string(argv[i]));
1064-
lean_ctor_set(n, 1, in);
1065-
in = n;
1066-
}
1067-
args.push_back(in);
1058+
rargs.push_back(args.to_obj_arg());
10681059
} else { // IO _
10691060
lean_assert(params.size() == 1);
10701061
}
10711062
object * w = io_mk_world();
1072-
args.push_back(w);
1073-
w = call_boxed("main", args.size(), &args[0]);
1063+
rargs.push_back(w);
1064+
w = call_boxed("main", rargs.size(), &rargs[0]);
10741065
if (io_result_is_ok(w)) {
10751066
int ret = 0;
10761067
lean::expr ret_ty = m_env.get("main").get_type();
@@ -1137,8 +1128,14 @@ object * run_boxed_kernel(environment const & env, options const & opts, name co
11371128
return run_boxed(elab_environment_of_kernel_env(env), opts, fn, n, args);
11381129
}
11391130

1140-
uint32 run_main(elab_environment const & env, options const & opts, int argv, char * argc[]) {
1141-
return interpreter::with_interpreter<uint32>(env, opts, "main", [&](interpreter & interp) { return interp.run_main(argv, argc); });
1131+
uint32 run_main(elab_environment const & env, options const & opts, list_ref<string_ref> const & args) {
1132+
return interpreter::with_interpreter<uint32>(env, opts, "main", [&](interpreter & interp) { return interp.run_main(args); });
1133+
}
1134+
1135+
/* runMain (env : Environment) (opts : Iptions) (args : List String) : BaseIO UInt32 */
1136+
extern "C" LEAN_EXPORT obj_res lean_run_main(b_obj_arg env, b_obj_arg opts, b_obj_arg args, obj_arg) {
1137+
uint32 ret = run_main(TO_REF(elab_environment, env), TO_REF(options, opts), TO_REF(list_ref<string_ref>, args));
1138+
return io_result_mk_ok(box(ret));
11421139
}
11431140

11441141
extern "C" LEAN_EXPORT object * lean_eval_const(object * env, object * opts, object * c) {

src/library/compiler/ir_interpreter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace lean {
1212
namespace ir {
1313
/** \brief Run `n` using the "boxed" ABI, i.e. with all-owned parameters. */
1414
object * run_boxed(elab_environment const & env, options const & opts, name const & fn, unsigned n, object **args);
15-
LEAN_EXPORT uint32 run_main(elab_environment const & env, options const & opts, int argv, char * argc[]);
15+
LEAN_EXPORT uint32 run_main(elab_environment const & env, options const & opts, list_ref<string_ref> const & args);
1616
}
1717
void initialize_ir_interpreter();
1818
void finalize_ir_interpreter();

src/library/compiler/llvm.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ Lean's IR.
3737
#pragma GCC diagnostic ignored "-Wunused-parameter"
3838
#endif
3939

40+
namespace lean {
41+
/* initLLVM : IO Unit */
42+
extern "C" obj_res initialize_Lean_Compiler_IR_EmitLLVM(uint8_t builtin, obj_arg);
43+
extern "C" LEAN_EXPORT obj_res lean_init_llvm(obj_arg) {
44+
return initialize_Lean_Compiler_IR_EmitLLVM(/*builtin*/ false, io_mk_world());
45+
}
46+
47+
/* emitLLVM (env : Environment) (modName : Name) (filepath : FilePath) : IO Unit */
48+
extern "C" obj_res lean_ir_emit_llvm(obj_arg env, obj_arg mod_name, obj_arg filepath, obj_arg);
49+
extern "C" LEAN_EXPORT obj_res lean_emit_llvm(obj_arg env, obj_arg mod_name, obj_arg filepath, obj_arg) {
50+
return lean_ir_emit_llvm(env, mod_name, filepath, io_mk_world());
51+
}
52+
}
53+
4054
extern "C" LEAN_EXPORT lean_object* lean_llvm_initialize_target_info(lean_object * /* w */) {
4155

4256
#ifdef LEAN_LLVM

src/library/time_task.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ void display_cumulative_profiling_times(std::ostream & out) {
3838
out << ss.str();
3939
}
4040

41+
/* displayCumulativeProfilingTimes : BaseIO Unit */
42+
extern "C" LEAN_EXPORT obj_res lean_display_cumulative_profiling_times(obj_arg) {
43+
display_cumulative_profiling_times(std::cerr);
44+
return lean_io_result_mk_ok(box(0));
45+
}
46+
4147
void initialize_time_task() {
4248
g_cum_times_mutex = new mutex;
4349
g_cum_times = new std::map<std::string, second_duration>;

src/runtime/platform.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,23 @@ extern "C" LEAN_EXPORT uint8 lean_system_platform_emscripten(obj_arg) {
4242

4343
extern "C" object * lean_get_githash(obj_arg) { return lean_mk_string(LEAN_GITHASH); }
4444

45-
extern "C" LEAN_EXPORT uint8_t lean_internal_has_llvm_backend(lean_obj_arg _unit) {
45+
extern "C" LEAN_EXPORT uint8_t lean_internal_has_llvm_backend(obj_arg) {
4646
#ifdef LEAN_LLVM
4747
return 1;
4848
#else
4949
return 0;
5050
#endif
5151
}
52+
53+
extern "C" uint8 lean_internal_has_address_sanitizer(obj_arg) {
54+
#if defined(__has_feature)
55+
#if __has_feature(address_sanitizer)
56+
return 1;
57+
#else
58+
return 0;
59+
#endif
60+
#else
61+
return 0;
62+
#endif
63+
}
5264
}

src/util/shell.cpp

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,6 @@ using namespace lean; // NOLINT
170170
#define LEAN_SERVER_DEFAULT_MAX_HEARTBEAT 100000
171171
#endif
172172

173-
extern "C" void *initialize_Lean_Compiler_IR_EmitLLVM(uint8_t builtin,
174-
lean_object *);
175-
extern "C" object *lean_ir_emit_llvm(object *env, object *mod_name,
176-
object *filepath, object *w);
177-
178173
static void display_header(std::ostream & out) {
179174
out << "Lean (version " << get_version_string() << ", " << LEAN_STR(LEAN_BUILD_TYPE) << ")\n";
180175
}
@@ -330,46 +325,61 @@ options set_config_option(options const & opts, char const * in) {
330325
}
331326

332327
namespace lean {
333-
extern "C" object * lean_run_frontend(
328+
extern "C" object * lean_shell_main(
329+
object * args,
334330
object * input,
335331
object * opts,
336332
object * filename,
337333
object * main_module_name,
338334
uint32_t trust_level,
339335
object * olean_filename,
340336
object * ilean_filename,
337+
object * c_filename,
338+
object * bc_filename,
341339
uint8_t json_output,
342340
object * error_kinds,
343-
object * plugins,
344341
bool print_stats,
345-
object * header_file_name,
342+
object * setup_file_name,
343+
bool run,
346344
object * w
347345
);
348-
option_ref<elab_environment> run_new_frontend(
346+
uint32 run_shell_main(
347+
int argc, char* argv[],
349348
std::string const & input,
350349
options const & opts, std::string const & file_name,
351350
name const & main_module_name,
352351
uint32_t trust_level,
353352
optional<std::string> const & olean_file_name,
354353
optional<std::string> const & ilean_file_name,
354+
optional<std::string> const & c_file_name,
355+
optional<std::string> const & bc_file_name,
355356
uint8_t json_output,
356357
array_ref<name> const & error_kinds,
357358
bool print_stats,
358-
optional<std::string> const & setup_file_name
359+
optional<std::string> const & setup_file_name,
360+
bool run
359361
) {
360-
return get_io_result<option_ref<elab_environment>>(lean_run_frontend(
362+
list_ref<string_ref> args;
363+
while (argc > 0) {
364+
argc--;
365+
args = list_ref<string_ref>(string_ref(argv[argc]), args);
366+
}
367+
return get_io_scalar_result<uint32>(lean_shell_main(
368+
args.steal(),
361369
mk_string(input),
362370
opts.to_obj_arg(),
363371
mk_string(file_name),
364372
main_module_name.to_obj_arg(),
365373
trust_level,
366374
olean_file_name ? mk_option_some(mk_string(*olean_file_name)) : mk_option_none(),
367375
ilean_file_name ? mk_option_some(mk_string(*ilean_file_name)) : mk_option_none(),
376+
c_file_name ? mk_option_some(mk_string(*c_file_name)) : mk_option_none(),
377+
bc_file_name ? mk_option_some(mk_string(*bc_file_name)) : mk_option_none(),
368378
json_output,
369379
error_kinds.to_obj_arg(),
370-
mk_empty_array(),
371380
print_stats,
372381
setup_file_name ? mk_option_some(mk_string(*setup_file_name)) : mk_option_none(),
382+
run,
373383
io_mk_world()
374384
));
375385
}
@@ -765,48 +775,12 @@ extern "C" LEAN_EXPORT int lean_main(int argc, char ** argv) {
765775

766776
if (!main_module_name)
767777
main_module_name = name("_stdin");
768-
option_ref<elab_environment> opt_env = run_new_frontend(
778+
return run_shell_main(
779+
argc - optind, argv + optind,
769780
contents, opts, mod_fn, *main_module_name, trust_lvl,
770-
olean_fn, ilean_fn, json_output, error_kinds, stats, setup_fn
781+
olean_fn, ilean_fn, c_output, llvm_output,
782+
json_output, error_kinds, stats, setup_fn, run
771783
);
772-
773-
if (opt_env) {
774-
elab_environment env = opt_env.get_val();
775-
if (run) {
776-
uint32 ret = ir::run_main(env, opts, argc - optind, argv + optind);
777-
return ret;
778-
}
779-
if (c_output) {
780-
std::ofstream out(*c_output, std::ios_base::binary);
781-
if (out.fail()) {
782-
std::cerr << "failed to create '" << *c_output << "'\n";
783-
return 1;
784-
}
785-
time_task _("C code generation", opts);
786-
out << lean::ir::emit_c(env, *main_module_name).data();
787-
out.close();
788-
}
789-
if (llvm_output) {
790-
initialize_Lean_Compiler_IR_EmitLLVM(/*builtin*/ false,
791-
lean_io_mk_world());
792-
time_task _("LLVM code generation", opts);
793-
lean::consume_io_result(lean_ir_emit_llvm(
794-
env.to_obj_arg(), (*main_module_name).to_obj_arg(),
795-
lean::string_ref(*llvm_output).to_obj_arg(),
796-
lean_io_mk_world()));
797-
}
798-
}
799-
800-
display_cumulative_profiling_times(std::cerr);
801-
802-
#if defined(__has_feature)
803-
#if __has_feature(address_sanitizer)
804-
return opt_env ? 0 : 1;
805-
#endif
806-
#endif
807-
// When not using the address/leak sanitizer, we interrupt execution without garbage collecting.
808-
// This is useful when profiling improvements to Lean startup time.
809-
exit(opt_env ? 0 : 1);
810784
} catch (lean::throwable & ex) {
811785
std::cerr << ex.what() << "\n";
812786
} catch (std::bad_alloc & ex) {

0 commit comments

Comments
 (0)