/- Copyright (c) 2022 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ import Lean.Compiler.LCNF.ReduceJpArity import Lean.Compiler.LCNF.Renaming import Lean.Compiler.LCNF.Simp.Basic import Lean.Compiler.LCNF.Simp.FunDeclInfo import Lean.Compiler.LCNF.Simp.JpCases import Lean.Compiler.LCNF.Simp.Config import Lean.Compiler.LCNF.Simp.InlineCandidate import Lean.Compiler.LCNF.Simp.SimpM import Lean.Compiler.LCNF.Simp.Main import Lean.Compiler.LCNF.Simp.InlineProj import Lean.Compiler.LCNF.Simp.DefaultAlt import Lean.Compiler.LCNF.Simp.SimpValue import Lean.Compiler.LCNF.Simp.Used namespace Lean.Compiler.LCNF open Simp def Decl.simp? (decl : Decl) : SimpM (Option Decl) := do updateFunDeclInfo decl.value traceM `Compiler.simp.inline.info do return m!"{decl.name}:{Format.nest 2 (← (← get).funDeclInfoMap.format)}" traceM `Compiler.simp.step do ppDecl decl let value ← simp decl.value let s ← get let value ← value.applyRenaming s.binderRenaming traceM `Compiler.simp.step.new do return m!"{decl.name} :=\n{← ppCode value}" trace[Compiler.simp.stat] "{decl.name}, size: {value.size}, # visited: {s.visited}, # inline: {s.inline}, # inline local: {s.inlineLocal}" if let some value ← simpJpCases? value then let decl := { decl with value } decl.reduceJpArity else if (← get).simplified then return some { decl with value } else return none partial def Decl.simp (decl : Decl) (config : Config) : CompilerM Decl := do let mut config := config if (← isTemplateLike decl) then let mut inlineDefs := config.inlineDefs /- At the base phase, we don't inline definitions occurring in instances. Reason: we eagerly lambda lift local functions occurring at instances before saving their code at the end of the base phase. The goal is to make them cheap to inline in actual code. By inlining definitions we would be just generating extra work for the lambda lifter. There is an exception: inlineable instances. This is important for auxiliary instances such as ``` @[always_inline] instance : Monad TermElabM := let i := inferInstanceAs (Monad TermElabM); { pure := i.pure, bind := i.bind } ``` by keeping `inlineDefs := true`, we can pre-compute the `pure` and `bind` methods for `TermElabM`. -/ if (← inBasePhase <&&> Meta.isInstance decl.name) then unless decl.inlineable do inlineDefs := false /- We do not eta-expand or inline partial applications in template like code. Recall we don't want to generate code for them. Remark: by eta-expanding partial applications in instances, we also make the simplifier work harder when inlining instance projections. -/ config := { config with etaPoly := false, inlinePartial := false, inlineDefs } go decl config where go (decl : Decl) (config : Config) : CompilerM Decl := do if let some decl ← decl.simp? |>.run { config, declName := decl.name } |>.run' {} |>.run {} then -- TODO: bound number of steps? go decl config else return decl def simp (config : Config := {}) (occurrence : Nat := 0) (phase := Phase.base) : Pass := .mkPerDeclaration `simp (Decl.simp · config) phase (occurrence := occurrence) builtin_initialize registerTraceClass `Compiler.simp (inherited := true) registerTraceClass `Compiler.simp.stat registerTraceClass `Compiler.simp.step registerTraceClass `Compiler.simp.step.new