为了生成 x86 汇编代码,我定义了一个名为的自定义类型X86
:
data X86 a = X86 { code :: String, counter :: Integer, value :: (X86 a -> a) }
这种类型在 do-notation 中使用,如下所示。这使得编写用于生成 if 语句、for 循环等的模板变得很容易......
generateCode :: X86 ()
generateCode = do
label1 <- allocateUniqueLabel
label2 <- allocateUniqueLabel
jmp label1
label label1
jmp label2
label label2
指令定义如下:
jmp :: String -> X86 ()
jmp l = X86 { code = "jmp " ++ l ++ ";\n", counter = 0, value = const () }
label :: String -> X86 ()
label l = X86 { code = l ++ ":\n", counter = 0, value = const () }
完成的汇编文件打印如下:
printAsm :: X86 a -> String
printAsm X86{code=code} = code
main = do
putStrLn (printAsm generateCode)
我X86
以以下方式实现了 monad。本质上,序列运算符按顺序连接汇编代码块并确保计数器递增。
instance Monad X86 where
x >> y = X86 { code = code x ++ code y, counter = counter x + counter y, value = value y }
x >>= f = x >> y
where y = f (value x x)
问题是标签没有正确增加,所以它们不是唯一的!以下是输出:
jmp Label1;
Label1:
jmp Label1;
Label1:
我希望输出对每个标签都有一个唯一的值:
jmp Label1;
Label1:
jmp Label2;
Label2:
为了完成示例,这里是allocatedUniqueLabel
函数的实现:
allocateUniqueId :: X86 Integer
allocateUniqueId = X86 { code = "", counter = 1, value = counter }
allocateUniqueLabel :: X86 String
allocateUniqueLabel = do
id <- allocateUniqueId
return ("Label" ++ show id)
如何修复我的X86
monad 以使标签独一无二?
这是我尝试过的:
- 增加一个全局计数器。=> Haskell 不允许 IO monad 之外的全局状态。
- 使用
State
单子。=> 我研究了一些例子,但不明白如何将它们集成到我现有的X86
monad 中。 - 跟踪单子外的柜台。=> 我宁愿计数器在“幕后”更新;否则,许多不使用标签的代码模板将需要手动传播计数器。