mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 10:24:07 +00:00
refactor: port EmitC to LCNF (#12781)
This PR ports the C emission pass from IR to LCNF, marking the last step of the IR/LCNF conversion and thus enabling end-to-end code generation through the new compilation infrastructure.
This commit is contained in:
@@ -13,7 +13,6 @@ public import Lean.Compiler.IR.CompilerM
|
||||
public import Lean.Compiler.IR.NormIds
|
||||
public import Lean.Compiler.IR.Checker
|
||||
public import Lean.Compiler.IR.UnboxResult
|
||||
public import Lean.Compiler.IR.EmitC
|
||||
public import Lean.Compiler.IR.Sorry
|
||||
public import Lean.Compiler.IR.ToIR
|
||||
public import Lean.Compiler.IR.ToIRType
|
||||
@@ -34,7 +33,6 @@ def compile (decls : Array Decl) : CompilerM (Array Decl) := do
|
||||
let mut decls := decls
|
||||
decls ← updateSorryDep decls
|
||||
logDecls `result decls
|
||||
checkDecls decls
|
||||
addDecls decls
|
||||
inferMeta decls
|
||||
return decls
|
||||
|
||||
@@ -70,6 +70,9 @@ where
|
||||
decl.saveImpure
|
||||
let decls ← Compiler.LCNF.addBoxedVersions #[decl]
|
||||
let decls ← Compiler.LCNF.runExplicitRc decls
|
||||
for decl in decls do
|
||||
decl.saveImpure
|
||||
modifyEnv fun env => Compiler.LCNF.recordFinalImpureDecl env decl.name
|
||||
return decls
|
||||
|
||||
addIr (decls : Array (Compiler.LCNF.Decl .impure)) : CoreM Unit := do
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ prelude
|
||||
public import Lean.Compiler.NameMangling
|
||||
public import Lean.Compiler.IR.EmitUtil
|
||||
public import Lean.Compiler.IR.NormIds
|
||||
public import Lean.Compiler.IR.SimpCase
|
||||
public import Lean.Compiler.IR.LLVMBindings
|
||||
import Lean.Compiler.LCNF.Types
|
||||
import Lean.Compiler.ModPkgExt
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Lean.Compiler.IR.Basic
|
||||
|
||||
public section
|
||||
|
||||
namespace Lean.IR
|
||||
|
||||
def ensureHasDefault (alts : Array Alt) : Array Alt :=
|
||||
if alts.any Alt.isDefault then alts
|
||||
else if alts.size < 2 then alts
|
||||
else
|
||||
let last := alts.back!
|
||||
let alts := alts.pop
|
||||
alts.push (Alt.default last.body)
|
||||
|
||||
end Lean.IR
|
||||
@@ -850,9 +850,11 @@ where
|
||||
| .jmp .. => inc
|
||||
| .return .. | unreach .. => return ()
|
||||
|
||||
@[inline]
|
||||
partial def Code.forM [Monad m] (c : Code pu) (f : Code pu → m Unit) : m Unit :=
|
||||
go c
|
||||
where
|
||||
@[specialize]
|
||||
go (c : Code pu) : m Unit := do
|
||||
f c
|
||||
match c with
|
||||
|
||||
1163
src/Lean/Compiler/LCNF/EmitC.lean
Normal file
1163
src/Lean/Compiler/LCNF/EmitC.lean
Normal file
File diff suppressed because it is too large
Load Diff
56
src/Lean/Compiler/LCNF/EmitUtil.lean
Normal file
56
src/Lean/Compiler/LCNF/EmitUtil.lean
Normal file
@@ -0,0 +1,56 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Henrik Böving
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Lean.Compiler.LCNF.CompilerM
|
||||
import Lean.Compiler.LCNF.PhaseExt
|
||||
import Lean.Compiler.InitAttr
|
||||
|
||||
namespace Lean.Compiler.LCNF
|
||||
|
||||
private structure CollectUsedDeclsState where
|
||||
visited : NameSet := {}
|
||||
localDecls : Array (Decl .impure) := #[]
|
||||
extSigs : Array (Signature .impure) := #[]
|
||||
|
||||
/--
|
||||
Find all declarations that the declarations in `decls` transitively depend on. They are returned
|
||||
partitioned into the declarations from the current module and declarations from other modules.
|
||||
-/
|
||||
public partial def collectUsedDecls (decls : Array Name) :
|
||||
CoreM (Array (Decl .impure) × Array (Signature .impure)) := do
|
||||
let (_, state) ← go decls |>.run {}
|
||||
return (state.localDecls, state.extSigs)
|
||||
where
|
||||
go (names : Array Name) : StateRefT CollectUsedDeclsState CoreM Unit :=
|
||||
names.forM fun name => do
|
||||
if (← get).visited.contains name then return
|
||||
modify fun s => { s with visited := s.visited.insert name }
|
||||
if let some decl ← getLocalImpureDecl? name then
|
||||
modify fun s => { s with localDecls := s.localDecls.push decl }
|
||||
decl.value.forCodeM (·.forM visitCode)
|
||||
let env ← getEnv
|
||||
if let some initializer := getBuiltinInitFnNameFor? env decl.name <|> getInitFnNameFor? env decl.name then
|
||||
go #[initializer]
|
||||
else if let some sig ← getImpureSignature? name then
|
||||
modify fun s => { s with extSigs := s.extSigs.push sig }
|
||||
else
|
||||
panic! s!"collectUsedDecls: could not find declaration or signature for '{name}'"
|
||||
|
||||
visitCode (code : Code .impure) : StateRefT CollectUsedDeclsState CoreM Unit := do
|
||||
match code with
|
||||
| .let decl _ =>
|
||||
match decl.value with
|
||||
| .const declName .. | .fap declName .. | .pap declName .. =>
|
||||
go #[declName]
|
||||
| _ => return ()
|
||||
| _ => return ()
|
||||
|
||||
public def usesModuleFrom (env : Environment) (modulePrefix : Name) : Bool :=
|
||||
env.header.modules.any fun mod => mod.irPhases != .comptime && modulePrefix.isPrefixOf mod.module
|
||||
|
||||
end Lean.Compiler.LCNF
|
||||
@@ -85,7 +85,9 @@ def saveImpure : Pass where
|
||||
phaseOut := .impure
|
||||
name := `saveImpure
|
||||
run decls := decls.mapM fun decl => do
|
||||
(← normalizeFVarIds decl).saveImpure
|
||||
let decl ← normalizeFVarIds decl
|
||||
decl.saveImpure
|
||||
modifyEnv fun env => recordFinalImpureDecl env decl.name
|
||||
return decl
|
||||
shouldAlwaysRunCheck := true
|
||||
|
||||
@@ -160,8 +162,8 @@ def builtinPassManager : PassManager := {
|
||||
pushProj (occurrence := 1),
|
||||
detectSimpleGround,
|
||||
inferVisibility (phase := .impure),
|
||||
saveImpure, -- End of impure phase
|
||||
toposortPass,
|
||||
saveImpure, -- End of impure phase
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,15 @@ namespace Lean.Compiler.LCNF
|
||||
/--
|
||||
Set of public declarations whose base bodies should be exported to other modules
|
||||
-/
|
||||
private builtin_initialize baseTransparentDeclsExt : EnvExtension (List Name × NameSet) ← mkDeclSetExt
|
||||
private builtin_initialize baseTransparentDeclsExt : EnvExtension (List Name × NameSet) ← mkOrderedDeclSetExt
|
||||
/--
|
||||
Set of public declarations whose mono bodies should be exported to other modules
|
||||
-/
|
||||
private builtin_initialize monoTransparentDeclsExt : EnvExtension (List Name × NameSet) ← mkDeclSetExt
|
||||
private builtin_initialize monoTransparentDeclsExt : EnvExtension (List Name × NameSet) ← mkOrderedDeclSetExt
|
||||
/--
|
||||
Set of public declarations whose impure bodies should be exported to other modules
|
||||
-/
|
||||
private builtin_initialize impureTransparentDeclsExt : EnvExtension (List Name × NameSet) ← mkDeclSetExt
|
||||
private builtin_initialize impureTransparentDeclsExt : EnvExtension (List Name × NameSet) ← mkOrderedDeclSetExt
|
||||
|
||||
private def getTransparencyExt : Phase → EnvExtension (List Name × NameSet)
|
||||
| .base => baseTransparentDeclsExt
|
||||
@@ -171,6 +171,9 @@ def getMonoDecl? (declName : Name) : CoreM (Option (Decl .pure)) := do
|
||||
def getLocalImpureDecl? (declName : Name) : CoreM (Option (Decl .impure)) := do
|
||||
return impureExt.getState (← getEnv) |>.find? declName
|
||||
|
||||
def getLocalImpureDecls : CoreM (Array Name) := do
|
||||
return impureExt.getState (← getEnv) |>.toArray |>.map (·.fst)
|
||||
|
||||
def getImpureSignature? (declName : Name) : CoreM (Option (Signature .impure)) := do
|
||||
return getSigCore? (← getEnv) impureSigExt declName
|
||||
|
||||
@@ -224,4 +227,23 @@ def getLocalDecl? (declName : Name) : CompilerM (Option ((pu : Purity) × Decl p
|
||||
let some decl ← getLocalDeclAt? declName (← getPhase) | return none
|
||||
return some ⟨_, decl⟩
|
||||
|
||||
builtin_initialize declOrderExt : EnvExtension (List Name × NameSet) ← mkOrderedDeclSetExt
|
||||
|
||||
def recordFinalImpureDecl (env : Environment) (name : Name) : Environment :=
|
||||
declOrderExt.modifyState env fun s =>
|
||||
(name :: s.1, s.2.insert name)
|
||||
|
||||
def getImpureDeclIndices (env : Environment) (targets : Array Name) : Std.HashMap Name Nat := Id.run do
|
||||
let (names, set) := declOrderExt.getState env
|
||||
let mut map := Std.HashMap.emptyWithCapacity set.size
|
||||
let targetSet := Std.HashSet.ofArray targets
|
||||
let mut i := set.size
|
||||
for name in names do
|
||||
if targetSet.contains name then
|
||||
map := map.insert name i
|
||||
assert! i != 0
|
||||
i := i - 1
|
||||
assert! map.size == targets.size
|
||||
return map
|
||||
|
||||
end Lean.Compiler.LCNF
|
||||
|
||||
@@ -10,15 +10,18 @@ public import Lean.Environment
|
||||
|
||||
namespace Lean.Compiler.LCNF
|
||||
|
||||
/-- Creates a replayable local environment extension holding a name set. -/
|
||||
public def mkDeclSetExt : IO (EnvExtension (List Name × NameSet)) :=
|
||||
/--
|
||||
Creates a replayable local environment extension holding a name set and the list of names in the
|
||||
order they were added to the set.
|
||||
-/
|
||||
public def mkOrderedDeclSetExt : IO (EnvExtension (List Name × NameSet)) :=
|
||||
registerEnvExtension
|
||||
(mkInitial := pure ([], {}))
|
||||
(asyncMode := .sync)
|
||||
(replay? := some <| fun oldState newState _ s =>
|
||||
let newEntries := newState.1.take (newState.1.length - oldState.1.length)
|
||||
newEntries.foldl (init := s) fun s n =>
|
||||
if s.1.contains n then
|
||||
newEntries.reverse.foldl (init := s) fun s n =>
|
||||
if s.2.contains n then
|
||||
s
|
||||
else
|
||||
(n :: s.1, if newState.2.contains n then s.2.insert n else s.2))
|
||||
@@ -26,7 +29,7 @@ public def mkDeclSetExt : IO (EnvExtension (List Name × NameSet)) :=
|
||||
/--
|
||||
Set of declarations to be exported to other modules; visibility shared by base/mono/IR phases.
|
||||
-/
|
||||
private builtin_initialize publicDeclsExt : EnvExtension (List Name × NameSet) ← mkDeclSetExt
|
||||
private builtin_initialize publicDeclsExt : EnvExtension (List Name × NameSet) ← mkOrderedDeclSetExt
|
||||
|
||||
public def isDeclPublic (env : Environment) (declName : Name) : Bool := Id.run do
|
||||
if !env.header.isModule then
|
||||
|
||||
@@ -115,6 +115,16 @@ def Decl.simpCase (decl : Decl .impure) : CompilerM (Decl .impure) := do
|
||||
let value ← decl.value.mapCodeM (·.simpCase)
|
||||
return { decl with value }
|
||||
|
||||
public def ensureHasDefault (alts : Array (Alt .impure)) : Array (Alt .impure) :=
|
||||
if alts.any (· matches .default ..) then
|
||||
alts
|
||||
else if alts.size < 2 then
|
||||
alts
|
||||
else
|
||||
let last := alts.back!
|
||||
let alts := alts.pop
|
||||
alts.push (.default last.getCode)
|
||||
|
||||
public def simpCase : Pass :=
|
||||
Pass.mkPerDeclaration `simpCase .impure Decl.simpCase 0
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ module
|
||||
prelude
|
||||
public import Lean.Compiler.LCNF.CompilerM
|
||||
public import Lean.Compiler.LCNF.PassManager
|
||||
import Lean.Compiler.InitAttr
|
||||
|
||||
/-!
|
||||
This module "topologically sorts" an SCC of decls (an SCC of decls in the pipeline may in fact
|
||||
@@ -42,8 +43,11 @@ where
|
||||
if (← get).seen.contains decl.name then
|
||||
return ()
|
||||
|
||||
let env ← getEnv
|
||||
modify fun s => { s with seen := s.seen.insert decl.name }
|
||||
decl.value.forCodeM (·.forM visitConsts)
|
||||
if let some initializer := getBuiltinInitFnNameFor? env decl.name <|> getInitFnNameFor? env decl.name then
|
||||
visitConst initializer
|
||||
modify fun s => { s with order := s.order.push decl }
|
||||
|
||||
visitConsts (code : Code pu) : ToposortM pu Unit := do
|
||||
@@ -51,15 +55,16 @@ where
|
||||
| .let decl _ =>
|
||||
match decl.value with
|
||||
| .const declName .. | .fap declName .. | .pap declName .. =>
|
||||
if let some d := (← read).declsMap[declName]? then
|
||||
process d
|
||||
visitConst declName
|
||||
| _ => return ()
|
||||
| _ => return ()
|
||||
|
||||
visitConst (declName : Name) : ToposortM pu Unit := do
|
||||
if let some d := (← read).declsMap[declName]? then
|
||||
process d
|
||||
|
||||
public def toposortDecls (decls : Array (Decl pu)) : CompilerM (Array (Decl pu)) := do
|
||||
let (externDecls, otherDecls) := decls.partition (fun decl => decl.value matches .extern ..)
|
||||
let otherDecls ← toposort otherDecls
|
||||
return externDecls ++ otherDecls
|
||||
toposort decls
|
||||
|
||||
public def toposortPass : Pass where
|
||||
phase := .impure
|
||||
|
||||
@@ -10,7 +10,7 @@ import Lean.Elab.Frontend
|
||||
import Lean.Elab.ParseImportsFast
|
||||
import Lean.Server.Watchdog
|
||||
import Lean.Server.FileWorker
|
||||
import Lean.Compiler.IR.EmitC
|
||||
import Lean.Compiler.LCNF.EmitC
|
||||
import Init.System.Platform
|
||||
|
||||
/- Lean companion to `shell.cpp` -/
|
||||
@@ -545,7 +545,8 @@ def shellMain (args : List String) (opts : ShellOptions) : IO UInt32 := do
|
||||
| IO.eprintln s!"failed to create '{c}'"
|
||||
return 1
|
||||
profileitIO "C code generation" opts.leanOpts do
|
||||
let data ← IR.emitC env mainModuleName
|
||||
let data ← Compiler.LCNF.emitC mainModuleName
|
||||
|>.toIO' { fileName, fileMap := default } { env }
|
||||
out.write data.toUTF8
|
||||
if let some bc := opts.bcFileName? then
|
||||
initLLVM
|
||||
|
||||
Reference in New Issue
Block a user