Recently, I discovered some Assembly language techniques to enhance G.I.S. Prolog even further, potentially valuable for a multitude of other purposes. They also have an intrinsic fascination in themselves, as general tools for Prolog meta-programming.
E.g. here is an Assembly language predicate, that takes as inputs another (external) predicate’s memory-address and a (Visual Prolog-) argument-list, and calls this (external) predicate, using the (arbitrary-length-) list of N arguments, as arguments of “arity N”:
apply_func(PRED, [Arg,Arg2,…]) <=> PRED(Arg1,Arg2,…)
Now, in ISO-Prolog there is a standard predicate known as “univ”, written as “=..“, which turns a list like [PRED,ARG1,ARG2,ARG3…] into a predicate call such as PRED(ARG1,ARG2,…). However, this does not exist in Visual Prolog, which sacrifices such “luxuries” for speed (which is the reason I also often use ISO-Prolog compilers, such as LPA-WinProlog and SWI-Prolog).
Anyway… The code you are about to see can be useful more generally, as an example of Prolog meta-programming, implemented in Assembly Language. The only difference between the way it works for Visual Prolog and the way it might work for another Prolog (or -indeed- ANY programming language, using a ‘C’-calling convention) is the Visual-Prolog-specific structure of a LIST, which in Visual Prolog has a different form than in all other languages. If you understand Assembly Language and intend to use this code for other (meta-programming) tasks, all you have to do is modify just a couple of lines in the code that follows. However, before you (even begin to) look at the Assembly Language Code, the following simple definitions in Visual Prolog (5.*) are a prerequisite for easier understanding:
GLOBAL DOMAINS dom_iii = determ INTEGER (INTEGER,INTEGER,INTEGER) -(i,i,i) language c % <-- example domain
GLOBAL PREDICATES apply_func(DWORD,ListDomain) -(i,i) language c % where arg-1 is a predicate-domain, such as "dom_iii"
After you compile the Assembly language code, you could create a simple “EasyWin” Visual Prolog project, with the following ines:
PREDICATES func2dword(dom_iii,DWORD) % converts a predicate to a doubleword/address add3: dom_iii
CLAUSES func2dword(FUNC,DW):- DW = cast(dword,FUNC). add3(_X,_Y,_Z,Out):- Out = _X+_Y+_Z, write("out = ",Out), nl, ! ; write("error!\n"), Out=-1, !.
GOAL func2dword(add3,DW), Out = apply_func(DW,[10,20,30]), write("result = ",Out), nl, readchar(_). %This program should produce "result = 60" (sum of [10,20,30]).
OK, so here is the Assembly language code:
; ==================== _apply_func.asm ===================== ; Code for TASM 5 Assembler, command-line call for compilation: ; C:\TASM\BIN\TASM32.EXE /p /z /w2 /m2 /ml _apply_func.asm
IDEAL P586 MODEL flat CODESEG ALIGN 4
public _apply_func ; (i,i)
PROC _apply_func near ARG func:dword, list:dword LOCAL fcnt:dword ENTER 4,0 push esi ; push edi ; push ebx ; push ecx ; mov ecx,[func] ; function............ ARG 1 mov esi,[list] ; list................ ARG 2 xor ebx,ebx ; make EBX=0 mov [fcnt],ebx ; initialize local variable 'fcnt' to 0 lodsd ; load the 1st list-element's "element-flag" dec al ; decrement it, to check if it was a 1 jnz short @@x1 ; exit if not (i.e. if it's the list's end) ; ----------------- else... @@L1: ; loop to read the (Visual-Prolog-) arg-list inc ebx ; increment ebx (counter for number_of_args) lodsd ; load next list-element (arg. of function) push eax ; push it into the stack (for a function-call) lodsd ; load the pointer to next list-element in EAX mov esi,eax ; now ESI = (pointer-to-) next list-element lodsd ; load element-flag of next list-element dec al ; decrement it, to check if it was a 1 jz short @@L1 ; if so, not yet the list's end, so repeat! ; ================= else... @@x1: mov [fcnt],ebx ; store the number_of_args in local var. 'fcnt' call ecx ; call the (external) function (given in ARG-1) mov ecx,[fcnt] ; get the function's number of args from 'fcnt' @@L2: jcxz @@x2 ; if the called function had NO args, exit ; ------------------ else... @@L3: ; loop to POP function-args after the call pop edx ; recover next argument from the stack dec ecx ; decrement the remaining number_of_args jnz short @@L3 ; if not zeroed, continue popping args... ; ------------------ @@x2: pop ecx ; pop ebx ; pop edi ; pop esi ; LEAVE ENDP _apply_func
- A not-so-obvious advantage of this code is that any Prolog interpeters written on the basis of Visual Prolog’s “PIE engine” (such as G.I.S. Prolog) make extensive use of calls such as this, inside their inference engine; using Prolog-lists of arguments to be called by turning them into proper predicate calls of arity=N (where N is the size of the list). So, an Assembly language implementation of such a calling mechanism can speed up such an interpreter considerably, especially inside recursive calls or loops, which call other predicates repeatedly countless times…
- Another not-so-obvious advantage is that -in this way- we managed to… trick Visual Prolog into doing “forbidden” predicate calls, such as PRED(arg1,arg2,….), where both the predicate’s functor and the arguments may appear as static data, stored in a Visual Prolog facts’ database.
- Don’t ask me (yet) how to implement such tricks in Visual Prolog 6.* or 7.*; I still use the version 5.* compilers a lot, because of their speed, as well as robustness in foreign language calls.
Related articles by Zemanta
- How to Compile Software From Source Code [Command Line]
- Intel Says to Prepare For “Thousands of Cores”
- Back to Basics: Algorithms
- JIT in Prolog
- Revenge of the Nerds
- Dip into concept programming
- Google Code Search Shows Function Outline
- Ard-e: Cheap Arduino robot platform
- Get Started With Movable Type