Author: leo
Date: Thu Apr 28 04:46:37 2005
New Revision: 7936
Added:
trunk/ops/pic.ops
Modified:
trunk/MANIFEST
trunk/imcc/pbc.c
trunk/include/parrot/mmd.h
trunk/include/parrot/pic.h
trunk/ops/ops.num
trunk/src/interpreter.c
trunk/src/mmd.c
trunk/src/packdump.c
trunk/src/packfile.c
trunk/src/pdump.c
trunk/src/pic.c
Log:
PIC 2 - infix_p_p and inline_sub_p_p
Experimental PIC support, mainly to show the potential for perfomance.
* only the inplace infix_ic_p_p opcode
* one inline variant sub_p_p for the MOPS benchmark
The inline variant is about 2,5 times faster then all other run cores and
even faster then native integer MOPS with the function core.
---
* fix pdump utility
Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST (original)
+++ trunk/MANIFEST Thu Apr 28 04:46:37 2005
@@ -1541,6 +1541,7 @@
ops/object.ops []
ops/obscure.ops []
ops/ops.num [devel]
+ops/pic.ops []
ops/pmc.ops []
ops/python.ops []
ops/rx.ops []
Modified: trunk/imcc/pbc.c
==============================================================================
--- trunk/imcc/pbc.c (original)
+++ trunk/imcc/pbc.c Thu Apr 28 04:46:37 2005
@@ -59,6 +59,7 @@
struct cs_t *prev; /* previous code segment */
struct cs_t *next; /* next code segment */
SymReg * key_consts[HASH_SIZE]; /* cached key constants for this seg */
+ int pic_idx; /* next index of PIC */
};
static struct globals {
@@ -1056,14 +1057,24 @@
constant_folding(interpreter, unit);
store_sub_size(code_size, ins_size);
bytes = (oldsize + code_size) * sizeof(opcode_t);
+ /*
+ * allocate code and pic_index
+ *
+ * pic_index is half the size of the code, as one PIC-cachable opcode
+ * is at least two opcodes wide - see below how to further decrease
+ * this storage
+ */
if (interpreter->code->base.data) {
interpreter->code->base.data =
mem_sys_realloc(interpreter->code->base.data, bytes);
+ interpreter->code->pic_index->data =
+ mem_sys_realloc(interpreter->code->pic_index->data, bytes/2);
} else {
- interpreter->code->base.data =
- mem_sys_allocate(bytes);
+ interpreter->code->base.data = mem_sys_allocate(bytes);
+ interpreter->code->pic_index->data = mem_sys_allocate(bytes/2);
}
interpreter->code->base.size = oldsize + code_size;
+ interpreter->code->pic_index->size = (oldsize + code_size)/2;
pc = (opcode_t*) interpreter->code->base.data + oldsize;
npc = 0;
/* add debug if necessary */
@@ -1131,8 +1142,23 @@
if (debug_seg) {
debug_seg->base.data[ins_line++] = (opcode_t) ins->line;
}
+ op = (opcode_t)ins->opnum;
+ /* add PIC idx */
+ if (parrot_PIC_op_is_cached(interpreter, op)) {
+ size_t offs = pc - interpreter->code->base.data;
+ /*
+ * for pic_idx fitting into a short, we could
+ * further reduce the size by storing shorts
+ * the relation code_size / pic_index_size could
+ * indicate the used storage
+ *
+ * drawback: if we reach 0xffff, we'd have to resize again
+ */
+ interpreter->code->pic_index->data[offs / 2] =
+ ++globals.cs->pic_idx;
+ }
/* Start generating the bytecode */
- *pc++ = op = (opcode_t)ins->opnum;
+ *pc++ = op;
/* Get the info for that opcode */
op_info = &interpreter->op_info_table[op];
IMCC_debug(interpreter, DEBUG_PBC, "%d %s", npc, op_info->full_name);
Modified: trunk/include/parrot/mmd.h
==============================================================================
--- trunk/include/parrot/mmd.h (original)
+++ trunk/include/parrot/mmd.h Thu Apr 28 04:46:37 2005
@@ -28,6 +28,19 @@
/* compare */
INTVAL mmd_dispatch_i_pp(Parrot_Interp, PMC *, PMC *, INTVAL);
+/* function typedefs */
+typedef PMC* (*mmd_f_p_ppp)(Interp *, PMC *, PMC *, PMC *);
+typedef PMC* (*mmd_f_p_pip)(Interp *, PMC *, INTVAL, PMC *);
+typedef PMC* (*mmd_f_p_pnp)(Interp *, PMC *, FLOATVAL, PMC *);
+typedef PMC* (*mmd_f_p_psp)(Interp *, PMC *, STRING *, PMC *);
+
+typedef void (*mmd_f_v_pp)(Interp *, PMC *, PMC *);
+typedef void (*mmd_f_v_pi)(Interp *, PMC *, INTVAL);
+typedef void (*mmd_f_v_pn)(Interp *, PMC *, FLOATVAL);
+typedef void (*mmd_f_v_ps)(Interp *, PMC *, STRING *);
+
+typedef INTVAL (*mmd_f_i_pp) (Interp *, PMC *, PMC *);
+
void mmd_add_by_class(Parrot_Interp, INTVAL, STRING *, STRING *, funcptr_t);
void mmd_register(Parrot_Interp, INTVAL, INTVAL, INTVAL, funcptr_t);
void mmd_register_sub(Parrot_Interp, INTVAL, INTVAL, INTVAL, PMC*);
Modified: trunk/include/parrot/pic.h
==============================================================================
--- trunk/include/parrot/pic.h (original)
+++ trunk/include/parrot/pic.h Thu Apr 28 04:46:37 2005
@@ -57,7 +57,7 @@
struct Parrot_pic_store_t *prev; /* prev pic_store */
size_t usable; /* size of usable memory: */
Parrot_PIC *pic; /* from rear */
- Parrot_MIC **mic; /* idx access to allocated MICs */
+ Parrot_MIC *mic; /* idx access to allocated MICs */
size_t n_mics; /* range check, debugging mainly */
} Parrot_PIC_store;
@@ -69,6 +69,10 @@
Parrot_MIC* parrot_PIC_alloc_mic(Interp*, size_t n);
Parrot_PIC* parrot_PIC_alloc_pic(Interp*);
+void parrot_pic_find_infix_v_pp(Interp *, PMC *left, PMC *right,
+ Parrot_MIC *mic, opcode_t *cur_opcode);
+void * parrot_pic_opcode(Interp *, int op);
+
#endif /* PARROT_PIC_H_GUARD */
/*
Modified: trunk/ops/ops.num
==============================================================================
--- trunk/ops/ops.num (original)
+++ trunk/ops/ops.num Thu Apr 28 04:46:37 2005
@@ -1328,3 +1328,5 @@
find_name_p_s 1298
find_name_p_sc 1299
backtrace 1300
+pic_infix___ic_p_p 1301
+pic_inline_sub___ic_p_p 1302
Added: trunk/ops/pic.ops
==============================================================================
--- (empty file)
+++ trunk/ops/pic.ops Thu Apr 28 04:46:37 2005
@@ -0,0 +1,141 @@
+/*
+** pic.ops
+*/
+
+#define DEPRECATED internal_exception(UNIMPLEMENTED, "you shouldn't see this")
+
+#include "parrot/oplib/ops.h"
+
+VERSION = PARROT_VERSION;
+
+=head1 NAME
+
+pic.ops - PIC (Polymorphic Inline Cache) opcode variants
+
+=cut
+
+=head1 DESCRIPTION
+
+During predereferencing opcodes that allow caching are rewritten so that
+equivalent opcodes in this file are used. User code MUST never emit these
+opcodes directly.
+
+###############################################################################
+
+=head2 General infix operations
+
+These operations take an infix operation number and PMC arguments.
+
+=over 4
+
+########################################
+
+=cut
+
+=item C<pic_infix__(inconst INT, in PMC, in PMC)>
+
+One for fun and MOPS.
+
+=cut
+
+inline op pic_infix__(inconst INT, in PMC, in PMC) :pic {
+ Parrot_MIC *mic;
+ Parrot_PIC_lru *lru;
+ PMC *left, *right;
+ INTVAL lr_types;
+
+ mic = (Parrot_MIC *) cur_opcode[1];
+ left = $2;
+ right = $3;
+ lru = &mic->lru;
+ lr_types = (left->vtable->base_type << 16) | right->vtable->base_type;
+ if (lru->lr_type == lr_types) {
+runit_v_pp:
+ ((mmd_f_v_pp)lru->f.real_function)(interpreter, left, right);
+ goto NEXT();
+ }
+ if (mic->pic) {
+ lru = mic->pic->lru;
+ if (lru->lr_type == lr_types)
+ goto runit_v_pp;
+ if (++lru->lr_type == lr_types)
+ goto runit_v_pp;
+ if (++lru->lr_type == lr_types)
+ goto runit_v_pp;
+ mic->pic->miss_count++;
+ /*
+ * TODO if we got too often here just do a dynamic lookup
+ */
+ }
+ parrot_pic_find_infix_v_pp(interpreter, left, right, mic, cur_opcode);
+ /* rerun this opcode */
+ goto OFFSET(0);
+}
+
+=item C<pic_inline_sub__(inconst INT, in PMC, in PMC)>
+
+And for more fun an inlined variant too.
+
+=cut
+
+inline op pic_inline_sub__(inconst INT, in PMC, in PMC) :pic {
+ Parrot_MIC *mic;
+ Parrot_PIC_lru *lru;
+ PMC *left, *right;
+ INTVAL lr_types, lt, rt;
+
+ left = $2;
+ mic = (Parrot_MIC *) cur_opcode[1];
+ lt = left->vtable->base_type;
+ right = $3;
+ lru = &mic->lru;
+ rt = right->vtable->base_type;
+ lr_types = (lt << 16) | rt;
+ if (lru->lr_type == lr_types) {
+ INTVAL a = lt == enum_class_Integer ? PMC_int_val(left) :
+ VTABLE_get_integer(interpreter, left);
+ INTVAL b = rt == enum_class_Integer ? PMC_int_val(right) :
+ VTABLE_get_integer(interpreter, right);
+ INTVAL c = a - b;
+ if ((c^a) >= 0 || (c^~b) >= 0) {
+ if (lt == enum_class_Integer)
+ PMC_int_val(left) = c;
+ else
+ VTABLE_set_integer_native(interpreter, left, c);
+ }
+ else {
+ if (PARROT_ERRORS_test(interpreter,PARROT_ERRORS_OVERFLOW_FLAG)) {
+ real_exception(interpreter, NULL, ERR_OVERFLOW,
+ "Integer overflow");
+ }
+ /* TODO preserve type system */
+ VTABLE_morph(interpreter, left, enum_class_BigInt);
+ VTABLE_set_integer_native(interpreter, left, a);
+ mmd_dispatch_p_pip(interpreter, left, b, left, MMD_SUBTRACT);
+ }
+ }
+ else {
+ ((void**)cur_opcode)[0] =
+ parrot_pic_opcode(interpreter, PARROT_OP_pic_infix___ic_p_p);
+ goto OFFSET(0);
+ }
+ goto NEXT();
+}
+
+
+=back
+
+=cut
+
+###############################################################################
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005 The Perl Foundation. All rights reserved.
+
+=head1 LICENSE
+
+This program is free software. It is subject to the same license
+as the Parrot interpreter itself.
+
+=cut
Modified: trunk/src/interpreter.c
==============================================================================
--- trunk/src/interpreter.c (original)
+++ trunk/src/interpreter.c Thu Apr 28 04:46:37 2005
@@ -337,8 +337,10 @@
load_prederef(interpreter, which);
if (!interpreter->code->prederef.code) {
size_t N = interpreter->code->base.size;
- size_t i;
+ opcode_t *pc = interpreter->code->base.data;
+ size_t i, n, n_pics;
void *pred_func;
+ op_info_t *opinfo;
/* Parrot_memalign_if_possible in OpenBSD allocates 256 if you ask for 312
-- Need to verify this, it may have been a bug elsewhere. If it works now,
we can remove the mem_sys_allocate_zeroed line below. */
@@ -355,11 +357,23 @@
else
pred_func = ((void **)
interpreter->op_lib->op_func_table)[CORE_OPS_prederef__];
- for (i = 0; i < N; i++) {
+ for (i = n_pics = 0; i < N; ) {
+ opinfo = &interpreter->op_info_table[*pc];
temp[i] = pred_func;
+ n = opinfo->arg_count;
+ pc += n;
+ i += n;
+ /* count ops that need a PIC */
+ if (parrot_PIC_op_is_cached(interpreter, *pc))
+ n_pics++;
}
interpreter->code->prederef.code = temp;
+ /* allocate pic store */
+ if (n_pics) {
+ /* pic_index is starting from 1 */
+ parrot_PIC_alloc_store(interpreter, interpreter->code, n_pics + 1);
+ }
}
}
Modified: trunk/src/mmd.c
==============================================================================
--- trunk/src/mmd.c (original)
+++ trunk/src/mmd.c Thu Apr 28 04:46:37 2005
@@ -47,18 +47,6 @@
static void mmd_create_builtin_multi_meth_2(Interp *,
INTVAL func_nr, INTVAL type, INTVAL right, funcptr_t func_ptr);
-typedef PMC* (*mmd_f_p_ppp)(Interp *, PMC *, PMC *, PMC *);
-typedef PMC* (*mmd_f_p_pip)(Interp *, PMC *, INTVAL, PMC *);
-typedef PMC* (*mmd_f_p_pnp)(Interp *, PMC *, FLOATVAL, PMC *);
-typedef PMC* (*mmd_f_p_psp)(Interp *, PMC *, STRING *, PMC *);
-
-typedef void (*mmd_f_v_pp)(Interp *, PMC *, PMC *);
-typedef void (*mmd_f_v_pi)(Interp *, PMC *, INTVAL);
-typedef void (*mmd_f_v_pn)(Interp *, PMC *, FLOATVAL);
-typedef void (*mmd_f_v_ps)(Interp *, PMC *, STRING *);
-
-typedef INTVAL (*mmd_f_i_pp) (Interp *, PMC *, PMC *);
-
#ifndef NDEBUG
static void
dump_mmd(Interp *interpreter, INTVAL function)
Modified: trunk/src/packdump.c
==============================================================================
--- trunk/src/packdump.c (original)
+++ trunk/src/packdump.c Thu Apr 28 04:46:37 2005
@@ -109,7 +109,7 @@
STRING *a_key = const_string(interpreter, "(keyed)");
STRING *null = const_string(interpreter, "(null)");
opcode_t *code_start =
- interpreter->code->cur_cs->base.data;
+ interpreter->code->base.data;
switch (pmc->vtable->base_type) {
case enum_class_Sub:
case enum_class_Coroutine:
Modified: trunk/src/packfile.c
==============================================================================
--- trunk/src/packfile.c (original)
+++ trunk/src/packfile.c Thu Apr 28 04:46:37 2005
@@ -1217,6 +1217,10 @@
cur_cs->const_table = (struct PackFile_ConstTable*) seg;
cur_cs->const_table->code = cur_cs;
+ seg = create_seg(interpreter, &pf->directory,
+ PF_UNKNOWN_SEG, "PIC_idx", file_name, add);
+ cur_cs->pic_index = seg;
+
return cur_cs;
}
/*
Modified: trunk/src/pdump.c
==============================================================================
--- trunk/src/pdump.c (original)
+++ trunk/src/pdump.c Thu Apr 28 04:46:37 2005
@@ -263,13 +263,13 @@
FILE *fp;
size = PackFile_pack_size(interpreter,
- interpreter->code) * sizeof(opcode_t);
+ interpreter->code->base.pf) * sizeof(opcode_t);
pack = (opcode_t*) mem_sys_allocate(size);
if (!pack) {
printf("out of mem\n");
exit(1);
}
- PackFile_pack(interpreter, interpreter->code, pack);
+ PackFile_pack(interpreter, interpreter->code->base.pf, pack);
if (strcmp (file, "-") == 0)
fp = stdout;
else if ((fp = fopen(file, "wb")) == 0) {
Modified: trunk/src/pic.c
==============================================================================
--- trunk/src/pic.c (original)
+++ trunk/src/pic.c Thu Apr 28 04:46:37 2005
@@ -12,11 +12,60 @@
prederefed run cores. Additionally opcodes that do some kind of lookup
like C<new_p_sc> are changed to faster variants.
-TODO For non-prederefed run-cores there's a less efficient variant which
+For non-prederefed run-cores there's a less efficient variant which
is basically:
- * the bytecode segment has an index per cached opcode
+ * the bytecode segment has an index per cached opcode (code->pic_index)
* this index points into pic_store
+ * TODO use the cache in opcodes
+
+=head1 OPERATION SCHEME
+
+Given this bytecode:
+
+ 0 1 2 3 4 5
+ +--------------+---------------+----+----+-----------------+----------+
+ | infix_ic_p_p | .MMD_SUBTRACT | P5 | P6 | callmethodcc_sc | "method" |
+ +--------------+---------------+----+----+-----------------+----------+
+
+In init_prederef the opcodes are replaced with prederef__, operands are
+replaced with their addresses:
+
+ 0 1 2 3 4 5
+ +--------------+---------------+----+----+-----------------+----------+
+ | prederef__ |&.MMD_SUBTRACT | &P5| &P6| prederef__ |&"method" |
+ +--------------+---------------+----+----+-----------------+----------+
+
+we have code->pic_index with an index into pic_store - the pic_index is
+half the size of the bytecode and addressed with pc_offset/2:
+
+ 0 1 2
+ +---+---+---+
+ | 1 | | 2 |
+ +---+---+---+
+
+During predereferencing the opcode gets rewritten to the PIC variant,
+the constant infix operation number is replaced with a pointer to the MIC
+in the pic_store at the index pic_index:
+
+ 0 1 2 3
+ +--------------------+-----+----+----+-----------------------+-----+
+ | pic_infix___ic_p_p | MIC1|&P5 |&P6 | pic_callmethodcc___sc | MIC2|
+ +--------------------+-----+----+----+-----------------------+-----+
+
+This can be further optimized due to static inlining:
+
+ 0 1 2 3
+ +--------------------+-----+----+----+-----------------------+-----+
+ | pic_inline_sub_p_p | MIC1|&P5 |&P6 | pic_callmethodcc___sc | MIC2|
+ +--------------------+-----+----+----+-----------------------+-----+
+
+The opcode is an opcode number for the switched core or the actual code address
+for the direct-threaded CGP core. With a little help of the JIT system we could
+also dynamicall create inlined code.
+
+Runcores with r/o (mmaped) bytecode can't be rewritten in this way, the
+lookup of the cache has to be done in the opcode itself.
=head2 Functions
@@ -29,10 +78,24 @@
#include "parrot/parrot.h"
#include "parrot/oplib/ops.h"
#include <assert.h>
+#ifdef HAVE_COMPUTED_GOTO
+# include "parrot/oplib/core_ops_cgp.h"
+#endif
+
+/* needs a Makefile dependency */
+/* #include "../classes/pmc_integer.h" */
+
+extern void Parrot_Integer_i_subtract_Integer(Interp* , PMC* pmc, PMC* value);
#define OP_AS_OFFS(o) (_reg_base + ((opcode_t*)cur_opcode)[o])
/*
+ * hack to turn on inlining - just sub_p_p for mops done
+ */
+
+#define ENABLE_INLINING 1
+
+/*
=item C<void parrot_PIC_alloc_store(Interp *, struct PackFile_ByteCode *,
size_t n);>
@@ -73,7 +136,7 @@
store->pic = (Parrot_PIC*)((char *)store + size);
store->usable = poly;
- store->mic = (Parrot_MIC**)((char*)store + sizeof(Parrot_PIC_store));
+ store->mic = (Parrot_MIC*)((char*)store + sizeof(Parrot_PIC_store));
store->n_mics = n;
}
@@ -101,6 +164,9 @@
int
parrot_PIC_op_is_cached(Interp *interpreter, int op_code)
{
+ switch (op_code) {
+ case PARROT_OP_infix_ic_p_p: return 1;
+ }
return 0;
}
/*
@@ -123,7 +189,7 @@
store = interpreter->code->pic_store;
assert(n < store->n_mics);
- return store->mic[n];
+ return store->mic + n;
}
Parrot_PIC*
@@ -168,6 +234,22 @@
*/
+void *
+parrot_pic_opcode(Interp *interpreter, int op)
+{
+ int core = interpreter->run_core;
+ op_lib_t *cg_lib;
+
+ if (core == PARROT_SWITCH_CORE)
+ return (void*) op;
+#ifdef HAVE_COMPUTED_GOTO
+ cg_lib = PARROT_CORE_CGP_OPLIB_INIT(1);
+ return ((void**)cg_lib->op_func_table)[op];
+#else
+ return NULL;
+#endif
+}
+
#define N_STATIC_TYPES 500
static INTVAL pmc_type_numbers[N_STATIC_TYPES];
@@ -206,9 +288,26 @@
op = PARROT_OP_new_p_ic;
}
break;
+ case PARROT_OP_infix_ic_p_p:
+ {
+ Parrot_MIC *mic;
+ size_t n;
+ struct PackFile_ByteCode *cs = interpreter->code;
+
+ n = cur_opcode - (opcode_t*)cs->prederef.code;
+ /*
+ * pic_index is half the size of the code
+ */
+ n = cs->pic_index->data[n / 2];
+ mic = parrot_PIC_alloc_mic(interpreter, n);
+ mic->m.func_nr = *(INTVAL*) cur_opcode[1];
+ pc_pred[1] = (void*) mic;
+ op = PARROT_OP_pic_infix___ic_p_p;
+ }
+ break;
}
/*
- * else set default prederef code address
+ * rewrite opcode
*/
if (core == PARROT_SWITCH_CORE)
*pc_pred = (void**) op;
@@ -216,6 +315,88 @@
*pc_pred = ((void **)prederef_op_func)[op];
}
+static void
+parrot_pic_move(Interp* interpreter, Parrot_MIC *mic)
+{
+ Parrot_PIC* pic;
+
+ /*
+ * MIC slot is empty - use it
+ */
+ if (!mic->lru.lr_type)
+ return;
+ /*
+ * need more cache slots - allocate one PIC
+ */
+ if (!mic->pic) {
+ mic->pic = parrot_PIC_alloc_pic(interpreter);
+ }
+ else {
+ /*
+ * PIC was already used - shift slots up
+ */
+ pic = mic->pic;
+ pic->lru[2].lr_type = pic->lru[1].lr_type;
+ pic->lru[2].f.sub = pic->lru[1].f.sub;
+ pic->lru[1].lr_type = pic->lru[0].lr_type;
+ pic->lru[1].f.sub = pic->lru[0].f.sub;
+ pic->lru[0].lr_type = mic->lru.lr_type;
+ pic->lru[0].f.sub = mic->lru.f.sub;
+ mic->lru.lr_type = 0;
+ }
+}
+
+void
+parrot_pic_find_infix_v_pp(Interp *interpreter, PMC *left, PMC *right,
+ Parrot_MIC *mic, opcode_t *cur_opcode)
+{
+ funcptr_t func;
+ int is_pmc;
+ INTVAL left_type, right_type;
+ /*
+ * if 2 threads are entering here, there is a chance
+ * that one moves the lru structure under the other thread
+ * and vv - just lock in case
+ *
+ * TODO
+ *
+ * if (TRY_LOCK_INTERPRETER(i) == EBUSY)
+ * return; - reexec
+ */
+ LOCK_INTERPRETER(interpreter);
+ /*
+ * move entries back and set topmost entry
+ */
+ parrot_pic_move(interpreter, mic);
+ /*
+ * get real dispatch function
+ */
+ left_type = left->vtable->base_type;
+ right_type = right->vtable->base_type;
+ func = get_mmd_dispatch_type(interpreter,
+ mic->m.func_nr, left_type, right_type, &is_pmc);
+ if (is_pmc) {
+ /* set prederef code address to orig slot for now
+ */
+ ((void**)cur_opcode)[0] =
+ parrot_pic_opcode(interpreter, PARROT_OP_infix_ic_p_p);
+ mic->lru.f.sub = (PMC*)F2DPTR(func);
+ }
+ else {
+ int op = PARROT_OP_pic_infix___ic_p_p;
+
+#if ENABLE_INLINING
+ if (func == (funcptr_t)Parrot_Integer_i_subtract_Integer && !mic->pic)
+ op = PARROT_OP_pic_inline_sub___ic_p_p;
+#endif
+ ((void**)cur_opcode)[0] =
+ parrot_pic_opcode(interpreter, op);
+ mic->lru.f.real_function = func;
+ }
+ mic->lru.lr_type = (left_type << 16) | right_type;
+ UNLOCK_INTERPRETER(interpreter);
+}
+
/*
=back
@@ -227,7 +408,7 @@
=head1 SEE ALSO
F<src/mmd.c>, F<src/object.c>, F<src/interpreter.c>, F<ops/core_ops_cgp.c>,
-F<include/parrot/pic.h>
+F<include/parrot/pic.h>, F<ops/pic.ops>
=cut