1
1
mirror of https://github.com/anoma/juvix.git synced 2024-10-03 19:47:59 +03:00

Add an if instruction to JuvixReg (#2855)

* Closes #2829
* Adds a transformation which converts `br` to `if` when the variable
branched on was assigned in the previous instruction. The transformation
itself doesn't check liveness and doesn't remove the assignment. Dead
code elimination should be run afterwards to remove the assignment.
* For Cairo, it only makes sense to convert `br` to `if` for equality
comparisons against zero. The assignment before `br` will always become
dead after converting `br` to `if`, because we convert to SSA before.
This commit is contained in:
Łukasz Czajka 2024-06-26 19:08:33 +02:00 committed by GitHub
parent 7cfddcf915
commit 4dcbb002fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 421 additions and 115 deletions

View File

@ -41,7 +41,7 @@ repos:
types_or: [json]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v17.0.2
rev: v18.1.4
hooks:
- id: clang-format
files: runtime/.+\.(c|h)$

View File

@ -33,10 +33,9 @@
DECL_TAIL_APPLY_3; \
juvix_program_start:
#define JUVIX_EPILOGUE \
juvix_program_end: \
STACK_POPT; \
IO_INTERPRET; \
#define JUVIX_EPILOGUE \
juvix_program_end : STACK_POPT; \
IO_INTERPRET; \
io_print_toplevel(juvix_result);
// Temporary / local vars
@ -45,9 +44,7 @@
// Begin a function definition. `max_stack` is the maximum stack allocation in
// the function.
#define JUVIX_FUNCTION(label, max_stack) \
label: \
STACK_ENTER((max_stack))
#define JUVIX_FUNCTION(label, max_stack) label : STACK_ENTER((max_stack))
/*
Macro sequence for function definition:
@ -67,8 +64,7 @@ closure_label:
*/
// Begin a function with no stack allocation.
#define JUVIX_FUNCTION_NS(label) \
label:
#define JUVIX_FUNCTION_NS(label) label:
#define JUVIX_INT_ADD(var0, var1, var2) (var0 = smallint_add(var1, var2))
#define JUVIX_INT_SUB(var0, var1, var2) (var0 = smallint_sub(var1, var2))
@ -83,6 +79,10 @@ closure_label:
#define JUVIX_VAL_EQ(var0, var1, var2) \
(var0 = make_bool(juvix_equal(var1, var2)))
#define JUVIX_BOOL_INT_LT(var1, var2) (smallint_lt(var1, var2))
#define JUVIX_BOOL_INT_LE(var1, var2) (smallint_le(var1, var2))
#define JUVIX_BOOL_VAL_EQ(var1, var2) (make_bool(juvix_equal(var1, var2)))
#define JUVIX_STR_CONCAT(var0, var1, var2) CONCAT_CSTRINGS(var0, var1, var2)
#define JUVIX_STR_TO_INT(var0, var1) \

View File

@ -85,7 +85,8 @@ static inline void *palign(void *ptr, uintptr_t alignment) {
return (void *)align((uintptr_t)ptr, alignment);
}
// `y` must be a power of 2
#define ASSERT_ALIGNED(x, y) ASSERT(((uintptr_t)(x) & ((uintptr_t)(y)-1)) == 0)
#define ASSERT_ALIGNED(x, y) \
ASSERT(((uintptr_t)(x) & ((uintptr_t)(y) - 1)) == 0)
#if defined(API_LIBC) && defined(DEBUG)
#define LOG(...) fprintf(stderr, __VA_ARGS__)

View File

@ -156,9 +156,9 @@ recurse' sig = go True
fixMemIntOp mem
OpIntMod ->
fixMemIntOp mem
OpIntLt ->
OpBool OpIntLt ->
fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeBool
OpIntLe ->
OpBool OpIntLe ->
fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeBool
OpFieldAdd ->
fixMemFieldOp mem
@ -168,7 +168,7 @@ recurse' sig = go True
fixMemFieldOp mem
OpFieldDiv ->
fixMemFieldOp mem
OpEq ->
OpBool OpEq ->
fixMemBinOp' mem TyDynamic TyDynamic mkTypeBool
OpStrConcat ->
fixMemBinOp' mem TyString TyString TyString

View File

@ -68,11 +68,11 @@ command = do
"mod" ->
return $ mkBinop' loc OpIntMod
"lt" ->
return $ mkBinop' loc OpIntLt
return $ mkBinop' loc (OpBool OpIntLt)
"le" ->
return $ mkBinop' loc OpIntLe
return $ mkBinop' loc (OpBool OpIntLe)
"eq" ->
return $ mkBinop' loc OpEq
return $ mkBinop' loc (OpBool OpEq)
"fadd" ->
return $ mkBinop' loc OpFieldAdd
"fsub" ->

View File

@ -251,6 +251,8 @@ fromRegInstr bNoStack info = \case
fromCallClosures x
Reg.Return x ->
return $ fromReturn x
Reg.If x ->
fromIf x
Reg.Branch x ->
fromBranch x
Reg.Case x ->
@ -271,6 +273,12 @@ fromRegInstr bNoStack info = \case
fromValue _instrBinopArg2
]
getBoolOpMacro :: Reg.BoolOp -> Text
getBoolOpMacro = \case
Reg.OpIntLt -> "JUVIX_BOOL_INT_LT"
Reg.OpIntLe -> "JUVIX_BOOL_INT_LE"
Reg.OpEq -> "JUVIX_BOOL_VAL_EQ"
getBinaryOpMacro :: Reg.BinaryOp -> Text
getBinaryOpMacro = \case
Reg.OpIntAdd -> "JUVIX_INT_ADD"
@ -278,9 +286,9 @@ fromRegInstr bNoStack info = \case
Reg.OpIntMul -> "JUVIX_INT_MUL"
Reg.OpIntDiv -> "JUVIX_INT_DIV"
Reg.OpIntMod -> "JUVIX_INT_MOD"
Reg.OpIntLt -> "JUVIX_INT_LT"
Reg.OpIntLe -> "JUVIX_INT_LE"
Reg.OpEq -> "JUVIX_VAL_EQ"
Reg.OpBool Reg.OpIntLt -> "JUVIX_INT_LT"
Reg.OpBool Reg.OpIntLe -> "JUVIX_INT_LE"
Reg.OpBool Reg.OpEq -> "JUVIX_VAL_EQ"
Reg.OpStrConcat -> "JUVIX_STR_CONCAT"
Reg.OpFieldAdd -> unsupported "field type"
Reg.OpFieldSub -> unsupported "field type"
@ -504,6 +512,26 @@ fromRegInstr bNoStack info = \case
integer argsNum,
ExpressionVar lab
]
fromIf :: Reg.InstrIf -> Sem r [Statement]
fromIf Reg.InstrIf {..} = do
br1 <- fromRegCode bNoStack info _instrIfTrue
br2 <- fromRegCode bNoStack info _instrIfFalse
return
[ StatementIf $
If
{ _ifCondition =
macroCall
"is_true"
[ macroCall
(getBoolOpMacro _instrIfOp)
[ fromValue _instrIfArg1,
fromValue _instrIfArg2
]
],
_ifThen = StatementCompound br1,
_ifElse = Just (StatementCompound br2)
}
]
fromBranch :: Reg.InstrBranch -> Sem r [Statement]
fromBranch Reg.InstrBranch {..} = do

View File

@ -154,6 +154,8 @@ fromRegInstr info = \case
fromCallClosures x
Reg.Return x ->
fromReturn x
Reg.If x ->
fromIf x
Reg.Branch x ->
fromBranch x
Reg.Case x ->
@ -173,16 +175,20 @@ fromRegInstr info = \case
[fromValue _instrBinopArg1, fromValue _instrBinopArg2]
)
getBoolOpName :: Reg.BoolOp -> Text
getBoolOpName = \case
Reg.OpIntLt -> "smallint_lt"
Reg.OpIntLe -> "smallint_le"
Reg.OpEq -> "juvix_equal"
getBinaryOpName :: Reg.BinaryOp -> Text
getBinaryOpName = \case
Reg.OpBool x -> getBoolOpName x
Reg.OpIntAdd -> "smallint_add"
Reg.OpIntSub -> "smallint_sub"
Reg.OpIntMul -> "smallint_mul"
Reg.OpIntDiv -> "smallint_div"
Reg.OpIntMod -> "smallint_mod"
Reg.OpIntLt -> "smallint_lt"
Reg.OpIntLe -> "smallint_le"
Reg.OpEq -> "juvix_equal"
Reg.OpStrConcat -> unsupported "strings"
Reg.OpFieldAdd -> unsupported "field type"
Reg.OpFieldSub -> unsupported "field type"
@ -349,6 +355,23 @@ fromRegInstr info = \case
]
]
fromIf :: Reg.InstrIf -> [Statement]
fromIf Reg.InstrIf {..} =
stmtsIf
( mkCall
"word_to_bool"
[ ( mkCall
(getBoolOpName _instrIfOp)
[fromValue _instrIfArg1, fromValue _instrIfArg2]
)
]
)
br1
br2
where
br1 = fromRegCode info _instrIfTrue
br2 = fromRegCode info _instrIfFalse
fromBranch :: Reg.InstrBranch -> [Statement]
fromBranch Reg.InstrBranch {..} =
stmtsIf (mkCall "word_to_bool" [fromValue _instrBranchValue]) br1 br2

View File

@ -387,9 +387,9 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
goExtraBinop IntDiv res arg1 arg2
Reg.OpIntMod ->
goExtraBinop IntMod res arg1 arg2
Reg.OpIntLt ->
Reg.OpBool Reg.OpIntLt ->
goExtraBinop IntLt res arg1 arg2
Reg.OpIntLe ->
Reg.OpBool Reg.OpIntLe ->
goIntLe res arg1 arg2
Reg.OpFieldAdd ->
goNativeBinop FieldAdd res arg1 arg2
@ -399,7 +399,7 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
goNativeBinop FieldMul res arg1 arg2
Reg.OpFieldDiv ->
goExtraBinop FieldDiv res arg1 arg2
Reg.OpEq ->
Reg.OpBool Reg.OpEq ->
goEq res arg1 arg2
Reg.OpStrConcat ->
unsupported "strings"
@ -527,6 +527,7 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
Reg.Call x -> goCall liveVars x
Reg.TailCall x -> goTailCall x
Reg.Return x -> goReturn x
Reg.If x -> goIf liveVars x
Reg.Branch x -> goBranch liveVars x
Reg.Case x -> goCase liveVars x
@ -573,34 +574,48 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
goAssignApValue _instrReturnValue
output'' Return
goIf :: HashSet Reg.VarRef -> Reg.InstrIf -> Sem r ()
goIf liveVars Reg.InstrIf {..} = case _instrIfOp of
Reg.OpEq
| Reg.ValConst (Reg.ConstInt 0) <- _instrIfArg1 -> do
v <- goValue _instrIfArg2
goBranch' liveVars _instrIfOutVar _instrIfTrue _instrIfFalse v
| Reg.ValConst (Reg.ConstInt 0) <- _instrIfArg2 -> do
v <- goValue _instrIfArg1
goBranch' liveVars _instrIfOutVar _instrIfTrue _instrIfFalse v
_ -> impossible
goBranch :: HashSet Reg.VarRef -> Reg.InstrBranch -> Sem r ()
goBranch liveVars Reg.InstrBranch {..} = do
v <- goValue _instrBranchValue
case v of
Imm c
| c == 0 -> goBlock blts failLab liveVars _instrBranchOutVar _instrBranchTrue
| otherwise -> goBlock blts failLab liveVars _instrBranchOutVar _instrBranchFalse
Ref r -> do
symFalse <- freshSymbol
symEnd <- freshSymbol
let labFalse = LabelRef symFalse Nothing
labEnd = LabelRef symEnd Nothing
output'' $ mkJumpIf (Lab labFalse) r
ap0 <- getAP
vars <- getVars
bltOff <- getBuiltinOffset
goLocalBlock ap0 vars bltOff liveVars _instrBranchOutVar _instrBranchTrue
-- _instrBranchOutVar is Nothing iff the branch returns
when (isJust _instrBranchOutVar) $
output'' (mkJumpRel (Val $ Lab labEnd))
addrFalse <- getPC
registerLabelAddress symFalse addrFalse
output'' $ Label labFalse
goLocalBlock ap0 vars bltOff liveVars _instrBranchOutVar _instrBranchFalse
addrEnd <- getPC
registerLabelAddress symEnd addrEnd
output'' $ Label labEnd
Lab {} -> impossible
goBranch' liveVars _instrBranchOutVar _instrBranchTrue _instrBranchFalse v
goBranch' :: HashSet Reg.VarRef -> Maybe Reg.VarRef -> Reg.Block -> Reg.Block -> Value -> Sem r ()
goBranch' liveVars outVar branchTrue branchFalse = \case
Imm c
| c == 0 -> goBlock blts failLab liveVars outVar branchTrue
| otherwise -> goBlock blts failLab liveVars outVar branchFalse
Ref r -> do
symFalse <- freshSymbol
symEnd <- freshSymbol
let labFalse = LabelRef symFalse Nothing
labEnd = LabelRef symEnd Nothing
output'' $ mkJumpIf (Lab labFalse) r
ap0 <- getAP
vars <- getVars
bltOff <- getBuiltinOffset
goLocalBlock ap0 vars bltOff liveVars outVar branchTrue
-- outVar is Nothing iff the branch returns
when (isJust outVar) $
output'' (mkJumpRel (Val $ Lab labEnd))
addrFalse <- getPC
registerLabelAddress symFalse addrFalse
output'' $ Label labFalse
goLocalBlock ap0 vars bltOff liveVars outVar branchFalse
addrEnd <- getPC
registerLabelAddress symEnd addrEnd
output'' $ Label labEnd
Lab {} -> impossible
goLoad :: Reg.Value -> Offset -> Sem r RValue
goLoad val off = do

View File

@ -5,6 +5,10 @@ import Data.HashSet qualified as HashSet
import Juvix.Compiler.Core.Extra
import Juvix.Compiler.Core.Transformation.Base
-- | Checks if `node` is a case tree such that all leaves are constructor
-- applications, and each constructor `C` matched on in `c` either occurs as a
-- leaf in `node` at most once or the branch body in `c` associated with `C` is
-- an immediate value.
isConstructorTree :: Module -> Case -> Node -> Bool
isConstructorTree md c node = case run $ runFail $ go mempty node of
Just ctrsMap ->
@ -29,6 +33,8 @@ isConstructorTree md c node = case run $ runFail $ go mempty node of
tags' = HashSet.fromList tags
Nothing -> True
-- Returns the map from tags to their number of occurrences in the leaves of
-- the case tree.
go :: (Member Fail r) => HashMap Tag Int -> Node -> Sem r (HashMap Tag Int)
go ctrs = \case
NCtr Constr {..} ->
@ -39,6 +45,9 @@ isConstructorTree md c node = case run $ runFail $ go mempty node of
_ ->
fail
-- | Convert e.g. `case (if A C1 C2) of C1 := X | C2 := Y` to
-- `if A (case C1 of C1 := X | C2 := Y) (case C2 of C1 := X | C2 := Y)`
-- See: https://github.com/anoma/juvix/issues/2440
convertNode :: Module -> Node -> Node
convertNode md = dmap go
where

View File

@ -534,9 +534,9 @@ compile = \case
Tree.OpIntMul -> return (callStdlib StdlibMul args)
Tree.OpIntDiv -> return (callStdlib StdlibDiv args)
Tree.OpIntMod -> return (callStdlib StdlibMod args)
Tree.OpIntLt -> return (callStdlib StdlibLt args)
Tree.OpIntLe -> return (callStdlib StdlibLe args)
Tree.OpEq -> testEq _nodeBinopArg1 _nodeBinopArg2
Tree.OpBool Tree.OpIntLt -> return (callStdlib StdlibLt args)
Tree.OpBool Tree.OpIntLe -> return (callStdlib StdlibLe args)
Tree.OpBool Tree.OpEq -> testEq _nodeBinopArg1 _nodeBinopArg2
Tree.OpStrConcat -> return (callStdlib StdlibCatBytes args)
Tree.OpFieldAdd -> fieldErr
Tree.OpFieldSub -> fieldErr

View File

@ -6,12 +6,15 @@ import Juvix.Prelude
data TransformationId
= IdentityTrans
| CleanupCairo
| Cleanup
| SSA
| InitBranchVars
| CopyPropagation
| ConstantPropagation
| DeadCodeElimination
| BranchToIf
| BranchOnZeroToIf
| OptPhaseMain
| OptPhaseCairo
deriving stock (Data, Bounded, Enum, Show)
@ -31,18 +34,21 @@ toRustTransformations :: [TransformationId]
toRustTransformations = [Cleanup]
toCasmTransformations :: [TransformationId]
toCasmTransformations = [Cleanup, SSA, OptPhaseCairo]
toCasmTransformations = [CleanupCairo, SSA, OptPhaseCairo]
instance TransformationId' TransformationId where
transformationText :: TransformationId -> Text
transformationText = \case
IdentityTrans -> strIdentity
CleanupCairo -> strCleanupCairo
Cleanup -> strCleanup
SSA -> strSSA
InitBranchVars -> strInitBranchVars
CopyPropagation -> strCopyPropagation
ConstantPropagation -> strConstantPropagation
DeadCodeElimination -> strDeadCodeElimination
BranchToIf -> strBranchToIf
BranchOnZeroToIf -> strBranchOnZeroToIf
OptPhaseMain -> strOptPhaseMain
OptPhaseCairo -> strOptPhaseCairo

View File

@ -17,6 +17,9 @@ strIdentity = "identity"
strCleanup :: Text
strCleanup = "cleanup"
strCleanupCairo :: Text
strCleanupCairo = "cleanup-cairo"
strSSA :: Text
strSSA = "ssa"
@ -32,6 +35,12 @@ strConstantPropagation = "constant-propagation"
strDeadCodeElimination :: Text
strDeadCodeElimination = "dead-code"
strBranchToIf :: Text
strBranchToIf = "br-to-if"
strBranchOnZeroToIf :: Text
strBranchOnZeroToIf = "brz-to-if"
strOptPhaseMain :: Text
strOptPhaseMain = "opt-main"

View File

@ -1,5 +1,6 @@
module Juvix.Compiler.Reg.Extra.Base where
import Data.HashSet qualified as HashSet
import Juvix.Compiler.Reg.Language
getResultVar :: Instruction -> Maybe VarRef
@ -30,6 +31,7 @@ setResultVar instr vref = case instr of
getOutVar :: Instruction -> Maybe VarRef
getOutVar = \case
If x -> x ^. instrIfOutVar
Branch x -> x ^. instrBranchOutVar
Case x -> x ^. instrCaseOutVar
_ -> Nothing
@ -48,6 +50,7 @@ overValueRefs'' f = \case
TailCall x -> TailCall <$> goTailCall x
TailCallClosures x -> TailCallClosures <$> goTailCallClosures x
Return x -> Return <$> goReturn x
If x -> If <$> goIf x
Branch x -> Branch <$> goBranch x
Case x -> Case <$> goCase x
Trace x -> Trace <$> goTrace x
@ -162,6 +165,9 @@ overValueRefs'' f = \case
goReturn :: InstrReturn -> m InstrReturn
goReturn = overM instrReturnValue goValue
goIf :: InstrIf -> m InstrIf
goIf = overM instrIfArg1 goValue >=> overM instrIfArg2 goValue
goBranch :: InstrBranch -> m InstrBranch
goBranch = overM instrBranchValue goValue
@ -199,3 +205,18 @@ updateLiveVars' f = \case
updateLiveVars :: (VarRef -> VarRef) -> Instruction -> Instruction
updateLiveVars f = updateLiveVars' (Just . f)
updateInstrLiveVars :: Instruction -> HashSet VarRef -> HashSet VarRef
updateInstrLiveVars instr liveVars =
HashSet.union
(maybe liveVars (`HashSet.delete` liveVars) (getResultVar instr))
(HashSet.fromList (getValueRefs instr))
computeBackwardLiveVars :: Instruction -> HashSet VarRef -> [HashSet VarRef] -> HashSet VarRef
computeBackwardLiveVars instr live lives = case instr of
If {} -> ulives
Branch {} -> ulives
Case {} -> ulives
_ -> live
where
ulives = HashSet.unions lives

View File

@ -13,6 +13,8 @@ overSubBlocks f block = block'
Call x -> Call x
TailCall x -> TailCall x
Return x -> Return x
If x ->
If $ over instrIfTrue f $ over instrIfFalse f x
Branch x ->
Branch $ over instrBranchTrue f $ over instrBranchFalse f x
Case x ->
@ -29,6 +31,8 @@ getSubBlocks block = maybe [] goFinal (block ^. blockFinal)
Call {} -> []
TailCall {} -> []
Return {} -> []
If x ->
[x ^. instrIfTrue, x ^. instrIfFalse]
Branch x ->
[x ^. instrBranchTrue, x ^. instrBranchFalse]
Case x ->
@ -55,6 +59,7 @@ getOutVar :: FinalInstruction -> Maybe VarRef
getOutVar = \case
Call x -> Just $ x ^. instrCallResult
ExtendClosure x -> Just $ x ^. instrExtendClosureResult
If x -> x ^. instrIfOutVar
Branch x -> x ^. instrBranchOutVar
Case x -> x ^. instrCaseOutVar
TailCall {} -> Nothing
@ -110,6 +115,7 @@ getValueRefs' = \case
Call x -> goCall x
TailCall x -> goTailCall x
Return x -> goReturn x
If x -> goIf x
Branch x -> goBranch x
Case x -> goCase x
where
@ -132,6 +138,9 @@ getValueRefs' = \case
goReturn :: InstrReturn -> [VarRef]
goReturn InstrReturn {..} = getValueRefs'' _instrReturnValue
goIf :: InstrIf -> [VarRef]
goIf InstrIf {..} = getValueRefs'' _instrIfArg1 ++ getValueRefs'' _instrIfArg2
goBranch :: InstrBranch -> [VarRef]
goBranch InstrBranch {..} = getValueRefs'' _instrBranchValue

View File

@ -44,6 +44,10 @@ computeMaxStackHeight lims = maximum . map go
+ lims
^. limitsDispatchStackSize
Return {} -> 0
If InstrIf {..} ->
max
(computeMaxStackHeight lims _instrIfTrue)
(computeMaxStackHeight lims _instrIfFalse)
Branch InstrBranch {..} ->
max
(computeMaxStackHeight lims _instrBranchTrue)
@ -83,6 +87,10 @@ computeMaxCallClosuresArgsNum = maximum . map go
TailCallClosures InstrTailCallClosures {..} ->
length _instrTailCallClosuresArgs
Return {} -> 0
If InstrIf {..} ->
max
(computeMaxCallClosuresArgsNum _instrIfTrue)
(computeMaxCallClosuresArgsNum _instrIfFalse)
Branch InstrBranch {..} ->
max
(computeMaxCallClosuresArgsNum _instrBranchTrue)
@ -135,6 +143,11 @@ computeStringMap strs = snd . run . execState (HashMap.size strs, strs) . mapM g
mapM_ goVal _instrTailCallClosuresArgs
Return InstrReturn {..} ->
goVal _instrReturnValue
If InstrIf {..} -> do
goVal _instrIfArg1
goVal _instrIfArg2
mapM_ go _instrIfTrue
mapM_ go _instrIfFalse
Branch InstrBranch {..} -> do
goVal _instrBranchValue
mapM_ go _instrBranchTrue
@ -179,6 +192,10 @@ computeLocalVarsNum = maximum . map go
CallClosures InstrCallClosures {..} -> goVarRef _instrCallClosuresResult
TailCallClosures {} -> 0
Return {} -> 0
If InstrIf {..} ->
max
(computeLocalVarsNum _instrIfTrue)
(computeLocalVarsNum _instrIfFalse)
Branch InstrBranch {..} ->
max
(computeLocalVarsNum _instrBranchTrue)

View File

@ -35,6 +35,11 @@ recurseF sig c = \case
(c0, i0) <- (sig ^. forwardFun) i c
(c', i') <-
case i0 of
If x@InstrIf {..} -> do
(c1, is1) <- recurseF sig c0 _instrIfTrue
(c2, is2) <- recurseF sig c0 _instrIfFalse
let i' = If x {_instrIfTrue = is1, _instrIfFalse = is2}
return $ (sig ^. forwardCombine) i' (c1 :| [c2])
Branch x@InstrBranch {..} -> do
(c1, is1) <- recurseF sig c0 _instrBranchTrue
(c2, is2) <- recurseF sig c0 _instrBranchFalse
@ -70,6 +75,10 @@ recurseB sig a = \case
let a0 = (sig ^. backwardAdjust) a'
(as, i') <-
case i of
If x@InstrIf {..} -> do
(a1, is1) <- recurseB sig a0 _instrIfTrue
(a2, is2) <- recurseB sig a0 _instrIfFalse
return ([a1, a2], If x {_instrIfTrue = is1, _instrIfFalse = is2})
Branch x@InstrBranch {..} -> do
(a1, is1) <- recurseB sig a0 _instrBranchTrue
(a2, is2) <- recurseB sig a0 _instrBranchFalse

View File

@ -60,6 +60,7 @@ runFunction hout infoTable args0 info0 = do
CallClosures x -> goCallClosures args tmps instrs x
TailCallClosures x -> goTailCallClosures args tmps instrs x
Return x -> goReturn args tmps instrs x
If x -> goIf args tmps instrs x
Branch x -> goBranch args tmps instrs x
Case x -> goCase args tmps instrs x
Block x -> goBlock args tmps instrs x
@ -261,6 +262,17 @@ runFunction hout infoTable args0 info0 = do
| otherwise =
throwRunError "return not in tail position" Nothing
goIf :: Args -> Vars s -> Code -> InstrIf -> ST s Val
goIf args tmps instrs InstrIf {..} = do
arg1 <- readValue args tmps _instrIfArg1
arg2 <- readValue args tmps _instrIfArg2
let val = binop (OpBool _instrIfOp) arg1 arg2
r <- case val of
ValBool True -> go args tmps _instrIfTrue
ValBool False -> go args tmps _instrIfFalse
_ -> throwRunError "expected a boolean" Nothing
goNext args tmps r instrs
goBranch :: Args -> Vars s -> Code -> InstrBranch -> ST s Val
goBranch args tmps instrs InstrBranch {..} = do
val <- readValue args tmps _instrBranchValue

View File

@ -31,6 +31,7 @@ import Juvix.Data.Keyword.All
kwFieldDiv,
kwFieldMul,
kwFieldSub,
kwIf,
kwLe_,
kwLive,
kwLt_,
@ -70,6 +71,7 @@ allKeywords =
kwStrcat,
kwEcOp,
kwEq,
kwIf,
kwShow,
kwAtoi,
kwTrace,

View File

@ -21,7 +21,8 @@ data Instruction
| TailCallClosures InstrTailCallClosures
| Return InstrReturn
| ----
Branch InstrBranch
If InstrIf
| Branch InstrBranch
| Case InstrCase
| ----
Trace InstrTrace
@ -54,6 +55,8 @@ data InstrTailCallClosures = InstrTailCallClosures
}
deriving stock (Eq)
type InstrIf = InstrIf' Code
type InstrBranch = InstrBranch' Code
type InstrCase = InstrCase' Code

View File

@ -30,9 +30,12 @@ data FinalInstruction
| Call InstrCall
| TailCall InstrTailCall
| Return InstrReturn
| If InstrIf
| Branch InstrBranch
| Case InstrCase
type InstrIf = InstrIf' Block
type InstrBranch = InstrBranch' Block
type InstrCase = InstrCase' Block

View File

@ -139,6 +139,18 @@ newtype InstrReturn = InstrReturn
}
deriving stock (Eq)
data InstrIf' a = InstrIf
{ _instrIfOp :: BoolOp,
_instrIfArg1 :: Value,
_instrIfArg2 :: Value,
_instrIfTrue :: a,
_instrIfFalse :: a,
-- | Output variable storing the result (corresponds to the top of the value
-- stack in JuvixAsm after executing the branches)
_instrIfOutVar :: Maybe VarRef
}
deriving stock (Eq, Functor)
data InstrBranch' a = InstrBranch
{ _instrBranchValue :: Value,
_instrBranchTrue :: a,
@ -180,6 +192,7 @@ makeLenses ''InstrExtendClosure
makeLenses ''InstrReturn
makeLenses ''InstrTailCall
makeLenses ''InstrCall
makeLenses ''InstrIf'
makeLenses ''InstrBranch'
makeLenses ''InstrCase'
makeLenses ''CaseBranch'

View File

@ -196,6 +196,30 @@ instance PrettyCode InstrReturn where
val <- ppCode _instrReturnValue
return $ primitive Str.ret <+> val
instance PrettyCode InstrIf where
ppCode InstrIf {..} = do
op <- Tree.ppCode _instrIfOp
arg1 <- ppCode _instrIfArg1
arg2 <- ppCode _instrIfArg2
br1 <- ppCodeCode _instrIfTrue
br2 <- ppCodeCode _instrIfFalse
var <- ppOutVar _instrIfOutVar
return $
primitive Str.if_
<+> op
<+> arg1
<+> arg2
<> var
<+> braces'
( constr Str.true_ <> colon
<+> braces' br1
<> semi
<> line
<> constr Str.false_
<> colon
<+> braces' br2 <> semi
)
instance PrettyCode InstrBranch where
ppCode InstrBranch {..} = do
val <- ppCode _instrBranchValue
@ -259,6 +283,7 @@ instance PrettyCode Instruction where
CallClosures x -> ppCode x
TailCallClosures x -> ppCode x
Return x -> ppCode x
If x -> ppCode x
Branch x -> ppCode x
Case x -> ppCode x
Block x -> ppCode x

View File

@ -10,6 +10,7 @@ import Juvix.Compiler.Reg.Transformation.Base
import Juvix.Compiler.Reg.Transformation.Cleanup
import Juvix.Compiler.Reg.Transformation.IdentityTrans
import Juvix.Compiler.Reg.Transformation.InitBranchVars
import Juvix.Compiler.Reg.Transformation.Optimize.BranchToIf
import Juvix.Compiler.Reg.Transformation.Optimize.ConstantPropagation
import Juvix.Compiler.Reg.Transformation.Optimize.CopyPropagation
import Juvix.Compiler.Reg.Transformation.Optimize.DeadCodeElimination
@ -24,10 +25,13 @@ applyTransformations ts tbl = foldM (flip appTrans) tbl ts
appTrans = \case
IdentityTrans -> return . identity
Cleanup -> return . cleanup
CleanupCairo -> return . cleanup' True
SSA -> return . computeSSA
InitBranchVars -> return . initBranchVars
CopyPropagation -> return . copyPropagate
ConstantPropagation -> return . constantPropagate
DeadCodeElimination -> return . removeDeadAssignments
BranchToIf -> return . convertBranchToIf
BranchOnZeroToIf -> return . convertBranchOnZeroToIf
OptPhaseMain -> Phase.Main.optimize
OptPhaseCairo -> Phase.Cairo.optimize

View File

@ -1,11 +1,12 @@
module Juvix.Compiler.Reg.Transformation.Cleanup where
import Juvix.Compiler.Reg.Extra.Base (updateLiveVars')
import Juvix.Compiler.Reg.Extra.Recursors
import Juvix.Compiler.Reg.Transformation.Base
import Juvix.Compiler.Tree.Extra.Rep
cleanup :: InfoTable -> InfoTable
cleanup tab = mapT (const (cmap go)) tab
cleanup' :: Bool -> InfoTable -> InfoTable
cleanup' bCairo tab = mapT (const (cmap go)) tab
where
go :: Code -> Code
go = \case
@ -17,4 +18,9 @@ cleanup tab = mapT (const (cmap go)) tab
_caseBranchCode ++ is
[] ->
fromJust _instrCaseDefault ++ is
Prealloc {} : is | bCairo -> is
i : is | bCairo -> updateLiveVars' (const Nothing) i : is
is -> is
cleanup :: InfoTable -> InfoTable
cleanup = cleanup' False

View File

@ -0,0 +1,52 @@
module Juvix.Compiler.Reg.Transformation.Optimize.BranchToIf where
import Juvix.Compiler.Reg.Extra
import Juvix.Compiler.Reg.Transformation.Base
convertBranchToIf' :: (BoolOp -> Value -> Value -> Bool) -> InfoTable -> InfoTable
convertBranchToIf' f = mapT (const goFun)
where
goFun :: Code -> Code
goFun =
snd
. runIdentity
. recurseB
BackwardRecursorSig
{ _backwardFun = \is () _ -> return ((), go is),
_backwardAdjust = id
}
mempty
go :: Code -> Code
go = \case
binop@(Binop InstrBinop {..}) : Branch InstrBranch {..} : is'
| OpBool op <- _instrBinopOpcode,
f op _instrBinopArg1 _instrBinopArg2,
VRef r <- _instrBranchValue,
r == _instrBinopResult,
r `notElem` getValueRefs binop ->
binop
: If
InstrIf
{ _instrIfOp = op,
_instrIfArg1 = _instrBinopArg1,
_instrIfArg2 = _instrBinopArg2,
_instrIfOutVar = _instrBranchOutVar,
_instrIfTrue = _instrBranchTrue,
_instrIfFalse = _instrBranchFalse
}
: is'
is -> is
convertBranchToIf :: InfoTable -> InfoTable
convertBranchToIf = convertBranchToIf' (\_ _ _ -> True)
convertBranchOnZeroToIf :: InfoTable -> InfoTable
convertBranchOnZeroToIf = convertBranchToIf' check
where
check :: BoolOp -> Value -> Value -> Bool
check op arg1 arg2 = case op of
OpEq
| ValConst (ConstInt 0) <- arg1 -> True
| ValConst (ConstInt 0) <- arg2 -> True
_ -> False

View File

@ -44,6 +44,7 @@ copyPropagate = mapT (const goFun)
where
mpv = combineMaps mpvs
instr' = case instr of
If x -> If $ over instrIfOutVar (fmap (adjustVarRef mpv)) x
Branch x -> Branch $ over instrBranchOutVar (fmap (adjustVarRef mpv)) x
Case x -> Case $ over instrCaseOutVar (fmap (adjustVarRef mpv)) x
_ -> impossible

View File

@ -32,14 +32,7 @@ removeDeadAssignments = mapT (const goFun)
_ ->
(liveVars', instr : is')
where
liveVars' =
HashSet.union
(maybe liveVars (`HashSet.delete` liveVars) (getResultVar instr))
(HashSet.fromList (getValueRefs instr))
liveVars = case instr of
Branch {} -> ulives
Case {} -> ulives
_ -> live
ulives = HashSet.unions lives
liveVars' = updateInstrLiveVars instr liveVars
liveVars = computeBackwardLiveVars instr live lives
[] ->
(live, [])

View File

@ -1,7 +1,14 @@
module Juvix.Compiler.Reg.Transformation.Optimize.Phase.Cairo where
import Juvix.Compiler.Reg.Transformation.Base
import Juvix.Compiler.Reg.Transformation.Optimize.BranchToIf
import Juvix.Compiler.Reg.Transformation.Optimize.DeadCodeElimination
import Juvix.Compiler.Reg.Transformation.Optimize.Phase.Main qualified as Main
optimize :: (Member (Reader Options) r) => InfoTable -> Sem r InfoTable
optimize = withOptimizationLevel 1 Main.optimize
optimize =
withOptimizationLevel 1 $
Main.optimize
>=> return
. removeDeadAssignments
. convertBranchOnZeroToIf

View File

@ -25,6 +25,7 @@ fromReg = over infoFunctions (fmap (over functionCode goCode))
Reg.Call x -> mkBlock (Call x)
Reg.TailCall x -> mkBlock (TailCall x)
Reg.Return x -> mkBlock (Return x)
Reg.If x -> mkBlock (If (fmap goCode x))
Reg.Branch x -> mkBlock (Branch (fmap goCode x))
Reg.Case x -> mkBlock (Case (fmap goCode x))
Reg.CallClosures {} -> impossible

View File

@ -60,6 +60,7 @@ instruction =
<|> (TailCall <$> instrTailCall)
<|> (TailCallClosures <$> instrTailCallClosures)
<|> (Return <$> instrReturn)
<|> (If <$> instrIf)
<|> (Branch <$> instrBranch)
<|> (Case <$> instrCase)
<|> (Block <$> instrBlock)
@ -94,9 +95,9 @@ instrBinop vref =
<|> parseBinaryOp kwMul_ OpIntMul vref
<|> parseBinaryOp kwDiv_ OpIntDiv vref
<|> parseBinaryOp kwMod_ OpIntMod vref
<|> parseBinaryOp kwLt_ OpIntLt vref
<|> parseBinaryOp kwLe_ OpIntLe vref
<|> parseBinaryOp kwEq_ OpEq vref
<|> parseBinaryOp kwLt_ (OpBool OpIntLt) vref
<|> parseBinaryOp kwLe_ (OpBool OpIntLe) vref
<|> parseBinaryOp kwEq_ (OpBool OpEq) vref
<|> parseBinaryOp kwFieldAdd OpFieldAdd vref
<|> parseBinaryOp kwFieldSub OpFieldSub vref
<|> parseBinaryOp kwFieldMul OpFieldMul vref
@ -367,6 +368,39 @@ instrReturn = do
{ _instrReturnValue = val
}
parseBoolOp :: ParsecS r BoolOp
parseBoolOp =
(kw kwLt_ >> return OpIntLt)
<|> (kw kwLe_ >> return OpIntLe)
<|> (kw kwEq_ >> return OpEq)
instrIf ::
(Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) =>
ParsecS r InstrIf
instrIf = do
kw kwIf
op <- parseBoolOp
arg1 <- value
arg2 <- value
var <- optional outVar
(br1, br2) <- braces $ do
symbol "true:"
br1 <- braces parseCode
kw delimSemicolon
symbol "false:"
br2 <- braces parseCode
kw delimSemicolon
return (br1, br2)
return
InstrIf
{ _instrIfOp = op,
_instrIfArg1 = arg1,
_instrIfArg2 = arg2,
_instrIfTrue = br1,
_instrIfFalse = br2,
_instrIfOutVar = var
}
instrBranch ::
(Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) =>
ParsecS r InstrBranch

View File

@ -22,8 +22,8 @@ evalBinop op arg1 arg2 = case op of
OpIntMod
| arg2 == ValInteger 0 -> Left "division by zero"
| otherwise -> goIntBinop rem arg1 arg2
OpIntLe -> goIntCmpBinop (<=) arg1 arg2
OpIntLt -> goIntCmpBinop (<) arg1 arg2
OpBool OpIntLe -> goIntCmpBinop (<=) arg1 arg2
OpBool OpIntLt -> goIntCmpBinop (<) arg1 arg2
OpFieldAdd -> goFieldBinop fieldAdd arg1 arg2
OpFieldSub -> goFieldBinop fieldSub arg1 arg2
OpFieldMul -> goFieldBinop fieldMul arg1 arg2
@ -31,7 +31,7 @@ evalBinop op arg1 arg2 = case op of
ValField arg2'
| fieldToInteger arg2' == 0 -> Left "division by zero"
_ -> goFieldBinop fieldDiv arg1 arg2
OpEq
OpBool OpEq
| arg1 == arg2 -> Right $ ValBool True
| otherwise -> Right $ ValBool False
OpStrConcat -> goStrConcat arg1 arg2

View File

@ -2,19 +2,23 @@ module Juvix.Compiler.Tree.Language.Builtins where
import Juvix.Prelude
data BoolOp
= OpIntLt
| OpIntLe
| OpEq
deriving stock (Eq)
data BinaryOp
= OpIntAdd
= OpBool BoolOp
| OpIntAdd
| OpIntSub
| OpIntMul
| OpIntDiv
| OpIntMod
| OpIntLt
| OpIntLe
| OpFieldAdd
| OpFieldSub
| OpFieldMul
| OpFieldDiv
| OpEq
| OpStrConcat
deriving stock (Eq)
@ -25,13 +29,13 @@ isCommutative = \case
OpIntMul -> True
OpIntDiv -> False
OpIntMod -> False
OpIntLt -> False
OpIntLe -> False
OpBool OpIntLt -> False
OpBool OpIntLe -> False
OpFieldAdd -> True
OpFieldSub -> False
OpFieldMul -> True
OpFieldDiv -> False
OpEq -> True
OpBool OpEq -> True
OpStrConcat -> False
data UnaryOp

View File

@ -198,21 +198,27 @@ instance PrettyCode Constant where
ConstVoid {} ->
return $ annotate (AnnKind KNameConstructor) Str.void
instance PrettyCode BinaryOp where
instance PrettyCode BoolOp where
ppCode op = return $ primitive $ case op of
OpIntAdd -> Str.instrAdd
OpIntSub -> Str.instrSub
OpIntMul -> Str.instrMul
OpIntDiv -> Str.instrDiv
OpIntMod -> Str.instrMod
OpIntLt -> Str.instrLt
OpIntLe -> Str.instrLe
OpFieldAdd -> Str.fadd
OpFieldSub -> Str.fsub
OpFieldMul -> Str.fmul
OpFieldDiv -> Str.fdiv
OpEq -> Str.instrEq
OpStrConcat -> Str.instrStrConcat
instance PrettyCode BinaryOp where
ppCode = \case
OpBool x -> ppCode x
op ->
return $ primitive $ case op of
OpIntAdd -> Str.instrAdd
OpIntSub -> Str.instrSub
OpIntMul -> Str.instrMul
OpIntDiv -> Str.instrDiv
OpIntMod -> Str.instrMod
OpFieldAdd -> Str.fadd
OpFieldSub -> Str.fsub
OpFieldMul -> Str.fmul
OpFieldDiv -> Str.fdiv
OpStrConcat -> Str.instrStrConcat
instance PrettyCode BinaryOpcode where
ppCode = \case

View File

@ -52,13 +52,13 @@ inferType tab funInfo = goInfer mempty
OpIntMul -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger
OpIntDiv -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger
OpIntMod -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger
OpIntLt -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool
OpIntLe -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool
OpBool OpIntLt -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool
OpBool OpIntLe -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool
OpFieldAdd -> checkBinop TyField TyField TyField
OpFieldSub -> checkBinop TyField TyField TyField
OpFieldMul -> checkBinop TyField TyField TyField
OpFieldDiv -> checkBinop TyField TyField TyField
OpEq -> checkBinop TyDynamic TyDynamic mkTypeBool
OpBool OpEq -> checkBinop TyDynamic TyDynamic mkTypeBool
OpStrConcat -> checkBinop TyString TyString TyString
goUnop :: BinderList Type -> NodeUnop -> Sem r Type

View File

@ -283,13 +283,13 @@ genCode infoTable fi =
Core.OpIntMul -> PrimBinop OpIntMul
Core.OpIntDiv -> PrimBinop OpIntDiv
Core.OpIntMod -> PrimBinop OpIntMod
Core.OpIntLt -> PrimBinop OpIntLt
Core.OpIntLe -> PrimBinop OpIntLe
Core.OpIntLt -> PrimBinop (OpBool OpIntLt)
Core.OpIntLe -> PrimBinop (OpBool OpIntLe)
Core.OpFieldAdd -> PrimBinop OpFieldAdd
Core.OpFieldSub -> PrimBinop OpFieldSub
Core.OpFieldMul -> PrimBinop OpFieldMul
Core.OpFieldDiv -> PrimBinop OpFieldDiv
Core.OpEq -> PrimBinop OpEq
Core.OpEq -> PrimBinop (OpBool OpEq)
Core.OpStrConcat -> PrimBinop OpStrConcat
Core.OpSeq -> OpSeq
_ -> impossible

View File

@ -75,13 +75,13 @@ parseBinop =
<|> parseBinaryOp kwMul_ (PrimBinop OpIntMul)
<|> parseBinaryOp kwDiv_ (PrimBinop OpIntDiv)
<|> parseBinaryOp kwMod_ (PrimBinop OpIntMod)
<|> parseBinaryOp kwLt_ (PrimBinop OpIntLt)
<|> parseBinaryOp kwLe_ (PrimBinop OpIntLe)
<|> parseBinaryOp kwLt_ (PrimBinop (OpBool OpIntLt))
<|> parseBinaryOp kwLe_ (PrimBinop (OpBool OpIntLe))
<|> parseBinaryOp kwFieldAdd (PrimBinop OpFieldAdd)
<|> parseBinaryOp kwFieldSub (PrimBinop OpFieldSub)
<|> parseBinaryOp kwFieldMul (PrimBinop OpFieldMul)
<|> parseBinaryOp kwFieldDiv (PrimBinop OpFieldDiv)
<|> parseBinaryOp kwEq_ (PrimBinop OpEq)
<|> parseBinaryOp kwEq_ (PrimBinop (OpBool OpEq))
<|> parseBinaryOp kwStrcat (PrimBinop OpStrConcat)
<|> parseBinaryOp kwSeq_ OpSeq

View File

@ -110,8 +110,7 @@ juvix_closure_plus:
JUVIX_BRANCH(
TMP(0),
{
PREALLOC(
2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
PREALLOC(2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
ALLOC_CONSTR_PAIR(juvix_result);
FST(juvix_result) = ARG(0);
SND(juvix_result) = CONSTR_NIL;

View File

@ -219,8 +219,7 @@ juvix_closure_app_plus:
JUVIX_BRANCH(
TMP(0),
{
PREALLOC(
2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
PREALLOC(2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
ALLOC_CONSTR_PAIR(juvix_result);
FST(juvix_result) = ARG(0);
SND(juvix_result) = CONSTR_NIL;

View File

@ -253,8 +253,7 @@ juvix_closure_app_plus:
JUVIX_BRANCH(
TMP(0),
{
PREALLOC(
2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
PREALLOC(2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
ALLOC_CONSTR_PAIR(juvix_result);
FST(juvix_result) = ARG(0);
SND(juvix_result) = CONSTR_NIL;
@ -266,8 +265,7 @@ juvix_closure_app_plus:
ARG(0) = TMP(0);
CALL(0, gen, juvix_label_gen_1);
STACK_POP(ARG(0));
PREALLOC(
2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
PREALLOC(2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
ALLOC_CONSTR_PAIR(TMP(0));
FST(TMP(0)) = ARG(0);
SND(TMP(0)) = juvix_result;

View File

@ -29,8 +29,7 @@ juvix_closure_print:
ARG(0) = CARG(0);
JUVIX_FUNCTION(juvix_function_print, 1);
{
PREALLOC(
2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
PREALLOC(2, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
ALLOC_CONSTR_BOXED(juvix_result, UID_WRITE, 1);
CONSTR_ARG(juvix_result, 0) = ARG(0);
RETURN;

View File

@ -124,8 +124,7 @@ int main() {
{ CARG(juvix_closure_nargs) = juvix_result; });
CALL_CLOSURE(TMP(0), juvix_label_7);
STACK_POP(TMP(1));
PREALLOC(
3, { STACK_PUSH(TMP(1)); }, { STACK_POP(TMP(1)); });
PREALLOC(3, { STACK_PUSH(TMP(1)); }, { STACK_POP(TMP(1)); });
ALLOC_CONSTR_BOXED(TMP(0), UID_CONS, 2);
CONSTR_ARG(TMP(0), 0) = juvix_result;
CONSTR_ARG(TMP(0), 1) = TMP(1);
@ -158,8 +157,7 @@ juvix_closure_add_one:
ARG(0) = TMP(0);
CALL(0, juvix_function_gen, juvix_label_gen_1);
STACK_POP(ARG(0));
PREALLOC(
3, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
PREALLOC(3, { STACK_PUSH(ARG(0)); }, { STACK_POP(ARG(0)); });
ALLOC_CONSTR_BOXED(TMP(0), UID_CONS, 2);
CONSTR_ARG(TMP(0), 0) = ARG(0);
CONSTR_ARG(TMP(0), 1) = juvix_result;