Ryan --
 
> Speaking of the standard library, I cannot find any discussions regarding
> it.  It's certainly easy enough to create new opcode .so libraries with the
> opcode processing script.   Has there been any thought as to how these
> might be properly loaded?

I've thought about it a bit, but I don't think my thinking matches that of
Dan and some other folks. FWIW:

  * Add another segment to the packfile format, after the byte code. There
    are N entries, one each for the N ops used in the bytecode. Each entry
    consists of an opcode_t (32-bit) triple <OPCODE, OPLIB, OPNAME>. The
    OPCODE is as it appears in the bytcode stream. The OPLIB is an index
    into the const_table. The core ops point to the string 'core'. The
    OPNAME is an index into the const_table, the string there contains the
    full name of the op (with all arg type qualifiers).

  * At bytecode loading time, the loader constructs a custom opfunc and
    opinfo table for the bytecode, based on reading the oplist segment at
    the end of the packfile. It dynamically loads the oplib and indexes
    the ops by full name for fast access. NOTE: We could consider having
    the hash table, tree, or whatever for the lookup be an integral part
    of the oplib static data, produced automatically by ops2c.pl (see
    my recent moby.patch). This will save time during loading. Anyway,
    the loader pulls in pointers to the opfuncs and opinfos and stashes
    pointers to these tables into the bytecode-ref structure the
    interpreter keeps (member 'code' today). NOTE: Today, the interpreter
    has The One True oplist (opfunc and opinfo tables), but that will
    change.

    Now, the interpreter can go about its merry business much the way it
    does today, but the ops it is running are completely custom to the
    bytecod it is running.

    A suitable default of referencing all the core oplib's ops when no
    more specific op info is provided could be used, but I'd rather see
    all programs mention the ops they use explicitly. That way no matter
    what happens to the list of ops, as long as one you need doesn't
    disappear or change into something semantically incompatible with
    what it was when the program was compiled, your program will work
    because we are really referencing ops by name.

    I don't think making these custom optables on the fly should hurt
    performance, since it happens once at load time, and it shouldn't
    represent much work, and the total amount of work is reasonably
    bounded, since you won't reference ops you don't use (see below).

  * The oplist segment is produced automatically by the assembler. Each
    time an op is encountered and verified to exist, its assigned opcode
    is determined. If it hasn't been assigned an opcode, a new assignment
    is created. The oplist is the result of this activity.

    Originally, I was thinking we'd have to reserve opcode zero for
    core::end, but in moby.patch I changed the way the runops loop
    terminates from getting a pointer that dereferences to an opcode_t
    of zero to getting a NULL pointer. Now, the 'end' opcode is just a
    wrapper around the op directive (see core.ops) HALT. Other ops can
    HALT too, such as the flag-modifying ops. So, it really doesn't
    matter what opcode gets assigned to 'core::end'.

    When the assembler first comes alive, it has already done a 'use
    core' to reference the core ops. Any other 'use' directives in
    the assembler source cause the assembler to reference those libs,
    too. In the case of ops with the same name, the op from the more
    recently 'use'd oplib prevails. Explicit ops of the form core::end
    can be used to ensure a particular oplib's version of the op.

I'd love to code this up after moby.patch goes in, but as I said before,
I think that at least some of this conflicts with what other people
have in mind. Hopefully something of value can be taken from it, though...


Regards,

-- Gregor

Reply via email to