Programming, Software and Code

Writing Ops in PIR



I put together an interesting little code snippet a few days ago that, when finished, would allow writing ops in PIR. I am probably not going to complete work on this idea myself (at least, not any time soon), so I want to put the partially-written code up here for other people to look at and maybe run with.

BEGIN_OPS_PREAMBLE

opcode_t *
Parrot_pir_op_lib_wrapper_func(opcode_t *cur_opcode, PARROT_INTERP)
{
// TODO: Set dest to the address of the next instruction, after all arguments.
// need to parse the number of args from the op signature
opcode_t * dest = cur_opcode + 3;
INTVAL opnum = (INTVAL)(*cur_opcode);
char * const opname = interp->op_info_table[opnum]->name;
STRING * name = string_make(interp, opname, 8, "ascii",
PObj_constant_FLAG|PObj_external_FLAG);
PMC * const root_ns = interp->root_namespace;
PMC * const ns = Parrot_get_namespace_keyed(interp, root_ns, CONST_STRING(interp, "pir_op_lib"));
PMC * sub = VTABLE_get_pmc_keyed_str(interp, ns, name);

// TODO: Need to convert the op signature into a PCC signature here
PMC * call_sig = Parrot_pcc_build_sig_object_from_op(interp, PMCNULL, signature,
cur_opcode);
Parrot_pcc_set_signature(interp, CURRENT_CONTEXT(interp), call_sig);
interp->current_cont = NEED_CONTINUATION;

dest = VTABLE_invoke(interp, sub, dest);
goto ADDRESS(dest);
}

END_OPS_PREAMBLE

op register_op_sub(IN STR, IN STR, IN PMC) {
PMC * const root_ns = interp->root_namespace;
PMC * const sub = $3;
PMC * const key = Parrot_pmc_new(interp, enum_class_ResizableStringArray);
STRING * const opname = $1;
STRING * const sig = $2;
PMC * const ns = Parrot_get_namespace_keyed(interp, root_ns, CONST_STRING(interp, "pir_op_lib"));
INTVAL argcount = 3; // TODO: Count number of args in signature
STRING * const separator = CONST_STRING(interp, "_");
STRING * longname = Parrot_str_append(interp, opname, separator);
longname = Parrot_str_append(interp, longname, sig);

VTABLE_set_string_keyed_int(interp, key, 0, );

VTABLE_set_pmc_keyed_str(interp, ns, $2, sub);
op_info_t * new_op = (op_info_t *)malloc(sizeof(op_info_t));
new_op->name = Parrot_str_to_cstring(interp, opname);
new_op->full_name = Parrot_str_to_cstring(interp, longname);
new_op->jump = 0;
new_op->opcount = argcount + 1;

// TODO: Initialize ->type based on what's in signature
new_op->type = (arg_type_t *)malloc(sizeof(arg_type_t) * PARROT_MAX_ARGS);

// TODO: Initialize based on the signature
new_op->dirs = (arg_dir_t *)malloc(sizeof(arg_dir_t) * PARROT_MAX_ARGS);

// TODO: what is this?
new_op->labels = (char *)malloc(sizeof(char) * PARROT_MAX_ARGS);

// TODO: Insert the new op into the optable.
}

This code, intended to become a dynop library one day, introduces a single new op: register_op_sub. register_op_sub takes three arguments: the string name of the new op, the string signature of the new op, and the Sub PMC for the new op. An entry is prepared for the interpreter's op_lib table (I haven't found a good way to insert that record yet), and the sub is stored in the "pir_op_lib" namespace. The body of the new op is the function Parrot_pir_op_lib_wrapper. This function body looks up the name of the current op in the op_lib table, then looks up the Sub PMC in the namespace, and will attempt to invoke that sub.

I haven't worked out all the mechanics of everything, obviously. But once a few more questions get answered and a modest amount of code gets written, this could turn into a very useful and very cool extension library for Parrot.

This entry was originally posted on Blogger and was automatically converted. There may be some broken links and other errors due to the conversion. Please let me know about any serious problems.