我有一些表达式a=b+c-d*e
,在 LLVM pass 的帮助下,我想制作一个这样的字符串
“['b'的十六进制地址] [+的操作码] ['c'的十六进制地址] [-的操作码] ['d'的十六进制地址] [*的操作码] ['e'的十六进制地址]”。
比我该怎么做。
我有一些表达式a=b+c-d*e
,在 LLVM pass 的帮助下,我想制作一个这样的字符串
“['b'的十六进制地址] [+的操作码] ['c'的十六进制地址] [-的操作码] ['d'的十六进制地址] [*的操作码] ['e'的十六进制地址]”。
比我该怎么做。
首先,请记住变量不一定驻留在内存中;它们可以存储在寄存器中或完全省略。在 LLVM IR 的上下文中,这意味着该值将直接从另一个值使用(不存储或加载)。
假设所有涉及的变量确实需要从内存中加载,我能想到的最直接的方法是找到store
,然后通过操作数向后执行后序 DFS,记录操作码,并在识别时停止加载。对于您提供的代码段,它应该给您 b 的负载,然后是操作码,然后是 c 的负载,然后是操作码,等等。
既然您有这样的序列,我想说从中生成字符串的最简单方法是sprintf
使用动态构建的格式字符串插入对 C 的调用,并将您找到的指针传递给它(load
从中编辑)。
不过,我看到上述两个问题:
这里有一些固有的歧义 - 只是以这种方式访问它们无法区分,例如,(b+c-d)*e
从b+(c-d)*e
. 因此,我认为每当您输入算术指令并分别离开它时,也记录“(”和“)”是有意义的。
这种方法实际上并不检查所有操作是否都是同一表达式的一部分。所以如果你有tmp = b+c; a = tmp-d*e;
, 并且tmp
被优化掉了,那么它在 IR 中看起来是一样的。我能想到的唯一强制执行方法是使用调试符号进行编译并深入研究这些符号以识别不同的表达式——尽管我真的不知道这是否可能——或者实际上修改 Clang 以记录表达式边界:\
这种方法的伪代码(带有简单的序列处理操作):
functionPass:
for each instruction:
if instruction is store:
processExpression(store)
processExpression(store):
sequence <- initialize
visit(sequence, store.value)
generateSprintfCallFromSequence(sequence)
visit(sequence, value):
if value is load:
sequence.add(load.pointer)
else if value is binaryop:
// sequence.add(openingParen)
visit(sequence, binaryop.operand(0))
sequence.add(binaryop.opcode)
visit(sequence, binaryop.operand(1))
// sequence.add(closingParen);