嗯,不完全是。我有更多的功能数据结构问题。
说我想模拟CPU的执行。我有一些改变CPU状态的指令(比如它是基于堆栈的cpu。只有跳转有操作数......或者其他),组成程序的一些指令列表和标签。通过引用某些标签来完成跳转,而不是某些偏移。我该如何表达这个?
如果我的程序看起来像[Label "foo", Add, Add, Mult, Label "bar", Jnz "foo"]
,那么当我点击Jnz "foo"
时,我需要向后和向前搜索标签“foo”以继续执行。这看起来有点傻。我想我应该能够拥有一个更好的数据结构,允许快速跳转到标签。现在,任意我会说我不想存储偏移量。假设CPU允许自我修改代码或类似的东西。我应该如何修补代码,同时确保引用仍然指向当前版本的代码?
我喜欢拉链的想法。 我只是不知道在O(1)时间内拉链跳转到预定义位置的想法是否有意义
答案 0 :(得分:2)
答案(但我不确定它是你想要的)是做LLVM的工作并将代码存储为BasicBlock
。在这个系统中,每个块都是一段只能从头开始输入的代码。最后一条指令必须跳转到另一个块。然后,您的标签将附加到基本块,而不是在指令列表中。程序可能如下:
newtype Lable = [Char]
data Code = Data.Map Label [Instruction]
当你点击跳转指令时,你只需lookup
它在地图中的阻挡并继续前进。
如果您的目标是优化代码(这也是LLVM使用它的原因),这有一些真正的优势,但它确实打破了将代码简单地表示为指令列表的模式。
另一方面,我无法想到程序代码中包含标签的任何CPU或计算模型。在汇编中,它们只是描述下一行代码的固定偏移量的便捷方式。它们不会在输出中结束,如果代码在运行时可修改,则jmp
指令也需要修改。
答案 1 :(得分:1)
我建议像
import Data.Map as M
data Code = Code { instructions :: [Instruction], labels :: M.Map String [Instruction] }
其中标签映射的值与指令列表共享。您可以将标签保留在指令流中并执行类似
的操作mkCode is = Code is (scan is)
where scan [] = M.empty
scan (Label l:js) = M.insert l js (scan js)
scan (_:js) = (scan js)
建立正确的代码值。
请注意Data.Map
实现二叉搜索树。如果你想要哈希表Data.Hashtable
。