diff --git a/src/Juvix/Compiler/Asm/Data/InfoTable.hs b/src/Juvix/Compiler/Asm/Data/InfoTable.hs index be1cff47b1..51075cd12c 100644 --- a/src/Juvix/Compiler/Asm/Data/InfoTable.hs +++ b/src/Juvix/Compiler/Asm/Data/InfoTable.hs @@ -25,6 +25,8 @@ data FunctionInfo = FunctionInfo -- and the result is a function. _functionArgsNum :: Int, _functionType :: Type, + _functionMaxValueStackHeight :: Int, + _functionMaxTempStackHeight :: Int, _functionCode :: Code } @@ -48,7 +50,8 @@ data InductiveInfo = InductiveInfo _inductiveLocation :: Maybe Location, _inductiveSymbol :: Symbol, _inductiveKind :: Type, - _inductiveConstructors :: [ConstructorInfo] + _inductiveConstructors :: [ConstructorInfo], + _inductiveRepresentation :: IndRep } makeLenses ''InfoTable diff --git a/src/Juvix/Compiler/Asm/Error.hs b/src/Juvix/Compiler/Asm/Error.hs index e8f45af24e..417c131d3e 100644 --- a/src/Juvix/Compiler/Asm/Error.hs +++ b/src/Juvix/Compiler/Asm/Error.hs @@ -3,6 +3,7 @@ module Juvix.Compiler.Asm.Error where import Juvix.Compiler.Asm.Language import Juvix.Data.PPOutput import Text.Megaparsec.Pos qualified as M +import Text.Show data AsmError = AsmError { _asmErrorLoc :: Maybe Location, @@ -30,6 +31,9 @@ instance ToGenericError AsmError where instance Pretty AsmError where pretty (AsmError {..}) = pretty _asmErrorMsg +instance Show AsmError where + show (AsmError {..}) = fromText _asmErrorMsg + instance HasLoc AsmError where getLoc (AsmError {..}) = fromMaybe defaultLoc _asmErrorLoc where diff --git a/src/Juvix/Compiler/Asm/Extra.hs b/src/Juvix/Compiler/Asm/Extra.hs index 4a98604c78..d3f37d22b5 100644 --- a/src/Juvix/Compiler/Asm/Extra.hs +++ b/src/Juvix/Compiler/Asm/Extra.hs @@ -28,9 +28,7 @@ validateCode tab args = void . recurse sig args } validateFunction :: Member (Error AsmError) r => InfoTable -> FunctionInfo -> Sem r () -validateFunction tab fi = validateCode tab args (fi ^. functionCode) - where - args = HashMap.fromList $ zip [0 ..] (typeArgs (fi ^. functionType)) +validateFunction tab fi = validateCode tab (argumentsFromFunctionInfo fi) (fi ^. functionCode) validateInfoTable :: Member (Error AsmError) r => InfoTable -> Sem r () validateInfoTable tab = mapM_ (validateFunction tab) (HashMap.elems (tab ^. infoFunctions)) diff --git a/src/Juvix/Compiler/Asm/Extra/Memory.hs b/src/Juvix/Compiler/Asm/Extra/Memory.hs index 457a6db5b8..3610d787a1 100644 --- a/src/Juvix/Compiler/Asm/Extra/Memory.hs +++ b/src/Juvix/Compiler/Asm/Extra/Memory.hs @@ -13,6 +13,11 @@ import Safe (atMay) type Arguments = HashMap Offset Type +argumentsFromFunctionInfo :: FunctionInfo -> Arguments +argumentsFromFunctionInfo fi = + HashMap.fromList $ + zip [0 ..] (take (fi ^. functionArgsNum) (typeArgs (fi ^. functionType))) + -- | A static representation of JuvixAsm memory providing type information for -- memory locations. data Memory = Memory @@ -39,12 +44,18 @@ pushValueStack ty = over memoryValueStack (Stack.push ty) popValueStack :: Int -> Memory -> Memory popValueStack n = iterateN n (over memoryValueStack Stack.pop) +valueStackHeight :: Memory -> Int +valueStackHeight mem = length (mem ^. memoryValueStack) + pushTempStack :: Type -> Memory -> Memory pushTempStack ty = over memoryTempStack (Stack.push ty) popTempStack :: Int -> Memory -> Memory popTempStack n = iterateN n (over memoryTempStack Stack.pop) +tempStackHeight :: Memory -> Int +tempStackHeight mem = length (mem ^. memoryTempStack) + -- | Read value stack at index `n` from the top. topValueStack :: Int -> Memory -> Maybe Type topValueStack n mem = Stack.nthFromTop n (mem ^. memoryValueStack) diff --git a/src/Juvix/Compiler/Asm/Extra/Recursors.hs b/src/Juvix/Compiler/Asm/Extra/Recursors.hs index f61637bdc2..05ba93ea7c 100644 --- a/src/Juvix/Compiler/Asm/Extra/Recursors.hs +++ b/src/Juvix/Compiler/Asm/Extra/Recursors.hs @@ -1,6 +1,6 @@ module Juvix.Compiler.Asm.Extra.Recursors ( module Juvix.Compiler.Asm.Extra.Recursors, - Arguments, + module Juvix.Compiler.Asm.Extra.Memory, ) where @@ -23,6 +23,9 @@ data RecursorSig r a = RecursorSig makeLenses ''RecursorSig +recurseFun :: Member (Error AsmError) r => RecursorSig r a -> FunctionInfo -> Sem r [a] +recurseFun sig fi = recurse sig (argumentsFromFunctionInfo fi) (fi ^. functionCode) + recurse :: Member (Error AsmError) r => RecursorSig r a -> Arguments -> Code -> Sem r [a] recurse sig args = fmap snd . recurse' sig (mkMemory args) diff --git a/src/Juvix/Compiler/Asm/Translation/FromCore.hs b/src/Juvix/Compiler/Asm/Translation/FromCore.hs index bfe3bf6b1f..6c03bdaf3d 100644 --- a/src/Juvix/Compiler/Asm/Translation/FromCore.hs +++ b/src/Juvix/Compiler/Asm/Translation/FromCore.hs @@ -32,7 +32,9 @@ genCode infoTable fi = _functionSymbol = fi ^. Core.functionSymbol, _functionArgsNum = fi ^. Core.functionArgsNum, _functionType = convertType (fi ^. Core.functionArgsNum) (fi ^. Core.functionType), - _functionCode = code + _functionCode = code, + _functionMaxTempStackHeight = -1, -- computed later + _functionMaxValueStackHeight = -1 } where unimplemented :: forall a. a diff --git a/src/Juvix/Compiler/Asm/Translation/FromSource.hs b/src/Juvix/Compiler/Asm/Translation/FromSource.hs index 3126e6c2d4..b23f469a9f 100644 --- a/src/Juvix/Compiler/Asm/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Asm/Translation/FromSource.hs @@ -71,7 +71,8 @@ declareBuiltins = do _inductiveSymbol = sym, _inductiveLocation = Just i, _inductiveKind = TyDynamic, - _inductiveConstructors = constrs + _inductiveConstructors = constrs, + _inductiveRepresentation = IndRepStandard } ) lift $ mapM_ registerConstr constrs @@ -115,7 +116,9 @@ statementFunction = do _functionLocation = Just i, _functionCode = [], _functionArgsNum = length argtys, - _functionType = mkTypeFun argtys (fromMaybe TyDynamic mrty) + _functionType = mkTypeFun argtys (fromMaybe TyDynamic mrty), + _functionMaxValueStackHeight = -1, -- computed later + _functionMaxTempStackHeight = -1 } lift $ registerFunction fi0 mcode <- (kw kwSemicolon $> Nothing) <|> optional (braces parseCode) @@ -153,7 +156,8 @@ statementInductive = do _inductiveLocation = Just i, _inductiveSymbol = sym, _inductiveKind = TyDynamic, - _inductiveConstructors = [] + _inductiveConstructors = [], + _inductiveRepresentation = IndRepStandard } lift $ registerInductive ii ctrs <- braces $ P.sepEndBy (constrDecl sym) (kw kwSemicolon) diff --git a/src/Juvix/Compiler/Reg/Data/InfoTable.hs b/src/Juvix/Compiler/Reg/Data/InfoTable.hs new file mode 100644 index 0000000000..00ded2ced9 --- /dev/null +++ b/src/Juvix/Compiler/Reg/Data/InfoTable.hs @@ -0,0 +1,51 @@ +module Juvix.Compiler.Reg.Data.InfoTable where + +import Juvix.Compiler.Reg.Language + +data InfoTable = InfoTable + { _infoFunctions :: HashMap Symbol FunctionInfo, + _infoConstrs :: HashMap Tag ConstructorInfo, + _infoInductives :: HashMap Symbol InductiveInfo, + _infoMainFunction :: Maybe Symbol + } + +data FunctionInfo = FunctionInfo + { _functionName :: Text, + _functionLocation :: Maybe Location, + _functionSymbol :: Symbol, + _functionArgsNum :: Int, + _functionStackVarsNum :: Int, + _functionTempVarsNum :: Int, + _functionCode :: Code + } + +data ConstructorInfo = ConstructorInfo + { _constructorName :: Text, + _constructorLocation :: Maybe Location, + _constructorTag :: Tag, + _constructorArgsNum :: Int, + _constructorInductive :: Symbol, + _constructorRepresentation :: MemRep + } + +data InductiveInfo = InductiveInfo + { _inductiveName :: Text, + _inductiveLocation :: Maybe Location, + _inductiveSymbol :: Symbol, + _inductiveConstructors :: [ConstructorInfo], + _inductiveRepresentation :: IndRep + } + +makeLenses ''InfoTable +makeLenses ''FunctionInfo +makeLenses ''ConstructorInfo +makeLenses ''InductiveInfo + +emptyInfoTable :: InfoTable +emptyInfoTable = + InfoTable + { _infoFunctions = mempty, + _infoConstrs = mempty, + _infoInductives = mempty, + _infoMainFunction = Nothing + } diff --git a/src/Juvix/Compiler/Reg/Language.hs b/src/Juvix/Compiler/Reg/Language.hs index 07484a440f..2e333621b1 100644 --- a/src/Juvix/Compiler/Reg/Language.hs +++ b/src/Juvix/Compiler/Reg/Language.hs @@ -6,18 +6,6 @@ where import Juvix.Compiler.Reg.Language.Base -data Type - = -- | Unboxed integer or pointer to boxed integer. - TyInteger - | -- | Unboxed boolean. - TyBool - | -- | Pointer to a string. - TyString - | -- | A raw word with arbitrary bit pattern. - TyWord - | -- | A pointer to a constructor. - TyPtr - data Value = ConstInt Integer | ConstBool Bool @@ -44,12 +32,12 @@ data VarGroup = VarGroupArgs | VarGroupStack | VarGroupTemp data VarRef = VarRef { _varRefGroup :: VarGroup, - _varRefIndex :: Index, - _varRefType :: Type + _varRefIndex :: Index } data Instruction - = Binop BinaryOp + = Nop -- no operation + | Binop BinaryOp | Assign InstrAssign | Trace InstrTrace | Dump @@ -113,6 +101,7 @@ data InstrAllocClosure = InstrAllocClosure data InstrExtendClosure = InstrExtendClosure { _instrExtendClosureResult :: VarRef, + _instrExtendClosureValue :: VarRef, _instrExtendClosureArgs :: [Value], _instrExtendClosureLiveVars :: [VarRef] } @@ -132,8 +121,8 @@ data InstrCall = InstrCall data InstrCallClosures = InstrCallClosures { _instrCallClosuresResult :: VarRef, - _instrCallClosuresClosure :: VarRef, _instrCallClosuresIsTail :: Bool, + _instrCallClosuresValue :: VarRef, _instrCallClosuresArgs :: [Value], _instrCallClosuresLiveVars :: [VarRef] } diff --git a/src/Juvix/Compiler/Reg/Translation/FromAsm.hs b/src/Juvix/Compiler/Reg/Translation/FromAsm.hs index 7eafcae68a..82c29d4a6e 100644 --- a/src/Juvix/Compiler/Reg/Translation/FromAsm.hs +++ b/src/Juvix/Compiler/Reg/Translation/FromAsm.hs @@ -1,7 +1,276 @@ module Juvix.Compiler.Reg.Translation.FromAsm where +import Data.HashMap.Strict qualified as HashMap +import Juvix.Compiler.Asm.Data.InfoTable qualified as Asm +import Juvix.Compiler.Asm.Error qualified as Asm +import Juvix.Compiler.Asm.Extra.Recursors qualified as Asm import Juvix.Compiler.Asm.Language qualified as Asm +import Juvix.Compiler.Reg.Data.InfoTable import Juvix.Compiler.Reg.Language -fromAsm :: Asm.Code -> Code -fromAsm = undefined +fromAsm :: Asm.InfoTable -> InfoTable +fromAsm tab = + InfoTable + { _infoFunctions = HashMap.map convertFun (tab ^. Asm.infoFunctions), + _infoConstrs = HashMap.map convertConstr (tab ^. Asm.infoConstrs), + _infoInductives = HashMap.map convertInductive (tab ^. Asm.infoInductives), + _infoMainFunction = tab ^. Asm.infoMainFunction + } + where + convertFun :: Asm.FunctionInfo -> FunctionInfo + convertFun fi = + FunctionInfo + { _functionName = fi ^. Asm.functionName, + _functionLocation = fi ^. Asm.functionLocation, + _functionSymbol = fi ^. Asm.functionSymbol, + _functionArgsNum = fi ^. Asm.functionArgsNum, + _functionStackVarsNum = fi ^. Asm.functionMaxValueStackHeight, + _functionTempVarsNum = fi ^. Asm.functionMaxTempStackHeight, + _functionCode = fromAsmFun tab fi + } + + convertConstr :: Asm.ConstructorInfo -> ConstructorInfo + convertConstr ci = + ConstructorInfo + { _constructorName = ci ^. Asm.constructorName, + _constructorLocation = ci ^. Asm.constructorLocation, + _constructorTag = ci ^. Asm.constructorTag, + _constructorArgsNum = ci ^. Asm.constructorArgsNum, + _constructorInductive = ci ^. Asm.constructorInductive, + _constructorRepresentation = ci ^. Asm.constructorRepresentation + } + + convertInductive :: Asm.InductiveInfo -> InductiveInfo + convertInductive ii = + InductiveInfo + { _inductiveName = ii ^. Asm.inductiveName, + _inductiveLocation = ii ^. Asm.inductiveLocation, + _inductiveSymbol = ii ^. Asm.inductiveSymbol, + _inductiveConstructors = map convertConstr (ii ^. Asm.inductiveConstructors), + _inductiveRepresentation = ii ^. Asm.inductiveRepresentation + } + +fromAsmFun :: + Asm.InfoTable -> + Asm.FunctionInfo -> + Code +fromAsmFun tab fi = + case run $ runError $ Asm.recurseFun sig fi of + Left err -> error (show err) + Right code -> code + where + sig :: Asm.RecursorSig (Error Asm.AsmError ': r) Instruction + sig = + Asm.RecursorSig + { _recursorInfoTable = tab, + _recurseInstr = fromAsmInstr tab, + _recurseBranch = fromAsmBranch, + _recurseCase = fromAsmCase tab + } + +fromAsmInstr :: + Asm.InfoTable -> + Asm.Memory -> + Asm.CmdInstr -> + Sem r Instruction +fromAsmInstr tab mem Asm.CmdInstr {..} = + case _cmdInstrInstruction of + Asm.IntAdd -> return $ mkBinop OpIntAdd + Asm.IntSub -> return $ mkBinop OpIntSub + Asm.IntMul -> return $ mkBinop OpIntMul + Asm.IntDiv -> return $ mkBinop OpIntDiv + Asm.IntMod -> return $ mkBinop OpIntMod + Asm.IntLt -> return $ mkBinop OpIntLt + Asm.IntLe -> return $ mkBinop OpIntLe + Asm.ValEq -> return $ mkBinop OpEq + Asm.Push val -> return $ mkAssign (VarRef VarGroupStack (n + 1)) (mkValue val) + Asm.Pop -> return Nop + Asm.PushTemp -> + return $ + mkAssign + (VarRef VarGroupTemp (Asm.tempStackHeight mem)) + (VRef $ VarRef VarGroupStack n) + Asm.PopTemp -> return Nop + Asm.Trace -> return $ Trace $ InstrTrace (VRef $ VarRef VarGroupStack n) + Asm.Dump -> return Dump + Asm.Failure -> return $ Failure $ InstrFailure (VRef $ VarRef VarGroupStack n) + Asm.AllocConstr tag -> return $ mkAlloc tag + Asm.AllocClosure x -> return $ mkAllocClosure x + Asm.ExtendClosure x -> return $ mkExtendClosure x + Asm.Call x -> return $ mkCall False x + Asm.TailCall x -> return $ mkCall True x + Asm.CallClosures x -> return $ mkCallClosures False x + Asm.TailCallClosures x -> return $ mkCallClosures True x + Asm.Return -> return Return + where + -- `n` is the index of the top of the value stack *before* executing the + -- instruction + n :: Int + n = Asm.valueStackHeight mem - 1 + + -- Live variables *after* executing the instruction. TODO: proper liveness + -- analysis in JuvixAsm. + liveVars :: Int -> [VarRef] + liveVars k = + map (VarRef VarGroupStack) [0 .. n - k] + ++ map (VarRef VarGroupTemp) [0 .. Asm.tempStackHeight mem] + ++ map (VarRef VarGroupArgs) [0 .. (mem ^. Asm.memoryArgsNum)] + + getArgs :: Int -> Int -> [Value] + getArgs s k = map (\i -> VRef $ VarRef VarGroupStack (n - i)) [s .. (s + k - 1)] + + mkBinop :: Opcode -> Instruction + mkBinop op = + Binop + ( BinaryOp + { _binaryOpCode = op, + _binaryOpResult = VarRef VarGroupStack n, + _binaryOpArg1 = VRef $ VarRef VarGroupStack n, + _binaryOpArg2 = VRef $ VarRef VarGroupStack (n - 1) + } + ) + + mkAssign :: VarRef -> Value -> Instruction + mkAssign tgt src = Assign (InstrAssign tgt src) + + mkValue :: Asm.Value -> Value + mkValue = \case + Asm.ConstInt v -> ConstInt v + Asm.ConstBool v -> ConstBool v + Asm.ConstString v -> ConstString v + Asm.ConstUnit -> ConstUnit + Asm.ConstVoid -> ConstVoid + Asm.Ref mv -> case mv of + Asm.DRef dref -> VRef $ mkVar dref + Asm.ConstrRef Asm.Field {..} -> + CRef $ + ConstrField + { _constrFieldTag = _fieldTag, + _constrFieldRef = mkVar _fieldRef, + _constrFieldIndex = _fieldOffset, + _constrFieldMemRep = ci ^. Asm.constructorRepresentation + } + where + ci = fromJust impossible $ HashMap.lookup _fieldTag (tab ^. Asm.infoConstrs) + + mkVar :: Asm.DirectRef -> VarRef + mkVar = \case + Asm.StackRef -> VarRef VarGroupStack n + Asm.ArgRef idx -> VarRef VarGroupArgs idx + Asm.TempRef idx -> VarRef VarGroupTemp idx + + mkAlloc :: Tag -> Instruction + mkAlloc tag = + Alloc $ + InstrAlloc + { _instrAllocTag = tag, + _instrAllocResult = VarRef VarGroupStack n, + _instrAllocArgs = getArgs 0 (ci ^. Asm.constructorArgsNum), + _instrAllocMemRep = ci ^. Asm.constructorRepresentation, + _instrAllocLiveVars = liveVars (ci ^. Asm.constructorArgsNum) + } + where + ci = fromJust impossible $ HashMap.lookup tag (tab ^. Asm.infoConstrs) + + mkAllocClosure :: Asm.InstrAllocClosure -> Instruction + mkAllocClosure Asm.InstrAllocClosure {..} = + AllocClosure $ + InstrAllocClosure + { _instrAllocClosureSymbol = fi ^. Asm.functionSymbol, + _instrAllocClosureResult = VarRef VarGroupStack n, + _instrAllocClosureExpectedArgsNum = fi ^. Asm.functionArgsNum, + _instrAllocClosureArgs = getArgs 0 _allocClosureArgsNum, + _instrAllocClosureLiveVars = liveVars _allocClosureArgsNum + } + where + fi = fromJust impossible $ HashMap.lookup _allocClosureFunSymbol (tab ^. Asm.infoFunctions) + + mkExtendClosure :: Asm.InstrExtendClosure -> Instruction + mkExtendClosure Asm.InstrExtendClosure {..} = + ExtendClosure $ + InstrExtendClosure + { _instrExtendClosureResult = VarRef VarGroupStack n, + _instrExtendClosureValue = VarRef VarGroupStack n, + _instrExtendClosureArgs = getArgs 1 _extendClosureArgsNum, + _instrExtendClosureLiveVars = liveVars (_extendClosureArgsNum + 1) + } + + mkCall :: Bool -> Asm.InstrCall -> Instruction + mkCall isTail Asm.InstrCall {..} = + Call $ + InstrCall + { _instrCallResult = VarRef VarGroupStack n, + _instrCallType = ct, + _instrCallIsTail = isTail, + _instrCallArgs = getArgs s _callArgsNum, + _instrCallLiveVars = liveVars (_callArgsNum + s) + } + where + ct = case _callType of + Asm.CallFun f -> CallFun f + Asm.CallClosure -> CallClosure (VarRef VarGroupStack n) + s = case _callType of + Asm.CallFun {} -> 0 + Asm.CallClosure -> 1 + + mkCallClosures :: Bool -> Asm.InstrCallClosures -> Instruction + mkCallClosures isTail Asm.InstrCallClosures {..} = + CallClosures $ + InstrCallClosures + { _instrCallClosuresResult = VarRef VarGroupStack n, + _instrCallClosuresValue = VarRef VarGroupStack n, + _instrCallClosuresIsTail = isTail, + _instrCallClosuresArgs = getArgs 1 _callClosuresArgsNum, + _instrCallClosuresLiveVars = liveVars _callClosuresArgsNum + } + +fromAsmBranch :: + Asm.Memory -> + Asm.CmdBranch -> + Code -> + Code -> + Sem r Instruction +fromAsmBranch mem Asm.CmdBranch {} codeTrue codeFalse = + return $ + Branch $ + InstrBranch + { _instrBranchValue = VRef $ VarRef VarGroupStack (Asm.valueStackHeight mem - 1), + _instrBranchTrue = codeTrue, + _instrBranchFalse = codeFalse + } + +fromAsmCase :: + Asm.InfoTable -> + Asm.Memory -> + Asm.CmdCase -> + [Code] -> + Maybe Code -> + Sem r Instruction +fromAsmCase tab mem Asm.CmdCase {..} brs def = + return $ + Case $ + InstrCase + { _instrCaseValue = VRef $ VarRef VarGroupStack (Asm.valueStackHeight mem - 1), + _instrCaseInductive = _cmdCaseInductive, + _instrCaseIndRep = ii ^. inductiveRepresentation, + _instrCaseBranches = + zipWithExact + ( \br code -> + let tag = br ^. Asm.caseBranchTag + ci = + fromJust impossible $ + HashMap.lookup tag (tab ^. Asm.infoConstrs) + in CaseBranch + { _caseBranchTag = tag, + _caseBranchMemRep = ci ^. constructorRepresentation, + _caseBranchCode = code + } + ) + _cmdCaseBranches + brs, + _instrCaseDefault = def + } + where + ii = + fromJust impossible $ + HashMap.lookup _cmdCaseInductive (tab ^. Asm.infoInductives)