How can I inspect WAM code in SICStus Prolog
Asked Answered
L

1

10

In the context of hacking on I want to glimpse at the code generated by SICStus Prolog. As an example, let's dissect the following predicate!

is_list([]).
is_list([_|Es]) :- is_list(Es).

Here's what I'm doing now:

  1. Split the 2 clauses of is_list/1 into 2 separate predicates and prepend 2 dummy clauses:

    is_list__clause1(dummy1).                  % dummy clause
    is_list__clause1([]).
    
    is_list__clause2(dummy2).                  % dummy clause
    is_list__clause2([_|Es]) :- is_list(Es).
    
  2. (Ab-)use the SICStus like so:

    | ?- 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
    …

This output—albeit somewhat cryptic—is giving me a feel of what's going on at the WAM level. I like!

But there has got to a simpler way... please help!

Likely answered 31/8, 2019 at 9:4 Comment(9)
Are you sure it is WAM? See: SICStus Prolog—the first 25 years I didn't think SWI-Prolog had an abstract machine, then found that it is based on ZIP SWI-Prolog Implementation history SICStus What Is New In Release 4Probabilism
However in the Sicstus manual, April 2019, version 4.5.1 it states: The system consists of a WAM emulator written in C, a library and runtime system written in C and Prolog and an interpreter and a compiler written in Prolog. The Prolog engine is a Warren Abstract Machine (WAM) emulator [Warren 83].Probabilism
This predates version 4, and since version seems to be a complete rewrite, this is most likely obliviated: The SICStus Emulator by Mats Carlsson Sept 1991 -- Found at: Prolog and Logic Programming Historical Sources ArchiveProbabilism
@GuyCoder. Please put your comments into an answer so I can upvote it.Likely
Honestly I can not say that these should be put into an answer for this question just to get an up-vote. I agree that they are of value and as answers to another question they would be worthy of an up-vote, but too many times I have posted such answers only to be bashed down by the moderators here for such answers. Whac-A-Mole Create a question in need of these comments and I will post there. Thanks. :)Probabilism
@GuyCoder. I found the historical sources archive to be very interesting. It was new to me, so your comments were very helpful. Don't let the internet drag you down. ☮Likely
Thanks. I am not letting the internet drag me down. I have moved my focus of helping others on-line with Prolog from here at StackOverflow to the SWI-Prolog Discourse forum. I put the historical sources archive as a link on Useful Prolog references.Probabilism
If you join the SWI-Prolog Discourse forum site and earn Basic trust level which is real easy, you can edit the the list also. The site is also useful if you need to have a discussion with a user here to help them. You can even keep the messages private if need be.Probabilism
The historical sources link is now part of the Prolog tagProbabilism
B
7

There is a simpler way: the undocumented, unsupported, library(disassembler).

You can use this for programmatically getting information about first-argument indexing in addition to the WAM instructions. See the source for more information.

| ?- 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
| ?- 
Bethink answered 31/8, 2019 at 15:42 Comment(4)
Is it possibly to see the JIT-ed code as well?Mood
There is no good way to see the JIT-ed code in release builds. On Linux you can use sicstus -DSP_SPTI_PATH=perf which will dump profile information, including object code (as ELF-binaries), somewhere in /tmp. The dumped information contains a perf.map that tells which elf file corresponds to which Prolog predicate, and something like objdump --disassemble-all can be used to disassemble it. Unfortunately, the binary contains a mixture of data and machine code, so the disassembler is likely to get confused.Bethink
If you want to see where your jitted code spends its time, use perf record sicstus -DSP_SPTI_PATH=perf and perf annotate --objdump=sp_jit_objdump (or perf top -objdump=sp_jit_objdump).Bethink
On other platforms, e.g. macOS, you could build something based on library/spti_verbose.*. It currently just logs the jitted procedures, but it gets the same information as the perf support, so you could modify it to dump the code block and disassemble it with some other tool. To build your own copy, do 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. This still suffers from the problem with data confusing the disassembler.Bethink

© 2022 - 2024 — McMap. All rights reserved.