Updated version of the Dis Virtual Machine Specification

318 views
Skip to first unread message

da...@boddie.org.uk

unread,
May 8, 2017, 11:02:41 AM5/8/17
to inferno-os
I downloaded a PDF copy of the Dis Virtual Machine Specification from inferno-os.org but this appears to be incomplete: not all the opcodes are documented, for example. Is there an updated version of this document?

Although I've dug around in the Limbo compiler source code to try and see where the gaps in the specification are, it's not particularly easy to see the overall structure of what it's generating.

da...@boddie.org.uk

unread,
Jun 18, 2017, 10:45:22 AM6/18/17
to inferno-os
I promised that I'd write up something to fill in the gaps in the specification and haven't yet done so.
In the meantime, I've put the Python code I was writing to explore the VM into a repository:

  https://bitbucket.org/dboddie/dis-virtual-machine-tools-for-python

I'm not sure how much more I will do with this because it seems a bit more work to produce new
.dis files than I expected. I wanted to try and compile .dis files from source code written in other
languages, such as dialects of Python, in the same way that I've done for Dex files targeting the
Dalvik virtual machine and its successors.

Anyway, I hope someone finds it interesting/useful.

Go Phone

unread,
Sep 4, 2021, 1:12:07 PM9/4/21
to inferno-os
Moving this effort forward, I am considering a forth interpreter for the dis vm to remove the need for the binary .dis files. The reason being limbo is complicated and a black box. I am hoping text .dis files opens up inferno to other interpreters and also isolates complexity...

Any opinions, please?

Thanks

clasp126...@icebubble.org

unread,
Jan 29, 2022, 12:31:17 AM1/29/22
to inferno-os
Go Phone <gopho...@gmail.com> writes:

> Moving this effort forward, I am considering a forth interpreter for the
> dis vm to remove the need for the binary .dis files. The reason being limbo
> is complicated and a black box. I am hoping text .dis files opens up

s/box/\& and poorly-documented/

Sometimes, I think my main tool for learning limbo is the stream of error
messages I get from the limbo compiler. ;) What, I can't initialize a
variable to an array constant? That's not documented anywhere. What?
A variable goes out of scope outside an alt statement? That's not
documented anywhere. You get the idea. :(

> Any opinions, please?

I've been toying with the idea of writing a WAM --> dis compiler.
Because Prolog --> WAM (Warren Abstract Machine) compilers exist, a
wam2dis would enable Prolog programs to be compiled to dis executables.
It would be easier to write a WAM --> limbo compiler (analogous to
wamcc), but having to then compile the intermediate limbo code to dis
would probably be too slow.

Charles Forsyth

unread,
Jan 29, 2022, 8:24:39 AM1/29/22
to clasp126...@icebubble.org, inferno-os
) What, I can't initialize a
variable to an array constant?  That's not documented anywhere.  What?

The Limbo Programming Language, §8.2.10 Creation of arrays
and there are several examples of its use (eg, in §8.1.5 Function calls, example, and IrTest.init).
 

A variable goes out of scope outside an alt statement?  That's not
documented anywhere.  You get the idea.  :(

The Limbo Programming Language,  §11 Scopes (how they work in general, and the penultimate paragraph specifically about case alt and pick)

Charles Forsyth

unread,
Jan 29, 2022, 8:46:59 AM1/29/22
to inferno-os
The discussion in another thread prompts me to say that if anyone has a list of errors or omissions in either the Dis document or the Limbo reference, I'd be happy to have them, even in unpolished note form (or marked up pages). I can see that some or all of later Dis changes to support added language features are missing.

clasp126...@icebubble.org

unread,
Jan 30, 2022, 7:28:03 PM1/30/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

> The discussion in another thread prompts me to say that if anyone has a
> list of errors or omissions in either the Dis document or the Limbo
> reference, I'd be happy to have them, even in unpolished note form (or
> marked up pages). I can see that some or all of later Dis changes to
> support added language features are missing.

Oh, yes, there are many, many errors in the Dis spec PDF. I don't think
I made notes of them, but I recall seeing enough that I started to get
annoyed as I read through the spec. IIRC, a few of the pseudocode
definitions for some of the instructions were obviously erroneous. It
wasn't until I began reading the interpreter source (in search of the
correct definition(s)) that I realized there were additional opcodes
which had been added to Dis but, as you note, remain completely
undocumented. A while ago, I posted to this list, inquiring if there
was a corrected/updated version of the spec, but nobody seemed to know
of any.

clasp126...@icebubble.org

unread,
Jan 30, 2022, 7:28:04 PM1/30/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

>> ) What, I can't initialize a
>> variable to an array constant? That's not documented anywhere. What?
>
>
> *The Limbo Programming Language, *§8.2.10 Creation of arrays
> and there are several examples of its use (eg, in §8.1.5 Function calls,
> example, and IrTest.init).

LOL. You're not the person who wrote that paper, are you? :)

Here's an example of some code which elicits compiler errors when trying
to initialize an array variable:

implement ArrayInit;

include "sys.m";
sys: Sys;
include "draw.m";
draw: Draw;

# simple program to try different ways of initializing arrays in Limbo

Heart: adt {
pick {
GOOD => love: int;
EVIL => hate: int;
}
};

# none of these work:
#hearts: array of ref Heart = array[] of {ref Heart.EVIL(0), ref Heart.GOOD(1)};
#hearts: array of ref Heart.EVIL = array[] of {ref Heart.EVIL(0), ref Heart.EVIL(1)};
#h0: ref Heart.EVIL = ref Heart.EVIL(0);
#h1: ref Heart = ref Heart.GOOD(1);
#hearts := array[] of {h0, h1};
#hearts := array[] of {h0, h0};
#hearts := array[] of {h1, h1};

ArrayInit: module {
init: fn(nil: ref Draw->Context, argv: list of string);
};

init(nil: ref Draw->Context, argv: list of string) {
# but the SAME EXACT declaration works here...
hearts: array of ref Heart = array[] of {ref Heart.EVIL(0), ref Heart.GOOD(1)};
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
for (i := 0; i < len hearts; i++)
pick heart := hearts[i] {
GOOD =>
sys->print("yeay: %d\n", heart.love);
EVIL =>
sys->print("boo: %d\n", heart.hate);
}
}

Charles Forsyth

unread,
Jan 31, 2022, 12:29:32 AM1/31/22
to clasp126...@icebubble.org, inferno-os
See the rules about top level declarations in section 6.1 Data declarations

--
You received this message because you are subscribed to the Google Groups "inferno-os" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inferno-os+...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/inferno-os/86pmo8ojyg.fsf_-_%40cmarib.ramside.

clasp126...@icebubble.org

unread,
Jan 31, 2022, 7:54:54 PM1/31/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

> The discussion in another thread prompts me to say that if anyone has a
> list of errors or omissions in either the Dis document or the Limbo
> reference, I'd be happy to have them, even in unpolished note form (or
> marked up pages). I can see that some or all of later Dis changes to
> support added language features are missing.

I just discovered, today, that Limbo supports polymorphism via
templates. That's not documented anywhere, AFAIK. I happened to
stumble upon it while reading lists(2). I guess templates were added
after the language spec was written.

clasp126...@icebubble.org

unread,
Feb 2, 2022, 1:27:01 PM2/2/22
to inferno-os
So, evidently, Limbo supports polymorphism using templates. However,
Limbo's polymorphism doesn't seem to be documented. Anywhere. The only
thing I have to go on is some example code which uses it. See, i.e.,
grep -n '\]\(' /appl/lib/*.b. Unfortunately, the error messages from
the Limbo compiler for polymorphic errors are very confusing and
misleading... errors like "type T is incompatible with type T", and the
line numbers reported by the errors often point to locations far removed
from the location of the actual problem.

I still can't figure out how to get it working. (See the attached 5
files. They're small enough.) When I run mk, I get the following:

% mk
limbo -I/module -gw condmod.b
condmod.b:34: cannot determine the instantiated type of ValT
condmod.b:34: cannot determine the instantiated type of ValT
condmod.b:35: cannot determine the instantiated type of ValT
condmod.b:35: cannot determine the instantiated type of ValT
condmod.b:36: cannot determine the instantiated type of ValT
condmod.b:36: cannot determine the instantiated type of ValT
mk: limbo -I/module -gw condmod.b : exit status=608 "Sh":fail:errors

Again, the error messages aren't particularly helpful, and the line
numbers (which are repeated?) don't even suggest where to begin. Of
course, it would help if there was documentation to show what I SHOULD
be doing. So, can anybody here get this code to compile? Any help
would be greatly appreciated.

valref.m
valref.b
condmod.m
condmod.b
mkfile

clasp126...@icebubble.org

unread,
Feb 2, 2022, 1:27:01 PM2/2/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

> The discussion in another thread prompts me to say that if anyone has a
> list of errors or omissions in either the Dis document or the Limbo
> reference, I'd be happy to have them, even in unpolished note form (or
> marked up pages). I can see that some or all of later Dis changes to
> support added language features are missing.

While trying to figure out how to use Limbo's polymorphism support, I
perused the source for the limbo compiler, and discovered that it
recognizes four (new?) keywords which don't seem to be (adequately?)
documented:

dynamic exception fixed raises

The keywords "exception" and "raises" are described in the Limbo spec,
but Limbo doesn't actually appear to behave as described. In real life,
exceptions can be "raise"d just about anywhere, regardless of whether or
not they are declared in the function type. So, then, what purpose does
"raises" serve?

The keywords "dynamic" and "fixed" don't appear to be documented
anywhere.

Charles Forsyth

unread,
Feb 2, 2022, 2:53:56 PM2/2/22
to clasp126...@icebubble.org, inferno-os
The keywords "dynamic" and "fixed" don't appear to be documented
anywhere.

fixed is defined in the manual addendum (see /doc/limbo/addendum.pdf), with some other changes. 
dynamic is reserved for future use. 

 In real life,
exceptions can be "raise"d just about anywhere, regardless of whether or
not they are declared in the function type.  So, then, what purpose does
"raises" serve?

raises becomes part of the signature. the failure to declare it produces
a warning, but that's actually supposed to be an error! 

h% limbo -gw t.b
t.b:15: warning: function 'init' raises Froggy but not declared

Charles Forsyth

unread,
Feb 2, 2022, 3:03:47 PM2/2/22
to clasp126...@icebubble.org, inferno-os
Polymorphism is discussed briefly in /doc/changes.pdf
based on a similar addition (unofficial, I think) to Oberon: https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.48.8930&rep=rep1&type=pdf
restricting the parameter type to a reference, but in this case allowing certain operations on the type to be required, listing them in a "for" clause,
in the form of a sequence of fn declarations. The restriction to reference types was fairly limiting, so it never really became official.
The constraint specification was loosely modelled on CLU's, but since there wasn't the equivalent to a cluster, it became clear that the things would need to be named to be useful.
I know why there was pressure to add it, but in retrospect, it would have been better to put the effort first into first-class functions, which would have been easier to make complete and are more generally useful.

Charles Forsyth

unread,
Feb 2, 2022, 3:08:07 PM2/2/22
to clasp126...@icebubble.org, inferno-os
This is just the sort of information I was looking for, thank you: things that are missing or at best in strange places in the documentation.

Sean Hinchee

unread,
Feb 2, 2022, 4:31:37 PM2/2/22
to Charles Forsyth, clasp126...@icebubble.org, inferno-os
Agree on polymorphism, especially with ADTs and the for{} block after a function signature, as shown here: 


I compiled most of these examples by reading existing Inferno code rather than specs. 

As I recall I struggled on interpreting the interactions between pick ADTs and polymorphic ADTs. 

Maybe I missed it in the documents, but pattern matched initialization of arrays was something I stumbled upon, but didn’t get out of the docs:

    buf := array[10] of {0 to 2 => byte 4, * => byte 3};

Cheers,
Sean


On Wednesday, February 2, 2022, Charles Forsyth <charles...@gmail.com> wrote:
This is just the sort of information I was looking for, thank you: things that are missing or at best in strange places in the documentation.

--
You received this message because you are subscribed to the Google Groups "inferno-os" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inferno-os+unsubscribe@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/inferno-os/CAOw7k5iOKiaRCNFKBCG_SMH2S9JyZobXHOB4pH15KH-TBip3PQ%40mail.gmail.com.

Sean Hinchee

unread,
Feb 2, 2022, 4:32:52 PM2/2/22
to clasp126...@icebubble.org, inferno-os
fwiw here’s my attempt at drawing up example code for polymorphism:


Cheers,
Sean 
--
You received this message because you are subscribed to the Google Groups "inferno-os" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inferno-os+unsubscribe@googlegroups.com.

Charles Forsyth

unread,
Feb 2, 2022, 5:07:44 PM2/2/22
to Sean Hinchee, clasp126...@icebubble.org, inferno-os
Maybe I missed it in the documents, but pattern matched initialization of arrays was something I stumbled upon, but didn’t get out of the docs:

Thanks! Things people find somehow obscure are useful too. The interaction between scopes and initialisers (having to be constant when global) is an earlier example.


On Wed, 2 Feb 2022 at 21:31, Sean Hinchee <henes...@gmail.com> wrote:
Agree on polymorphism, especially with ADTs and the for{} block after a function signature, as shown here: 


I compiled most of these examples by reading existing Inferno code rather than specs. 

As I recall I struggled on interpreting the interactions between pick ADTs and polymorphic ADTs. 

Maybe I missed it in the documents, but pattern matched initialization of arrays was something I stumbled upon, but didn’t get out of the docs:

    buf := array[10] of {0 to 2 => byte 4, * => byte 3};

Cheers,
Sean

On Wednesday, February 2, 2022, Charles Forsyth <charles...@gmail.com> wrote:
This is just the sort of information I was looking for, thank you: things that are missing or at best in strange places in the documentation.

--
You received this message because you are subscribed to the Google Groups "inferno-os" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inferno-os+...@googlegroups.com.

Charles Forsyth

unread,
Feb 2, 2022, 5:10:45 PM2/2/22
to clasp126...@icebubble.org, inferno-os
It isn't "templates" (in the C++ sense) but type parameters ("parametric polymorphism" in Strachey's sense), with constraints, very much along the lines of CLU's "where" clauses.

--
You received this message because you are subscribed to the Google Groups "inferno-os" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inferno-os+...@googlegroups.com.

Sean Hinchee

unread,
Feb 2, 2022, 5:14:57 PM2/2/22
to Charles Forsyth, clasp126...@icebubble.org, inferno-os
Agree on scope, too :)

One other thing maybe worth voicing here, I wasn't sure if it was
possible, but I was never able to get a polymorphic function on top of
a polymorphic ADT (method?) declared.

I'm thinking something along the lines of the polymorphic function
ismember() from lists.b:

ismember[T](x: T, l: list of T): int
for { T => eq: fn(a, b: T): int; }
{
for(; l != nil; l = tl l)
if(T.eq(x, hd l))
return 1;
return 0;
}

but written like a method over a polymorphic ADT (from above generics.b):

# Skip white space
Rd[T].ws(rd: self ref Rd[T]): int {
while(isspace(c := rd.t.getb()))
{}

return c;
}

maybe this is beyond the scope of Limbo's polymorphism goal, but if
there's a specific syntax I'd love to see it shown :)

Cheers,
Sean

Charles Forsyth

unread,
Feb 2, 2022, 5:45:11 PM2/2/22
to clasp126...@icebubble.org, inferno-os
 # the limbo compiler chokes on this...
conds := Cond.zero("debug")
:: Cond.equal("product", SVal("chair"))
:: Cond.range("qty", IVal(2), IVal(3)) :: nil;

It's been some time, but I'm fairly sure the problem is that you're creating a list, which must be a list of T for some one T, but here T must simultaneously be SVal and IVal (by unification).

Charles Forsyth

unread,
Feb 2, 2022, 8:25:58 PM2/2/22
to clasp126...@icebubble.org, inferno-os
The actual diagnostics are:
condmod.b:22: return 'Cond.equal(name, IVal(0))' of type ref Cond[ValT] from a fn of ref Cond[ValT]
condmod.b:30: cannot determine the instantiated type of ValT
condmod.b:31: cannot determine the instantiated type of ValT
condmod.b:32: cannot determine the instantiated type of ValT
condmod.b:31: type clash in 'Cond.equal("product", SVal("chair"))' of type ref Cond[ValT] :: 'Cond.range("qty", IVal(2), IVal(3))::nil' of type list of ref Cond[ValT]

which is tricky to follow because ... it's showing the uninstantiated types Cond[ValT] and not the different instantiations. I'm not sure yet how hard that is to fix.

Kristof Wycz

unread,
Feb 2, 2022, 8:28:50 PM2/2/22
to Charles Forsyth, inferno-os
It's been a while since I've needed to generate dis code, and my
memory is fading, but I recall the following:

* Goto's secret table length entry isn't documented.
* There's some ambiguity about where the pc goes in a case table.
* Casec's table is documented as having the same format as case's
table, but casec's table entries have only two fields.
* Neither case's nor casec's tables are documented as having entries
for the default case.
* The array addressing instructions are documented as writing to their
destination operand, when they actually write to their middle operand.
* The use of the terms src1 & src2 throughout is a little odd, especially
since the sections lifted from dis(6) use source & middle.
* The 386 code generator takes some short-cuts which produce gibberish
when a branch or array addressing instruction has coalesced mid & dst
operands. It's easy enough to use the expanded encoding instead, but
an urk couldn't hurt.

I never thought the compiler's polymorphism error messages were too
bad, and found them to be an effective tutorial. Overall I think the
compiler's messages are pretty good, and I can't recollect ever having
been misled by them.

Charles Forsyth

unread,
Feb 2, 2022, 8:30:49 PM2/2/22
to clasp126...@icebubble.org, inferno-os
At the time, the idea was that the array initialisers at global level would be constants, efficiently represented as static values in the Dis, set up by the Dis loader, with run-time evaluation included instead in the init() function, invoked by the process that loads the module.

Brucee has a variant using the "load" keyword as a function name to mark code that should automatically be invoked when the module is loaded, similar I suppose to the later use of a special "init()" function in Go (which might have been inspired by the former). Go anyway doesn't require static values in as many contexts.

In this case a future version of limbo would evaluate those declaration  expressions at load time.

clasp126...@icebubble.org

unread,
Feb 3, 2022, 2:35:49 AM2/3/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

> fixed is defined in the manual addendum (see /doc/limbo/addendum.pdf), with
> some other changes.

Oh, I didn't realize there was an addendum. Perhaps someone should
point at that document more loudly. Most of the advice is to read
"Descent Into Limbo" and "The Limbo Programming Language". Nobody ever
mentioned the addendum to me before.

W/R/T exceptions: The addendum contains a good description of exception
handling, with more precise detail and examples, which is what was
missing from the description in TLPL. Note that the description states
that the "raises" clauses of a function definition and its declaration
must be "compatible," but it doesn't define what it means to be
"compatible." Must they be identical? Or can one be a subset of the
other (like with modules)?

W/R/T the "fixed" keyword: Wow! I never even knew that Limbo supported
fixed-point arithmetic. That's really cool. But I'm left wondering if
the fixed-point arithmetic (stored, internally, as ints) makes any use
of the floating-point Dis instructions. Could fixed-point code be run,
for example, on a Dis without an FPU? Most of the functions in math(2),
obviously, would require casting operands to real, then casting the
result back to fixed-point. But I imagine that most of the integer
arithmetic could be done on fixed-point values without ever touching the
FPU. Likewise, it seems that casting to/from fixed-point, int/big, and
floating-point could be done without an FPU, as well.

> Polymorphism is discussed briefly in /doc/changes.pdf

I think someone should be pointing more loudly at that document, as
well. Hey, I see that you're actually one of the authors of it. :D
This PDF is the only place in the Inferno documentation where I see any
description of the syntax for polymorphism in Limbo. The text states
that changes to Dis would need to be made to support polymorphism for
value types such as int, big, and real. What sort of changes to Dis
would be required? It also says that details will be provided in a
future "note." Does that note exist?

(No need to CC: me, BTW. I'm on the list.)

clasp126...@icebubble.org

unread,
Feb 3, 2022, 2:35:49 AM2/3/22
to inferno-os
Kristof Wycz <krist...@gmail.com> writes:

> * The array addressing instructions are documented as writing to their
> destination operand, when they actually write to their middle operand.
> * The use of the terms src1 & src2 throughout is a little odd, especially
> since the sections lifted from dis(6) use source & middle.

Yes. Now that you mention them, I remember seeing those errors, as
well.

clasp126...@icebubble.org

unread,
Feb 3, 2022, 2:35:50 AM2/3/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

> The actual diagnostics are:
> condmod.b:22: return 'Cond.equal(name, IVal(0))' of type ref Cond[ValT]
> from a fn of ref Cond[ValT]
> condmod.b:30: cannot determine the instantiated type of ValT
> condmod.b:31: cannot determine the instantiated type of ValT
> condmod.b:32: cannot determine the instantiated type of ValT
> condmod.b:31: type clash in 'Cond.equal("product", SVal("chair"))' of type
> ref Cond[ValT] :: 'Cond.range("qty", IVal(2), IVal(3))::nil' of type list
> of ref Cond[ValT]

How did you get those extra error messages? Limbo doesn't give me that
verbiage about "Cond.equal" (not that it's very helpful).

> which is tricky to follow because ... it's showing the uninstantiated types
> Cond[ValT] and not the different instantiations. I'm not sure yet how hard
> that is to fix.

Yeah, an error which reports that ref Cond[ValT] isn't compatible with
ref Cond[ValT] is... well, confusing at best. (Those LOOK compatible
to me.)

> It's been some time, but I'm fairly sure the problem is that you're
> creating a list, which must be a list of T for some one T, but here T must
> simultaneously be SVal and IVal (by unification).

Originally, I had tried to declare:

Cond: adt {
name: string;
min, max: ref ValRef->Val;
# ...
};

But I had trouble assigning values to min and max. (The compiler was
telling me that the type ref ValRef->Val[ValT] couldn't be matched to
ref ValRef->Val[ref ValRef->IntVal], or something like that.) So, I
tried making CondMod->Cond a type-parameterized ADT, as:

Cond: adt[ValT] for {
ValT => less: fn(n: ValT, m: ValT): int;
} {
name: string;
min, max: ref ValRef->Val[ValT];
# ...
}

As you've seen, that makes it impossible to include them together in the
same list. :(

Is there some way to "wrap" parameterized types in an ADT such that they
can be passed-around as values of the same abstract type? You can do
that with pick ADTs by casting values of, for example, type ref
PickAdt.Subtype to ref PickAdt, and treating them all as ref PickAdt.
Is there any way to do something like that with parameterized types?

clasp126...@icebubble.org

unread,
Feb 6, 2022, 6:20:20 PM2/6/22
to inferno-os
clasp126...@icebubble.org writes:

> Charles Forsyth <charles...@gmail.com> writes:
>
>> The actual diagnostics are:
>> condmod.b:22: return 'Cond.equal(name, IVal(0))' of type ref Cond[ValT]
>> from a fn of ref Cond[ValT]
>> condmod.b:30: cannot determine the instantiated type of ValT
>> condmod.b:31: cannot determine the instantiated type of ValT
>> condmod.b:32: cannot determine the instantiated type of ValT
>> condmod.b:31: type clash in 'Cond.equal("product", SVal("chair"))' of type
>> ref Cond[ValT] :: 'Cond.range("qty", IVal(2), IVal(3))::nil' of type list
>> of ref Cond[ValT]

Never mind. I figured it out. You have to train your brain to ignore
the (meaningless, misleading) error messages generated for polymorphic
types, and learn to read the compiler's mind. Once you get it right,
though, the code becomes just beautiful. A little wart here, a freckle
there, but smooth as silk everywhere else. It's king of like coding
Cindy Crawford. Limbo's grown a bit wrinkly since 1997, but still sexy.

BTW, "cannot determine the instantiated type of..." usually means that
you're trying to do something polymorphic which doesn't need to be done
polymorphically.

clasp126...@icebubble.org

unread,
Feb 10, 2022, 7:58:08 PM2/10/22
to inferno-os
So, I've made quite a bit of progress on my polymorphic code, but have
run into a problem which has me stumped. This code compiles fine but
when I run it, half-way through, it throws an exception "module not
loaded". I've combed over this code several times... what am I missing?
A mkfile is included, just "mk test". Or just "limbo *.b".

storer.b
valref.m
valref.b
mkfile

clasp126...@icebubble.org

unread,
Feb 12, 2022, 9:56:06 PM2/12/22
to inferno-os
I'm pretty sure, now, that this is a bug in the Limbo compiler. When I
try to compile this module (Limbarf), the limbo compiler (the compiler,
itself) dies with the exception "Limbo":dereference of nil. This code
was one of my attempts to get some other code working (a distilled
version of which is attached). This module, Limbarf, can be used to
reproduce the compiler's nil pointer dereference:

implement Limbarf;

Limbarf: module {

Foo: adt {
kick: fn[T](it: T);
};

Bar: adt[T] for {
T => kick: fn[T](it: T);
} {
butt: T;

kick: fn[T](it: ref Bar[T])
for { T => kick: fn[T](it: T);
};
};
};

Foo.kick[T](me: T) {
}

Bar[T].kick[T](its: ref Bar[T])
for { T => kick: fn[T](it: T);
} {
its.butt.kick(its.butt);
}

kicker.b
foobar.m
foobar.b
mkfile

da...@boddie.org.uk

unread,
Feb 13, 2022, 5:35:46 PM2/13/22
to inferno-os
In storer.b, things execute fine until the line

        putval("345", bar);

I wondered if the polymorphism features of the language require a module to be loaded that support them.
Running disdump on them didn't provide any clues.

clasp126...@icebubble.org

unread,
Feb 13, 2022, 10:59:15 PM2/13/22
to inferno-os
Sean Hinchee <henes...@gmail.com> writes:

> One other thing maybe worth voicing here, I wasn't sure if it was
> possible, but I was never able to get a polymorphic function on top of
> a polymorphic ADT (method?) declared.

...

> # Skip white space
> Rd[T].ws(rd: self ref Rd[T]): int {
> while(isspace(c := rd.t.getb()))
> {}
>
> return c;
> }

This will work (and it works in generics.b) as long as it's in the same
module. If you try importing the polymorphic ADT (Rd, in this case)
from another module, things start to melt-down. (Try it.)

"da...@boddie.org.uk" <da...@boddie.org.uk> writes:

> In storer.b, things execute fine until the line
>
> putval("345", bar);

The odd thing is that it WORKS on the first invocation, where the
argument is a pick ADT, but fails in the second invocation, where the
argument is a polymorthic ADT.

> I wondered if the polymorphism features of the language require a module to
> be loaded that support them.

Well, the obvious soulution would be to specify from which module
variable to import the fromstring function (method), something like:

putval("345", valref->bar);

But the compiler objects to such an attempt to module-qualify the
variable, and complains that bar isn't a member of ValRef (which is
correct). I would THINK that bar would get its implementation from Val,
which is specifically imported from ValRef at the top of storer.b. I
can't find anyplace in Limbo's syntax that allows me to provide any
additional module qualification to appease the compiler. :(

> Running disdump on them didn't provide any clues.

I ran wm/deb on the code, and discovered that it was failing where
Val[ValT].fromstring calls ValT.fromstring, here:

Val[ValT].fromstring(n: self ref Val[ValT], strval: string): ref Val[ValT] {
if (n.vref != nil) return ref Val[ValT](n.vref.fromstring(strval));
else return nil;
}

It looks like the Limbo compiler has some trouble figuring out which
implementation module is associated with the ADT data member vref. I
think I've stumbled upon a bug in the compiler, here. I tried
simplifying the code to see if I could isolate the problem (see the
other attachment I posted, kicker.b) but the limbo compiler can't even
compile it. The compiler dies with "Limbo":dereference of nil. So, it
looks like there are some cases (i.e., this one) which the compiler
isn't handling correctly.

Unfortunately, debugging the compiler is a bit beyond me. The code is a
bit ...cryptic, and the principles of its internal operation don't
appear to be documented anywhere. I'd have to spend a lot of time
studying the compiler to figure out where the problem is. I'm hoping
someone on this list with a better understanding of the compiler's
internals will be able to identify what's broken.

clasp126...@icebubble.org

unread,
Feb 13, 2022, 10:59:15 PM2/13/22
to inferno-os
David Boddie <da...@boddie.org.uk> writes:

> It turned out that I misunderstood how some of the support functions are
> supposed to work, so I had working versions of _div, _divu, _mod and _modu
> for simple use cases, but I had missed that the linker generates code to
> call them in a particular way. Since the MCU provides SDIV and UDIV
> instructions, I moved those functions into the linker.

I presume you're talking about the interpreter's JIT? That's another
thing which is missing from the Dis docs. There's no specification (or
explanation or even description) of the JIT architecture. That's really
important to have for porting Dis to a new architecture. As it stands,
you just have to read the kernel source and figure it out for yourself.

da...@boddie.org.uk

unread,
Feb 14, 2022, 7:37:32 AM2/14/22
to inferno-os
On Monday 14 February 2022, clasp126 wrote:
Actually, I was talking about the utils/tl linker. I didn't enable the JIT for
Thumb code because the implementation is missing. (I see that it was deleted in
2013, so I might bring it back.)

clasp126...@icebubble.org

unread,
Feb 15, 2022, 5:04:50 PM2/15/22
to inferno-os
So, I've made a bit more progress getting polymorphic code to both
compile AND run (yeay!). In the attached buttkicker.b, the first
invocation of kicker() works fine, but the compiler won't accept a
polymorphic ADT as an argument. (The error messages aren't much help,
either. The compiler could tell me that "peanuts are incompatible with
popcorn on line -3", and it would be just about as helpful.) I'm also
at a loss to figure out how to make the "ref" argmuents "self ref"
arguments. Whenever I try, the compiler starts to get cranky again. :(

A couple of times, while experimenting with the code, emu threw errors:
Broken: "sys: segmentation violation addr=0xbadc0bfe". Bad coffee?
That's got to be a joke. It looks like the error is generated in
Linux/os.c:74, static void trapmemref, and derives the address from:

include/pool.h: MAGIC_F = 0xbadc0c0a, /* Free block */

OK, so it's bad c0c0a, not c0ffe...

But I thought that the Dis VM made it impossible to access out-of-bounds
memory. How is this even possible? At the very least, it's clear that
there's something very wrong in the compiler. I get the vague, general
impression that the polymorphic support in Limbo isn't quite ready for
prime time yet. Unless code like this can be made to work (meaning both
compile AND run w/o throwing mysterious exceptions), I don't see how
polymorphism in Limbo could really be used put to serious use. In order
to be useful, in practice, it would at least have to have working
support for generic functions.

buttkicker.b
foobar.m
foobar.b
mkfile

clasp126...@icebubble.org

unread,
Feb 23, 2022, 2:25:51 PM2/23/22
to inferno-os
Charles Forsyth <charles...@gmail.com> writes:

> The discussion in another thread prompts me to say that if anyone has a
> list of errors or omissions in either the Dis document or the Limbo
> reference, I'd be happy to have them, even in unpolished note form (or
> marked up pages). I can see that some or all of later Dis changes to
> support added language features are missing.

Additionally:

(1) I think the description of the syntax for declaration of a
user-defined exception is missing a semicolon.

(2) The section on module declarations doesn't say that a module
declaration can contain an exception declaration.

(3) Evidently, you can't "import" from a data member of an ADT, even
if that ADT member is of the appropriate module type. (Why? Is
this documented anywhere?)

Charles Forsyth

unread,
Mar 5, 2022, 11:16:22 AM3/5/22
to clasp126...@icebubble.org, inferno-os
 (3) Evidently, you can't "import" from a data member of an ADT, even
     if that ADT member is of the appropriate module type.  (Why?  Is
     this documented anywhere?)

It's determined by the syntax: identifier-list : import identifier ;

where only an identifier is allowed.

 (1) I think the description of the syntax for declaration of a
     user-defined exception is missing a semicolon.

 (2) The section on module declarations doesn't say that a module
     declaration can contain an exception declaration.

yes, you're right.

 

Charles Forsyth

unread,
Mar 5, 2022, 11:39:10 AM3/5/22
to clasp126...@icebubble.org, inferno-os
The yacc grammar is superficially more permissive:   ids ':' Limport exp ';'
but anything more than a name (identifier) is rejected during semantic checks.
Allowing an arbitrary expression would need more work.
The imported identifiers are resolved using the current value of the module variable (allowing dynamically changing the module instance).


Reply all
Reply to author
Forward
0 new messages