This PR makes Lean code generation respect the module name provided through `lean --setup`. This is accomplished by porting to Lean the portion of `shell.cpp` that spans running the frontend to exiting the process. This makes it easier to load the module setup and control how its name is passed to the code generation functions. This port attempts to minimize the changes made to Lean. It marks the new Lean functions `private` and tries to preserve as faithfully as possible the behavior of the original C++ code. Exposing the new Lean interface publicly and/or further improving the code now that is written in Lean is left for the future.
84 lines
3 KiB
Text
84 lines
3 KiB
Text
/-
|
|
Copyright (c) 2025 Lean FRO. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura, Mac Malone
|
|
-/
|
|
prelude
|
|
import Lean.Elab.Frontend
|
|
import Lean.Compiler.IR.EmitC
|
|
|
|
/- Lean companion to `shell.cpp` -/
|
|
|
|
open System
|
|
|
|
namespace Lean
|
|
|
|
/- Runs the `main` function of the module with `args` using the Lean interpreter. -/
|
|
@[extern "lean_run_main"]
|
|
private opaque runMain (env : @& Environment) (opts : @& Options) (args : @& List String) : BaseIO UInt32
|
|
|
|
/--
|
|
Initializes the LLVM subsystem.
|
|
If Lean lacks LLVM support, this function will fail with an assertion violation.
|
|
-/
|
|
@[extern "lean_init_llvm"]
|
|
private opaque initLLVM : IO Unit
|
|
|
|
/--
|
|
Emits LLVM bitcode for the module.
|
|
Before calling this function, the LLVM subsystem must first be successfully initialized.
|
|
-/
|
|
@[extern "lean_emit_llvm"]
|
|
private opaque emitLLVM (env : Environment) (modName : Name) (filepath : FilePath) : IO Unit
|
|
|
|
/-- Print all profiling times (if any) to standard error. -/
|
|
@[extern "lean_display_cumulative_profiling_times"]
|
|
private opaque displayCumulativeProfilingTimes : BaseIO Unit
|
|
|
|
/-- Whether Lean was built with an address sanitizer enabled. -/
|
|
@[extern "lean_internal_has_address_sanitizer"]
|
|
private opaque Internal.hasAddressSanitizer (_ : Unit) : Bool
|
|
|
|
@[export lean_shell_main]
|
|
private def shellMain
|
|
(args : List String)
|
|
(input : String)
|
|
(opts : Options)
|
|
(fileName : String)
|
|
(mainModuleName : Name)
|
|
(trustLevel : UInt32 := 0)
|
|
(oleanFileName? : Option System.FilePath := none)
|
|
(ileanFileName? : Option System.FilePath := none)
|
|
(cFileName? : Option System.FilePath := none)
|
|
(bcFileName? : Option System.FilePath := none)
|
|
(jsonOutput : Bool := false)
|
|
(errorOnKinds : Array Name := #[])
|
|
(printStats : Bool := false)
|
|
(setupFileName? : Option System.FilePath := none)
|
|
(run : Bool := false)
|
|
: IO UInt32 := do
|
|
let setup? ← setupFileName?.mapM ModuleSetup.load
|
|
let mainModuleName := setup?.map (·.name) |>.getD mainModuleName
|
|
let env? ← Elab.runFrontend input opts fileName mainModuleName trustLevel
|
|
oleanFileName? ileanFileName? jsonOutput errorOnKinds #[] printStats setup?
|
|
if let some env := env? then
|
|
if run then
|
|
return ← runMain env opts args
|
|
if let some c := cFileName? then
|
|
let .ok out ← IO.FS.Handle.mk c .write |>.toBaseIO
|
|
| IO.eprintln s!"failed to create '{c}'"
|
|
return 1
|
|
profileitIO "C code generation" opts do
|
|
let data ← IO.ofExcept <| IR.emitC env mainModuleName
|
|
out.write data.toUTF8
|
|
if let some bc := bcFileName? then
|
|
initLLVM
|
|
profileitIO "LLVM code generation" opts do
|
|
emitLLVM env mainModuleName bc
|
|
displayCumulativeProfilingTimes
|
|
if Internal.hasAddressSanitizer () then
|
|
return if env?.isSome then 0 else 1
|
|
else
|
|
-- When not using the address/leak sanitizer, we interrupt execution without garbage collecting.
|
|
-- This is useful when profiling improvements to Lean startup time.
|
|
IO.Process.exit <| if env?.isSome then 0 else 1
|