Opcode definitions------------------ Opcodes are defined in include/isa.h, limbo/optab.c and libinterp/xec.c. consc is documented but does not exist in the opcode definitions. Function signatures------------------- Function signatures will be output by the Limbo compiler if you pass it the"-D S" debug option. For example, the Sys->print function has this summary output for it: signed Sys->print type fn(s: string, *): int len 6 sig 0xac849033 The type "fn(s: string, *): int" is how the function is declared in themodule/sys.m file. Function signatures are generated by the sign function in the limbo/types.cfile. We add the debug code after the md5 function call: md5(sig, sigend, md5sig, nil); if(debug['S']) { print("sign %D len %d\n", d, sigend); print("%s\n", (char*)sig); } This results in the following additional two debug lines: sign Sys->print len 6 f*(s)i The "f*(s)i" string is the function signature that needs to be hashed using theMD5 algorithm. Additionally, the following code is used to reduce the hashvalue to four bytes: for(i = 0; i < MD5dlen; i += 4) t->sig ^= md5sig[i+0] | (md5sig[i+1]<<8) | (md5sig[i+2]<<16) | (md5sig[i+3]<<24); We can reproduce this in Python using the md5 module, obtaining the hash valuethen combining each eight digit segment of it using XOR, before reversing theendianness of the resulting value: import md5 h = md5.md5("f*(s)i").hexdigest() l = map(lambda x: int(h[x:x+2], 16), range(0, 32, 2)) hd = sum(map(lambda x: reduce(lambda y, z: y ^ z, l[x::4]) << (x * 8), range(4))) The rtsign and idsign functions in the limbo/types.c file are responsible forcreating a signature for a function. The sigkind array contains the charactersused for each type of object that will be serialised. Operands-------- The double indirect operands SO(SO(fp)) and SO(SO(mp)) are represented by twooffsets in the encoded instruction. The first represents the "inner" offset,the second the "outer" offset. For example: 10(20(fp)) is represented as 20, 10 in the instruction. This was discovered by running the Limbo compiler with the -S option. Register allocation and stack frames------------------------------------ The following program was compiled using the Limbo compiler: implement count; include "sys.m"; include "draw.m"; count: module { init: fn(ctxt: ref Draw->Context, args: list of string); }; init(ctxt: ref Draw->Context, args: list of string) { sys := load Sys Sys->PATH; sys->print("Counting...\n"); for (i := 0; i < 20; i++) sys->print("%d\n", i); } It was disassembled to produce this listing: 0x0: load 0(mp), $0x0, 44(fp) 0x1: frame $0x1, 52(fp) 0x2: movp 8(mp), 32(52(fp)) 0x3: lea 48(fp), 16(52(fp)) 0x4: mcall 52(fp), $0x0, 44(fp) 0x5: movw $0x0, 40(fp) 0x6: blew $0x7b, 40(fp), $0xe 0x7: frame $0x1, 48(fp) 0x8: movp 4(mp), 32(48(fp)) 0x9: movw 40(fp), 36(48(fp)) 0xa: lea 52(fp), 16(48(fp)) 0xb: mcall 48(fp), $0x0, 44(fp) 0xc: addw $0x1, 40(fp) 0xd: jmp $0x6 0xe: ret entry 0x0, 2 desc $0x0, 16, "f0" desc $0x1, 40, "0080" desc $0x2, 56, "00d0" var @mp, 16 string @mp+0, "$Sys" string @mp+4, "%d\n" string @mp+8, "Counting...\n" module count link 0x0, 2, 0x4244b354, "init" ldts @ldt, 1 word @ldt+0,1 ext @ldt+4, 0xac849033, "print" source "/usr/david/count.b" The entry line gives the initial PC value and the type (2) containing stackframe information: desc $0x2, 56, "00d0" This type contains 56 bytes and the pointer bitmap indicates that offsets 32,36 and 44 are pointers: 0 4 8 12 16 20 24 28 | 32 36 40 44 48 52 56 60 * * * 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0x00d0 The code starts at pc=0x0 where it loads a module: 0x0: load 0(mp), $0x0, 44(fp) ; path, linkage descriptor, destination In this instruction, the path refers to this item in the data section: string @mp+0, "$Sys" The linkage descriptor refers to the first sequence of items in the ldts area(there is only one): word @ldt+0,1 ext @ldt+4, 0xac849033, "print" The result is placed in the pointer at 44(fp) which represents the sysvariable. A stack frame is created so that a function can be called: 0x1: frame $0x1, 52(fp) ; type index, destination This specifies the type (1) containing stack frame information: desc $0x1, 40, "0080" In this frame, only offset 32 is a pointer. The result of the frame instructionis held in 52(fp) which should be a pointer, but apparently isn't recorded asone. The next instruction copies the location of a string into the new frame at anaddress in the frame that is marked as a pointer: 0x2: movp 8(mp), 32(52(fp)) ; source, destination string @mp+8, "Counting...\n" 0x3: lea 48(fp), 16(52(fp))
Opcode definitions
------------------
Opcodes are defined in include/isa.h, limbo/optab.c and libinterp/xec.c.
consc is documented but does not exist in the opcode definitions.
Function signatures
-------------------
Function signatures will be output by the Limbo compiler if you pass it the
"-D S" debug option.
For example, the Sys->print function has this summary output for it:
signed Sys->print type fn(s: string, *): int len 6 sig 0xac849033
The type "fn(s: string, *): int" is how the function is declared in the
module/sys.m file.
Function signatures are generated by the sign function in the limbo/types.c
file. We add the debug code after the md5 function call:
md5(sig, sigend, md5sig, nil);
if(debug['S']) {
print("sign %D len %d\n", d, sigend);
print("%s\n", (char*)sig);
}
This results in the following additional two debug lines:
sign Sys->print len 6
f*(s)i
The "f*(s)i" string is the function signature that needs to be hashed using the
MD5 algorithm. Additionally, the following code is used to reduce the hash
value to four bytes:
for(i = 0; i < MD5dlen; i += 4)
t->sig ^= md5sig[i+0] | (md5sig[i+1]<<8) | (md5sig[i+2]<<16) | (md5sig[i+3]<<24);
We can reproduce this in Python using the md5 module, obtaining the hash value
then combining each eight digit segment of it using XOR, before reversing the
endianness of the resulting value:
import md5
h = md5.md5("f*(s)i").hexdigest()
l = map(lambda x: int(h[x:x+2], 16), range(0, 32, 2))
hd = sum(map(lambda x: reduce(lambda y, z: y ^ z, l[x::4]) << (x * 8), range(4)))
The rtsign and idsign functions in the limbo/types.c file are responsible for
creating a signature for a function. The sigkind array contains the characters
used for each type of object that will be serialised.
Operands
--------
The double indirect operands SO(SO(fp)) and SO(SO(mp)) are represented by two
offsets in the encoded instruction. The first represents the "inner" offset,
the second the "outer" offset. For example:
10(20(fp)) is represented as 20, 10 in the instruction.
This was discovered by running the Limbo compiler with the -S option.
Register allocation and stack frames
------------------------------------
The following program was compiled using the Limbo compiler:
implement count;
include "sys.m";
include "draw.m";
count: module
{
init: fn(ctxt: ref Draw->Context, args: list of string);
};
init(ctxt: ref Draw->Context, args: list of string)
{
sys := load Sys Sys->PATH;
sys->print("Counting...\n");
for (i := 0; i < 20; i++)
sys->print("%d\n", i);
}
It was disassembled to produce this listing:
0x0: load 0(mp), $0x0, 44(fp)
0x1: frame $0x1, 52(fp)
0x2: movp 8(mp), 32(52(fp))
0x3: lea 48(fp), 16(52(fp))
0x4: mcall 52(fp), $0x0, 44(fp)
0x5: movw $0x0, 40(fp)
0x6: blew $0x7b, 40(fp), $0xe
0x7: frame $0x1, 48(fp)
0x8: movp 4(mp), 32(48(fp))
0x9: movw 40(fp), 36(48(fp))
0xa: lea 52(fp), 16(48(fp))
0xb: mcall 48(fp), $0x0, 44(fp)
0xc: addw $0x1, 40(fp)
0xd: jmp $0x6
0xe: ret
entry 0x0, 2
desc $0x0, 16, "f0"
desc $0x1, 40, "0080"
desc $0x2, 56, "00d0"
var @mp, 16
string @mp+0, "$Sys"
string @mp+4, "%d\n"
string @mp+8, "Counting...\n"
module count
link 0x0, 2, 0x4244b354, "init"
ldts @ldt, 1
word @ldt+0,1
ext @ldt+4, 0xac849033, "print"
source "/usr/david/count.b"
The entry line gives the initial PC value and the type (2) containing stack
frame information:
desc $0x2, 56, "00d0"
This type contains 56 bytes and the pointer bitmap indicates that offsets 32,
36 and 44 are pointers:
0 4 8 12 16 20 24 28 | 32 36 40 44 48 52 56 60
* * *
0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0x00d0
The code starts at pc=0x0 where it loads a module:
0x0: load 0(mp), $0x0, 44(fp) ; path, linkage descriptor, destination
In this instruction, the path refers to this item in the data section:
string @mp+0, "$Sys"
The linkage descriptor refers to the first sequence of items in the ldts area
(there is only one):
word @ldt+0,1
ext @ldt+4, 0xac849033, "print"
The result is placed in the pointer at 44(fp) which represents the sys
variable.
A stack frame is created so that a function can be called:
0x1: frame $0x1, 52(fp) ; type index, destination
This specifies the type (1) containing stack frame information:
desc $0x1, 40, "0080"
In this frame, only offset 32 is a pointer. The result of the frame instruction
is held in 52(fp) which should be a pointer, but apparently isn't recorded as
one.
The next instruction copies the location of a string into the new frame at an
address in the frame that is marked as a pointer:
0x2: movp 8(mp), 32(52(fp)) ; source, destination
string @mp+8, "Counting...\n"
0x3: lea 48(fp), 16(52(fp))