如何在SICStus Prolog中检查WAM代码

10
在黑客的背景下,我想一睹SICStus Prolog生成的代码。例如,让我们剖析以下谓词!
is_list([]).
is_list([_|Es]) :- is_list(Es).

这是我现在正在做的:

  1. is_list/1 的 2 个子句拆分成 2 个单独的谓词,并在前面添加 2 个虚拟子句:

    is_list__clause1(dummy1).                  % 虚拟子句
    is_list__clause1([]).
    
    is_list__clause2(dummy2).                  % 虚拟子句
    is_list__clause2([_|Es]) :- is_list(Es).
    
  2. 滥用 SICStus ,操作如下:

    | ?- is_list__clause1(X).
    X = dummy1 ? t
    …
    0x7eff37281300: GET_NIL_X0 
    0x7eff37281304: PROCEED 
    0x7eff37281308: END_OF_CLAUSEQ user:is_list__clause1/1
    …
    | ?- is_list__clause2(X).
    X = dummy2 ? t
    …
    0x7eff37281150: GET_LIST_X0 
    0x7eff37281154: U2_VOID_XVAR 1,x(0)
    0x7eff37281160: EXECUTEQ user:is_list/1
    0x7eff37281170: END_OF_CLAUSEQ user:is_list__clause2/1
    …
这个输出——虽然有些神秘——让我感受到了WAM级别上正在发生的事情。我喜欢!但是必须有更简单的方法...请帮帮我!

3
你确定是WAM吗?参见:SICStus Prolog - 前25年。我原本认为SWI-Prolog没有抽象机,但后来发现它是基于ZIP的。请参阅SWI-Prolog实现历史和SICStus发布版4中的新内容。 - Guy Coder
3
然而,在2019年4月的Sicstus手册中,第[4.5.1]版(https://sicstus.sics.se/sicstus/docs/latest4/pdf/sicstus.pdf)指出:“该系统由一个用C编写的WAM模拟器、一个用C和Prolog编写的库和运行时系统、以及一个用Prolog编写的解释器和编译器组成。Prolog引擎是一个Warren抽象机(WAM)模拟器[Warren 83]。” - Guy Coder
3
这是早于版本4的内容,由于该版本似乎是一次完全的重写,因此这很可能已经被遗忘了:[The SICStus模拟器](http://soda.swedish-ict.se/2479/1/T91_15.pdf)由Mats Carlsson于1991年9月撰写。 -- 找到于:Prolog和逻辑编程历史源代码存档 - Guy Coder
2
@GuyCoder。我发现历史来源档案非常有趣。这对我来说是新的,所以你的评论非常有帮助。不要让互联网拖垮你。☮ - repeat
2
谢谢。我不会让互联网拖垮我。我已经将帮助他人的重点从StackOverflow转移到了SWI-Prolog Discourse论坛上。我在有用的Prolog参考资料中放置了历史来源档案的链接。 - Guy Coder
显示剩余4条评论
1个回答

7

有一种更简单的方法:未经记录、不受支持的 library(disassembler) 库。

您可以使用此库以编程方式获取有关第一个参数索引以及 WAM 指令的信息。有关更多信息,请参见源代码。

| ?- use_module(library(disassembler)).
% ...
yes
| ?- [user].
% compiling user...
| foo([],Ys,Ys). foo([X|Xs],Ys,[X|Zs]) :- foo(Xs,Ys,Zs). end_of_file.
% compiled user in module user, 89 msec 599696 bytes
yes
| ?- disassemble(foo/3).
% Predicate: user:foo/3 (user)
% Varcase: [4343940512-4343958960,4346212208-4343221120]
% Lstcase: [4346212212]
% Switch: [[]=4343940516], default: failcode
% clause indexed [var,number,atom,structure] (user)
%    4343940512: 'GET_NIL_X0'
%    4343940516: 'GET_X_VALUE_PROCEED'(x(1),x(2))
%    4343940528: 'END_OF_CLAUSEQ'(user:foo/3)
% clause indexed [var,list] (user)
%    4346212208: 'GET_LIST_X0'
%    4346212212: 'U2_XVAR_XVAR'(x(3,0),x(0,0))
%    4346212224: 'GET_LIST'(x(2))
%    4346212232: 'U2_XVAL_XVAR'(x(3),x(2,0))
%    4346212244: 'EXECUTE'(user:foo/3)
%    4346212256: 'END_OF_CLAUSEQ'(user:foo/3)
yes
| ?- 

能否查看 JIT 编译后的代码呢? - user502187
在发布的版本中,没有很好的方法来查看JIT编译的代码。在Linux上,您可以使用 sicstus -DSP_SPTI_PATH=perf 来转储性能信息(包括目标代码(作为ELF二进制文件)),并将其存储在 /tmp 中的某个位置。 转储的信息包含一个perf.map,告诉哪个elf文件对应于哪个Prolog谓词,以及类似于 objdump --disassemble-all 的内容可用于反汇编它。不幸的是,二进制文件包含了一些数据和机器代码的混合体,所以反汇编器可能会感到困惑。 - Per Mildner
如果你想查看JIT编译代码花费的时间,使用perf record sicstus -DSP_SPTI_PATH=perfperf annotate --objdump=sp_jit_objdump(或者perf top -objdump=sp_jit_objdump)。 - Per Mildner
在其他平台上,例如macOS,您可以基于library/spti_verbose.*构建一些东西。它目前只记录了已编译过的过程,但它获得了与perf支持相同的信息,因此您可以修改它以使用其他工具转储代码块并对其进行反汇编。要构建自己的副本,请执行cp .../library/spti_verbose.{c,pl} . && splfr --no-hide-symbols spti_verbose.pl spti_verbose.c -o spti_verbose.dylib && sicstus -DSP_SPTI_PATH=./spti_verbose.dylib。这仍然存在数据混淆反汇编器的问题。 - Per Mildner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接