Skip to content

Commit

Permalink
Reduce size of compiled Elm code by inlining simple declaration blocks
Browse files Browse the repository at this point in the history
Simplify compiled Elm code by inlining the subset of let-in blocks in which none of the block declarations is referenced more than once.
  • Loading branch information
Viir committed Oct 7, 2024
1 parent b9a3e82 commit 4680af1
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 1 deletion.
14 changes: 14 additions & 0 deletions implement/pine/ElmTime/compile-elm-program/src/Common.elm
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,17 @@ listUniqueHelp remaining accumulator =

else
listUniqueHelp rest (first :: accumulator)


listCount : (a -> Bool) -> List a -> Int
listCount predicate list =
case list of
[] ->
0

first :: rest ->
if predicate first then
1 + listCount predicate rest

else
listCount predicate rest
39 changes: 38 additions & 1 deletion implement/pine/ElmTime/compile-elm-program/src/ElmCompiler.elm
Original file line number Diff line number Diff line change
Expand Up @@ -1451,12 +1451,49 @@ compileElmSyntaxLetBlock stackBefore letBlock =

Ok expression ->
Ok
(DeclarationBlockExpression
(inlineDeclBlockIfSimple
(List.concat letEntries)
expression
)


inlineDeclBlockIfSimple : List ( String, Expression ) -> Expression -> Expression
inlineDeclBlockIfSimple blockDecls expression =
let
aggregateRefs : List String
aggregateRefs =
List.concatMap
(\declExpr ->
FirCompiler.listUnboundReferencesInExpression declExpr []
)
(expression :: List.map Tuple.second blockDecls)

refsBlockDeclMoreThanOnce : Bool
refsBlockDeclMoreThanOnce =
List.any
(\( declName, _ ) ->
Common.listCount
(\ref -> ref == declName)
aggregateRefs
> 1
)
blockDecls
in
{-
There is no need to check for recursive references in the block declarations separately,
because no recursive references can be reachable is the reference count is less than 2.
-}
if refsBlockDeclMoreThanOnce then
DeclarationBlockExpression
blockDecls
expression

else
FirCompiler.inlineLocalReferences
blockDecls
expression


compileElmSyntaxLetDeclaration :
CompilationStack
-> Elm.Syntax.Expression.LetDeclaration
Expand Down
83 changes: 83 additions & 0 deletions implement/pine/ElmTime/compile-elm-program/src/FirCompiler.elm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module FirCompiler exposing
, estimatePineValueSize
, evaluateAsIndependentExpression
, getTransitiveDependencies
, inlineLocalReferences
, listFunctionAppExpressions
, listItemFromIndexExpression
, listItemFromIndexExpression_Pine
Expand Down Expand Up @@ -2434,6 +2435,88 @@ listFunctionAppExpressions expr =
listFunctionAppExpressions argument


inlineLocalReferences : List ( String, Expression ) -> Expression -> Expression
inlineLocalReferences references expression =
case expression of
FunctionApplicationExpression funcExpr args ->
FunctionApplicationExpression
(inlineLocalReferences references funcExpr)
(List.map (inlineLocalReferences references) args)

LiteralExpression _ ->
expression

ListExpression list ->
ListExpression (List.map (inlineLocalReferences references) list)

KernelApplicationExpression funcName argument ->
KernelApplicationExpression funcName (inlineLocalReferences references argument)

ConditionalExpression condition falseBranch trueBranch ->
ConditionalExpression
(inlineLocalReferences references condition)
(inlineLocalReferences references falseBranch)
(inlineLocalReferences references trueBranch)

FunctionExpression params functionBody ->
let
allParamNames =
List.concatMap (List.map Tuple.first) params

referencesForInnerExpression =
{-
Implement shadowing by removing the declarations from the references list.
-}
List.filter
(\( name, _ ) ->
not (List.member name allParamNames)
)
references
in
FunctionExpression
params
(inlineLocalReferences referencesForInnerExpression functionBody)

ReferenceExpression moduleName functionName ->
if moduleName == [] then
case Common.assocListGet functionName references of
Nothing ->
ReferenceExpression moduleName functionName

Just inlinedExpr ->
inlineLocalReferences references inlinedExpr

else
ReferenceExpression moduleName functionName

DeclarationBlockExpression declarations innerExpression ->
let
referencesForInnerExpression =
{-
Implement shadowing by removing the declarations from the references list.
-}
List.filter
(\( name, _ ) ->
not (List.member name (List.map Tuple.first declarations))
)
references
in
DeclarationBlockExpression
(List.map
(\( name, declExpr ) ->
( name, inlineLocalReferences references declExpr )
)
declarations
)
(inlineLocalReferences referencesForInnerExpression innerExpression)

StringTagExpression tag tagged ->
StringTagExpression tag (inlineLocalReferences references tagged)

PineFunctionApplicationExpression pineExpr argExpr ->
PineFunctionApplicationExpression pineExpr (inlineLocalReferences references argExpr)


evaluateAsIndependentExpression : Pine.Expression -> Result String Pine.Value
evaluateAsIndependentExpression expression =
if not (pineExpressionIsIndependent expression) then
Expand Down

0 comments on commit 4680af1

Please sign in to comment.