几个月前,我在Haskell的第一个项目是编写一个c编译器,结果是一个相当幼稚的代码生成方法,我将在这里介绍。请做而不是将此作为代码生成器的良好设计示例,而是将其视为一种快速而肮脏(并且最终是天真)的方式,以便能够以相当快的速度运行,并且具有良好的性能。
我开始通过定义一个中间表示LIR(下中间表示),它紧密对应于我的指令集(在我的情况x86_64的):
data LIRInst = LIRRegAssignInst LIRReg LIRExpr
| LIRRegOffAssignInst LIRReg LIRReg LIRSize LIROperand
| LIRStoreInst LIRMemAddr LIROperand
| LIRLoadInst LIRReg LIRMemAddr
| LIREnterInst LIRInt
| LIRJumpLabelInst LIRLabel
| LIRIfInst LIRRelExpr LIRLabel LIRLabel -- false, then true
| LIRCallInst LIRLabel LIRLabel -- method label, return label
| LIRCalloutInst String
| LIRRetInst [LIRLabel] String -- list of successors, and the name of the method returning from
| LIRLabelInst LIRLabel
deriving (Show, Eq, Typeable)
接下来来到一个单子,将处理整个交织状态翻译(我是一无所知我们的朋友,在State Monad
-at的时间):
newtype LIRTranslator a = LIRTranslator
{ runLIR :: Namespace -> (a, Namespace) }
instance Monad LIRTranslator where
return a = LIRTranslator (\s -> (a, s))
m >>= f = LIRTranslator (\s ->
let (a, s') = runLIR m s
in runLIR (f a) s')
通过各种转换阶段,将被“拧”国家一起:
data Namespace = Namespace
{ temp :: Int -- id's for new temporaries
, labels :: Int -- id's for new labels
, scope :: [(LIRLabel, LIRLabel)] -- current program scope
, encMethod :: String -- current enclosing method
, blockindex :: [Int] -- index into the SymbolTree
, successorMap :: Map.Map String [LIRLabel]
, ivarStack :: [(LIRReg, [CFGInst])] -- stack of ivars (see motioned code)
}
为了方便起见,我还指定了一系列的翻译一元功能,例如:
-- |Increment our translator's label counter
incLabel :: LIRTranslator Int
incLabel = LIRTranslator (\[email protected](Namespace{ labels = l }) -> (l, ns{ labels = (l+1) }))
我然后继续递归模式匹配我的AST,片段逐片段,得到形式的许多功能:
translateBlock :: SymbolTree -> ASTBlock -> LIRTranslator [LIRInst]
translateBlock st (DecafBlock _ [] _) = withBlock (return [])
translateBlock st block =
withBlock (do b <- getBlock
let st' = select b st
declarations <- mapM (translateVarDeclaration st') (blockVars block)
statements <- mapM (translateStm st') (blockStms block)
return (concat declarations ++ concat statements))
(用于翻译目标语言的代码块)或
-- | Given a SymbolTree, Translate a single DecafMethodStm into [LIRInst]
translateStm st (DecafMethodStm mc _) =
do (instructions, operand) <- translateMethodCall st mc
final <- motionCode instructions
return final
(用于翻译的方法调用)或
translateMethodPrologue :: SymbolTree -> DecafMethod -> LIRTranslator [LIRInst]
translateMethodPrologue st (DecafMethod _ ident args _ _) =
do let numRegVars = min (length args) 6
regvars = map genRegVar (zip [LRDI, LRSI, LRDX, LRCX, LR8, LR9] args)
stackvars <- mapM genStackVar (zip [1..] (drop numRegVars args))
return (regvars ++ stackvars)
where
genRegVar (reg, arg) =
LIRRegAssignInst (symVar arg st) (LIROperExpr $ LIRRegOperand reg)
genStackVar (index, arg) =
do let mem = LIRMemAddr LRBP Nothing ((index + 1) * 8) qword --^[rbp] = old rbp; [rbp + 8] = ret address; [rbp + 16] = first stack param
return $ LIRLoadInst (symVar arg st) mem
为实际生成一些LIR代码的例子。希望这三个例子能给你一个很好的起点;最终,你会想慢慢地,一次关注AST中的一个片段(或中间类型)。
这不值得一个完整的答案,显然涉及到一种非常不同的语言风格,但是[你可能熟悉用Haskell编写的编译器](http://hackage.haskell.org/trac/ ghc/wiki /评论/编译器/后端)的源代码,你可以看看灵感。 – 2011-06-09 21:29:06
你的中间表示映射到jvm操作码一对一吗?如果没有,那么这是一个开始的地方:创建一个或多个数据类型来表示(目标的)JVM操作码的一部分。然后走更高级的IR并创建低级别的JVM中心IR。 – Lambdageek 2011-06-09 21:38:48