Skip to content

Commit

Permalink
Generalize backend builtin primitive operations (#2653)
Browse files Browse the repository at this point in the history
* Closes #2651 
* Depends on #2650
  • Loading branch information
lukaszcz authored Feb 26, 2024
1 parent 346b18f commit 6b74d84
Show file tree
Hide file tree
Showing 29 changed files with 458 additions and 763 deletions.
10 changes: 8 additions & 2 deletions src/Juvix/Compiler/Asm/Extra/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ import Juvix.Compiler.Asm.Language
mkInstr :: Instruction -> Command
mkInstr = Instr . CmdInstr emptyInfo

mkBinop :: Opcode -> Command
mkBinop :: BinaryOp -> Command
mkBinop = mkInstr . Binop

mkInstr' :: Maybe Location -> Instruction -> Command
mkInstr' loc = Instr . CmdInstr (CommandInfo loc)

mkBinop' :: Maybe Location -> Opcode -> Command
mkBinop' :: Maybe Location -> BinaryOp -> Command
mkBinop' loc = mkInstr' loc . Binop

mkUnop :: UnaryOp -> Command
mkUnop = mkInstr . Unop

mkUnop' :: Maybe Location -> UnaryOp -> Command
mkUnop' loc = mkInstr' loc . Unop

isFinalInstr :: Instruction -> Bool
isFinalInstr = \case
Return -> True
Expand Down
99 changes: 44 additions & 55 deletions src/Juvix/Compiler/Asm/Extra/Recursors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,10 @@ recurse' sig = go True
fixMemInstr :: Memory -> Instruction -> Sem r Memory
fixMemInstr mem instr =
case instr of
Binop IntAdd ->
fixMemIntOp mem
Binop IntSub ->
fixMemIntOp mem
Binop IntMul ->
fixMemIntOp mem
Binop IntDiv ->
fixMemIntOp mem
Binop IntMod ->
fixMemIntOp mem
Binop IntLt ->
fixMemBinOp mem mkTypeInteger mkTypeInteger mkTypeBool
Binop IntLe ->
fixMemBinOp mem mkTypeInteger mkTypeInteger mkTypeBool
Binop ValEq ->
fixMemBinOp mem TyDynamic TyDynamic mkTypeBool
Binop StrConcat ->
fixMemBinOp mem TyString TyString TyString
ValShow ->
return (pushValueStack TyString (popValueStack 1 mem))
StrToInt -> do
checkValueStack' loc (sig ^. recursorInfoTable) [TyString] mem
return (pushValueStack mkTypeInteger (popValueStack 1 mem))
Binop op ->
fixMemBinop mem op
Unop op ->
fixMemUnop mem op
Push val -> do
ty <- getValueType' loc (sig ^. recursorInfoTable) mem val
return (pushValueStack ty mem)
Expand All @@ -115,12 +96,6 @@ recurse' sig = go True
return mem
Failure ->
return $ pushValueStack TyDynamic (popValueStack 1 mem)
ArgsNum -> do
when (null (mem ^. memoryValueStack)) $
throw $
AsmError loc "empty value stack"
checkFunType (topValueStack' 0 mem)
return $ pushValueStack mkTypeInteger (popValueStack 1 mem)
Prealloc {} ->
return mem
AllocConstr tag -> do
Expand Down Expand Up @@ -157,13 +132,48 @@ recurse' sig = go True
return (dropTempStack mem)

fixMemIntOp :: Memory -> Sem r Memory
fixMemIntOp mem = fixMemBinOp mem mkTypeInteger mkTypeInteger mkTypeInteger
fixMemIntOp mem = fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeInteger

fixMemBinOp :: Memory -> Type -> Type -> Type -> Sem r Memory
fixMemBinOp mem ty0 ty1 rty = do
fixMemBinOp' :: Memory -> Type -> Type -> Type -> Sem r Memory
fixMemBinOp' mem ty0 ty1 rty = do
checkValueStack' loc (sig ^. recursorInfoTable) [ty0, ty1] mem
return $ pushValueStack rty (popValueStack 2 mem)

fixMemBinop :: Memory -> BinaryOp -> Sem r Memory
fixMemBinop mem op = case op of
OpIntAdd ->
fixMemIntOp mem
OpIntSub ->
fixMemIntOp mem
OpIntMul ->
fixMemIntOp mem
OpIntDiv ->
fixMemIntOp mem
OpIntMod ->
fixMemIntOp mem
OpIntLt ->
fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeBool
OpIntLe ->
fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeBool
OpEq ->
fixMemBinOp' mem TyDynamic TyDynamic mkTypeBool
OpStrConcat ->
fixMemBinOp' mem TyString TyString TyString

fixMemUnop :: Memory -> UnaryOp -> Sem r Memory
fixMemUnop mem op = case op of
OpShow ->
return (pushValueStack TyString (popValueStack 1 mem))
OpStrToInt -> do
checkValueStack' loc (sig ^. recursorInfoTable) [TyString] mem
return (pushValueStack mkTypeInteger (popValueStack 1 mem))
OpArgsNum -> do
when (null (mem ^. memoryValueStack)) $
throw $
AsmError loc "empty value stack"
checkFunType (topValueStack' 0 mem)
return $ pushValueStack mkTypeInteger (popValueStack 1 mem)

fixMemExtendClosure :: Memory -> InstrExtendClosure -> Sem r Memory
fixMemExtendClosure mem InstrExtendClosure {..} = do
when (length (mem ^. memoryValueStack) < _extendClosureArgsNum + 1) $
Expand Down Expand Up @@ -363,27 +373,9 @@ recurseS' sig = go True
fixStackInstr :: StackInfo -> Instruction -> Sem r StackInfo
fixStackInstr si instr =
case instr of
Binop IntAdd ->
fixStackBinOp si
Binop IntSub ->
fixStackBinOp si
Binop IntMul ->
fixStackBinOp si
Binop IntDiv ->
fixStackBinOp si
Binop IntMod ->
Binop {} ->
fixStackBinOp si
Binop IntLt ->
fixStackBinOp si
Binop IntLe ->
fixStackBinOp si
Binop ValEq ->
fixStackBinOp si
Binop StrConcat ->
fixStackBinOp si
ValShow ->
return si
StrToInt ->
Unop {} ->
return si
Push {} -> do
return (stackInfoPushValueStack 1 si)
Expand All @@ -395,9 +387,6 @@ recurseS' sig = go True
return si
Failure ->
return si
ArgsNum ->
-- push + pop = nop
return si
Prealloc {} ->
return si
AllocConstr tag -> do
Expand Down
99 changes: 18 additions & 81 deletions src/Juvix/Compiler/Asm/Interpreter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Juvix.Compiler.Asm.Interpreter.Base
import Juvix.Compiler.Asm.Interpreter.Extra
import Juvix.Compiler.Asm.Interpreter.Runtime
import Juvix.Compiler.Asm.Pretty
import Text.Read (readMaybe)
import Juvix.Compiler.Tree.Evaluator.Builtins

-- | Interpret JuvixAsm code for a single function. The returned Val is the
-- value on top of the value stack at exit, i.e., when executing a toplevel
Expand Down Expand Up @@ -73,45 +73,8 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta

goInstr :: (Member Runtime r) => Maybe Location -> Instruction -> Code -> Sem r ()
goInstr loc instr cont = case instr of
Binop IntAdd ->
goIntBinOp (\x y -> ValInteger (x + y)) >> goCode cont
Binop IntSub ->
goIntBinOp (\x y -> ValInteger (x - y)) >> goCode cont
Binop IntMul ->
goIntBinOp (\x y -> ValInteger (x * y)) >> goCode cont
Binop IntDiv -> do
goIntBinOp'
( \x y ->
if
| y == 0 -> runtimeError "division by zero"
| otherwise -> return $ ValInteger (x `quot` y)
)
goCode cont
Binop IntMod -> do
goIntBinOp'
( \x y ->
if
| y == 0 -> runtimeError "division by zero"
| otherwise -> return $ ValInteger (x `rem` y)
)
goCode cont
Binop IntLt ->
goIntBinOp (\x y -> ValBool (x < y)) >> goCode cont
Binop IntLe ->
goIntBinOp (\x y -> ValBool (x <= y)) >> goCode cont
Binop ValEq ->
goBinOp (\x y -> ValBool (x == y)) >> goCode cont
Binop StrConcat ->
goStrBinOp (\x y -> ValString (x <> y)) >> goCode cont
ValShow -> do
v <- popValueStack
pushValueStack (ValString (printVal v))
goCode cont
StrToInt -> do
v <- popValueStack
i <- valToInt v
pushValueStack (ValInteger i)
goCode cont
Binop op -> goBinOp (evalBinop op) >> goCode cont
Unop op -> goUnop (evalUnop infoTable op) >> goCode cont
Push ref -> do
v <- getVal ref
pushValueStack v
Expand All @@ -120,22 +83,14 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta
popValueStack >> goCode cont
Trace -> do
v <- topValueStack
logMessage (printVal v)
logMessage (printValue infoTable v)
goCode cont
Dump -> do
dumpState
goCode cont
Failure -> do
v <- topValueStack
runtimeError $ mappend "failure: " (printVal v)
ArgsNum -> do
v <- popValueStack
case v of
ValClosure cl -> do
let n = lookupFunInfo infoTable (cl ^. closureSymbol) ^. functionArgsNum - length (cl ^. closureArgs)
pushValueStack (ValInteger (toInteger n))
goCode cont
_ -> runtimeError "invalid operation: expected closure on top of value stack"
runtimeError $ mappend "failure: " (printValue infoTable v)
Prealloc {} ->
goCode cont
AllocConstr tag -> do
Expand Down Expand Up @@ -189,26 +144,17 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta
v <- op v1 v2
pushValueStack v

goBinOp :: (Member Runtime r) => (Val -> Val -> Val) -> Sem r ()
goBinOp op = goBinOp' (\x y -> return (op x y))

goIntBinOp' :: (Member Runtime r) => (Integer -> Integer -> Sem r Val) -> Sem r ()
goIntBinOp' op = goBinOp' $ \v1 v2 ->
case (v1, v2) of
(ValInteger i1, ValInteger i2) -> op i1 i2
_ -> runtimeError "invalid operation: expected two integers on value stack"
goBinOp :: (Member Runtime r) => (Val -> Val -> Either Text Val) -> Sem r ()
goBinOp op = goBinOp' (\x y -> eitherToError (op x y))

goIntBinOp :: (Member Runtime r) => (Integer -> Integer -> Val) -> Sem r ()
goIntBinOp op = goIntBinOp' (\v1 v2 -> return (op v1 v2))

goStrBinOp' :: (Member Runtime r) => (Text -> Text -> Sem r Val) -> Sem r ()
goStrBinOp' op = goBinOp' $ \v1 v2 ->
case (v1, v2) of
(ValString s1, ValString s2) -> op s1 s2
_ -> runtimeError "invalid operation: expected two strings on value stack"
goUnop' :: (Member Runtime r) => (Val -> Sem r Val) -> Sem r ()
goUnop' op = do
v <- popValueStack
v' <- op v
pushValueStack v'

goStrBinOp :: (Member Runtime r) => (Text -> Text -> Val) -> Sem r ()
goStrBinOp op = goStrBinOp' (\v1 v2 -> return (op v1 v2))
goUnop :: (Member Runtime r) => (Val -> Either Text Val) -> Sem r ()
goUnop op = goUnop' (eitherToError . op)

getConstantVal :: Constant -> Val
getConstantVal = \case
Expand Down Expand Up @@ -331,19 +277,10 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta
goCode (fi ^. functionCode)
_ -> runtimeError "invalid indirect call: expected closure on top of value stack"

printVal :: Val -> Text
printVal = \case
ValString s -> s
v -> ppPrint infoTable v

valToInt :: (Member Runtime r) => Val -> Sem r Integer
valToInt = \case
ValString s ->
case readMaybe (fromText s) of
Just i -> return i
Nothing -> runtimeError "string to integer conversion error"
ValInteger i -> return i
_ -> runtimeError "integer conversion error"
eitherToError :: (Member Runtime r) => Either Text Val -> Sem r Val
eitherToError = \case
Left err -> runtimeError err
Right v -> return v

-- | Interpret JuvixAsm code and the resulting IO actions.
runCodeIO :: InfoTable -> FunctionInfo -> IO Val
Expand Down
45 changes: 6 additions & 39 deletions src/Juvix/Compiler/Asm/Language.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
module Juvix.Compiler.Asm.Language
( module Juvix.Compiler.Asm.Language,
module Juvix.Compiler.Tree.Language.Base,
module Juvix.Compiler.Tree.Language.Builtins,
)
where

import Juvix.Compiler.Tree.Language.Base
import Juvix.Compiler.Tree.Language.Builtins

-- In what follows, when referring to the stack we mean the current local value
-- stack, unless otherwise stated. By stack[n] we denote the n-th cell from the
Expand All @@ -35,12 +37,10 @@ data CallType
data Instruction
= -- | An instruction which takes its operands from the two top stack cells,
-- pops the stack by two, and then pushes the result.
Binop Opcode
| -- | Convert the top stack cell to a string. JVA opcode: 'show'.
ValShow
| -- | Convert a string on top of the stack into an integer. JVA opcode:
-- 'atoi'.
StrToInt
Binop BinaryOp
| -- | An instruction which on the top of the value stack, replacing it with
-- the result of a unary operation.
Unop UnaryOp
| -- | Push a value on top of the stack. JVA opcode: 'push <val>'.
Push Value
| -- | Pop the stack. JVA opcode: 'pop'.
Expand All @@ -53,10 +53,6 @@ data Instruction
| -- | Interrupt execution with a runtime error printing the value on top of
-- the stack. JVA opcode: 'fail'.
Failure
| -- | Computes the number of expected arguments for the closure on top of the
-- stack, pops the stack and pushes the result on top of the stack. JVA
-- opcode: 'argsnum'.
ArgsNum
| -- | Preallocate memory. This instruction is inserted automatically before
-- translation to JuvixReg. It does not occur in JVA files.
Prealloc InstrPrealloc
Expand Down Expand Up @@ -116,35 +112,6 @@ data Instruction
-- JVA opcode: 'ret'.
Return

data Opcode
= -- | Add stack[0] + stack[1], pop the stack by two, and push the result. JVA
-- opcode: 'add'.
IntAdd
| -- | Subtract stack[0] - stack[1], pop the stack by two, and push the
-- result. JVA opcode: 'sub'.
IntSub
| -- | Multiply stack[0] * stack[1], pop the stack by two, and push the
-- result. JVA opcode 'mul'.
IntMul
| -- | Divide stack[0] / stack[1], pop the stack by two, and push the result.
-- JVA opcode: 'div'.
IntDiv
| -- | Calculate modulus stack[0] % stack[1], pop the stack by two, and push
-- the result. JVA opcode: 'mod'.
IntMod
| -- | Compare stack[0] < stack[1], pop the stack by two, and push the result.
-- JVA opcode: 'lt'.
IntLt
| -- | Compare stack[0] <= stack[1], pop the stack by two, and push the
-- result. JVA opcode: 'le'.
IntLe
| -- | Compare the two top stack cells with structural equality, pop the stack
-- by two, and push the result. JVA opcode: 'eq'.
ValEq
| -- | Concatenate two string on top of the stack, pop the stack by two, and
-- push the result. JVA opcode: 'strcat'.
StrConcat

newtype InstrPrealloc = InstrPrealloc
{ -- | How many words to preallocate?
_preallocWordsNum :: Int
Expand Down
Loading

0 comments on commit 6b74d84

Please sign in to comment.