Compare commits

...

2 Commits

Author SHA1 Message Date
Paul Reichert
f63c803585 test performance implications of implicit_reducible 2026-03-02 10:25:44 +01:00
Leonardo de Moura
3aff8d1c0d refactor: replace isImplicitReducible with Meta.isInstance in shouldInline
This PR replaces the `isImplicitReducible` check in `shouldInline` with
`Meta.isInstance`. At the base phase, we avoid inlining instances tagged
with `[inline]/[always_inline]/[inline_if_reduce]` because their local
functions will be lambda lifted during the base phase. Inlining them
prematurely creates extra work for the lambda lifter and produces many
lambda-lifted closures, which is particularly problematic for the IR
interpreter (causing C++ stack overflows through stub closures).

The previous `isImplicitReducible` check did not capture the original
intent: some `instanceReducible` declarations are not instances. Using
`Meta.isInstance` correctly targets only actual instances. This is
scope-safe because LCNF compilation runs at `addDecl` time, before any
scope changes that could affect instance visibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:46:01 +00:00
4 changed files with 25 additions and 11 deletions

View File

@@ -35,7 +35,7 @@ A `ForIn'` instance for iterators. Its generic membership relation is not easy t
so this is not marked as `instance`. This way, more convenient instances can be built on top of it
or future library improvements will make it more comfortable.
-/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def Iter.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] :
ForIn' n (Iter (α := α) β) β fun it out => it.IsPlausibleIndirectOutput out where
@@ -53,7 +53,7 @@ instance (α : Type w) (β : Type w) (n : Type x → Type x') [Monad n]
/--
An implementation of `for h : ... in ... do ...` notation for partial iterators.
-/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def Iter.Partial.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] :
ForIn' n (Iter.Partial (α := α) β) β fun it out => it.it.IsPlausibleIndirectOutput out where
@@ -71,7 +71,7 @@ instance (α : Type w) (β : Type w) (n : Type x → Type x') [Monad n]
A `ForIn'` instance for iterators that is guaranteed to terminate after finitely many steps.
It is not marked as an instance because the membership predicate is difficult to work with.
-/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def Iter.Total.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] [Finite α Id] :
ForIn' n (Iter.Total (α := α) β) β fun it out => it.it.IsPlausibleIndirectOutput out where

View File

@@ -159,7 +159,7 @@ This is the default implementation of the `IteratorLoop` class.
It simply iterates through the iterator using `IterM.step`. For certain iterators, more efficient
implementations are possible and should be used instead.
-/
@[always_inline, inline, expose]
@[always_inline, inline, expose, implicit_reducible]
def IteratorLoop.defaultImplementation {α : Type w} {m : Type w Type w'} {n : Type x Type x'}
[Monad n] [Iterator α m β] :
IteratorLoop α m n where
@@ -211,7 +211,7 @@ theorem IteratorLoop.wellFounded_of_productive {α β : Type w} {m : Type w →
/--
This `ForIn'`-style loop construct traverses a finite iterator using an `IteratorLoop` instance.
-/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def IteratorLoop.finiteForIn' {m : Type w Type w'} {n : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [Monad n]
(lift : γ δ, (γ n δ) m γ n δ) :
@@ -224,7 +224,7 @@ A `ForIn'` instance for iterators. Its generic membership relation is not easy t
so this is not marked as `instance`. This way, more convenient instances can be built on top of it
or future library improvements will make it more comfortable.
-/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def IterM.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [Monad n]
[MonadLiftT m n] :
@@ -239,7 +239,7 @@ instance IterM.instForInOfIteratorLoop {m : Type w → Type w'} {n : Type w →
instForInOfForIn'
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def IterM.Partial.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n] :
ForIn' n (IterM.Partial (α := α) m β) β fun it out => it.it.IsPlausibleIndirectOutput out where
@@ -247,7 +247,7 @@ def IterM.Partial.instForIn' {m : Type w → Type w'} {n : Type w → Type w''}
haveI := @IterM.instForIn'; forIn' it.it init f
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
@[always_inline, inline, expose, implicit_reducible]
def IterM.Total.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n]
[Finite α m] :

View File

@@ -62,11 +62,24 @@ def inlineCandidate? (e : LetValue .pure) : SimpM (Option InlineCandidateInfo) :
if !decl.inlineIfReduceAttr && decl.recursive then return false
if mustInline then return true
/-
We don't inline instances tagged with `[inline]/[always_inline]/[inline_if_reduce]` at the base phase
We assume that at the base phase these annotations are for the instance methods that have been lambda lifted.
We don't inline instances tagged with `[inline]/[always_inline]/[inline_if_reduce]` at the base phase.
We assume that at the base phase these annotations are for the instance methods that will be lambda lifted during the base phase.
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 their definitions we would be just generating extra work for the lambda lifter.
-/
if ( inBasePhase) then
if ( isImplicitReducible decl.name) then
/-
We claim it is correct to use `Meta.isInstance` because
1. `shouldInline` is called during LCNF compilation, which runs at `addDecl` time
2. Any instance referenced in the code was found by type class resolution during elaboration
3. For TC resolution to find it, the scope was active during elaboration
4. LCNF compilation happens before the scope changes
We don't want to use `isImplicitReducible` because some `instanceReducible` declarations are
**not** instances.
-/
if ( Meta.isInstance decl.name) then
unless decl.name == ``instDecidableEqBool do
/-
TODO: remove this hack after we refactor `Decidable` as suggested by Gabriel.

View File

@@ -1,3 +1,4 @@
// update me
#include "util/options.h"
namespace lean {