Skip to content

Commit

Permalink
integrate with GCC's garbage collector (ticket #1)
Browse files Browse the repository at this point in the history
Support creating instances of PyTypeObject subclasses in cpybuilder.py,
and convert all of the various wrapper object classes from being PyObject
subclasses to being subclassed from PyGccWrapper.  All instances of
PyGccWrapper are now tracked in a doubly-linked list (and thus the types
require a tp_dealloc of gcc_python_wrapper_dealloc in order to remove them
from this list.

Introduce wrapperbuilder.py and use it to convert all of the type objects
for the wrapper classes to being instances of a new PyGccWrapperTypeObject.

The above class changes allow us to add a new per-wrapper hook ("wrtp_mark")
for working with GCC's garbage collector.

Change "struct_name" from "struct foo" to "foo" when creating PyTypeObject
instances in the various generate-*-c.py files, so that we can easily match
up the autogenerated "wrtp_mark_for" callback from that file with the
declaration of said callback from the DECLARE_SIMPLE_WRAPPER() macro in
gcc-python.h

Wire up to GCC's garbage collector (in the new gcc-python-wrapper.c), so that
when it runs, we can iterate through all of our wrapper objects, and mark the
wrapped objects, so they don't get swept (and deleted) from under us.

Add gcc._gc_selftest() to exercise this machinery (for trees only, so far, and
without exercising recursive marking)

Add gc._force_garbage_collection() and a selftest for this.

Fixes https://fedorahosted.org/gcc-python-plugin/ticket/1
  • Loading branch information
davidmalcolm committed Jan 14, 2012
1 parent e75f80d commit 58d65a1
Show file tree
Hide file tree
Showing 37 changed files with 887 additions and 163 deletions.
27 changes: 15 additions & 12 deletions Makefile
Expand Up @@ -36,6 +36,7 @@ PLUGIN_SOURCE_FILES= \
gcc-python-tree.c \
gcc-python-variable.c \
gcc-python-version.c \
gcc-python-wrapper.c \
autogenerated-callgraph.c \
autogenerated-cfg.c \
autogenerated-option.c \
Expand All @@ -52,6 +53,8 @@ PLUGIN_SOURCE_FILES= \
PLUGIN_OBJECT_FILES= $(patsubst %.c,%.o,$(PLUGIN_SOURCE_FILES))
GCCPLUGINS_DIR:= $(shell $(GCC) --print-file-name=plugin)

GENERATOR_DEPS=cpybuilder.py wrapperbuilder.py

# The plugin supports both Python 2 and Python 3
#
# In theory we could have arbitrary combinations of python versions for each
Expand Down Expand Up @@ -116,40 +119,40 @@ autogenerated-rtl-types.txt: rtl-types.txt.in
autogenerated-tree-types.txt: tree-types.txt.in
cpp $(CFLAGS) $^ -o $@

autogenerated-callgraph.c: cpybuilder.py generate-callgraph-c.py
autogenerated-callgraph.c: $(GENERATOR_DEPS) generate-callgraph-c.py
python generate-callgraph-c.py > $@

autogenerated-cfg.c: cpybuilder.py generate-cfg-c.py
autogenerated-cfg.c: $(GENERATOR_DEPS) generate-cfg-c.py
$(PYTHON) generate-cfg-c.py > $@

autogenerated-function.c: cpybuilder.py generate-function-c.py
autogenerated-function.c: $(GENERATOR_DEPS) generate-function-c.py
$(PYTHON) generate-function-c.py > $@

autogenerated-gimple.c: cpybuilder.py generate-gimple-c.py autogenerated-gimple-types.txt maketreetypes.py
autogenerated-gimple.c: $(GENERATOR_DEPS) generate-gimple-c.py autogenerated-gimple-types.txt maketreetypes.py
$(PYTHON) generate-gimple-c.py > $@

autogenerated-location.c: cpybuilder.py generate-location-c.py
autogenerated-location.c: $(GENERATOR_DEPS) generate-location-c.py
$(PYTHON) generate-location-c.py > $@

autogenerated-option.c: cpybuilder.py generate-option-c.py
autogenerated-option.c: $(GENERATOR_DEPS) generate-option-c.py
$(PYTHON) generate-option-c.py > $@

autogenerated-parameter.c: cpybuilder.py generate-parameter-c.py
autogenerated-parameter.c: $(GENERATOR_DEPS) generate-parameter-c.py
$(PYTHON) generate-parameter-c.py > $@

autogenerated-pass.c: cpybuilder.py generate-pass-c.py
autogenerated-pass.c: $(GENERATOR_DEPS) generate-pass-c.py
$(PYTHON) generate-pass-c.py > $@

autogenerated-pretty-printer.c: cpybuilder.py generate-pretty-printer-c.py
autogenerated-pretty-printer.c: $(GENERATOR_DEPS) generate-pretty-printer-c.py
$(PYTHON) generate-pretty-printer-c.py > $@

autogenerated-tree.c: cpybuilder.py generate-tree-c.py autogenerated-tree-types.txt maketreetypes.py
autogenerated-tree.c: $(GENERATOR_DEPS) generate-tree-c.py autogenerated-tree-types.txt maketreetypes.py
$(PYTHON) generate-tree-c.py > $@

autogenerated-rtl.c: cpybuilder.py generate-rtl-c.py autogenerated-rtl-types.txt maketreetypes.py
autogenerated-rtl.c: $(GENERATOR_DEPS) generate-rtl-c.py autogenerated-rtl-types.txt maketreetypes.py
python generate-rtl-c.py > $@

autogenerated-variable.c: cpybuilder.py generate-variable-c.py autogenerated-gimple-types.txt maketreetypes.py
autogenerated-variable.c: $(GENERATOR_DEPS) generate-variable-c.py autogenerated-gimple-types.txt maketreetypes.py
$(PYTHON) generate-variable-c.py > $@

# Hint for debugging: add -v to the gcc options
Expand Down
30 changes: 23 additions & 7 deletions cpybuilder.py
@@ -1,5 +1,5 @@
# Copyright 2011 David Malcolm <dmalcolm@redhat.com>
# Copyright 2011 Red Hat, Inc.
# Copyright 2011, 2012 David Malcolm <dmalcolm@redhat.com>
# Copyright 2011, 2012 Red Hat, Inc.
#
# This is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -75,6 +75,17 @@ def c_src_field(self, name):
else:
return ' %s, /* %s */\n' % (val, name)

def c_src_field_value(self, name, val, cast=None):
if cast:
caststr = '(%s)' % cast
else:
caststr = ''
if with_gcc_extensions:
# Designate the initializer fields:
return ' .%s = %s%s,\n' % (name, caststr, val)
else:
return ' %s%s, /* %s */\n' % (caststr, val, name)

class PyGetSetDef:
def __init__(self, name, get, set, doc, closure=None):
self.name = name
Expand Down Expand Up @@ -229,7 +240,13 @@ def __init__(self, identifier, localname, tp_name, struct_name, **kwargs):
def c_defn(self):
result = '\n'
result += 'PyTypeObject %(identifier)s = {\n' % self.__dict__
result += ' PyVarObject_HEAD_INIT(0, 0)\n'
result += self.c_initializer()
result += '};\n' % self.__dict__
result +='\n'
return result

def c_initializer(self):
result = ' PyVarObject_HEAD_INIT(0, 0)\n'
result += ' "%(tp_name)s", /*tp_name*/\n' % self.__dict__
result += ' sizeof(%(struct_name)s), /*tp_basicsize*/\n' % self.__dict__
result += ' 0, /*tp_itemsize*/\n'
Expand Down Expand Up @@ -263,7 +280,7 @@ def c_defn(self):
result += self.c_ptr_field('tp_methods')
result += self.c_ptr_field('tp_members')
result += self.c_ptr_field('tp_getset')
result += self.c_ptr_field('tp_base')
result += self.c_ptr_field('tp_base', 'PyTypeObject*')
result += self.c_ptr_field('tp_dict')
result += self.c_ptr_field('tp_descr_get')
result += self.c_ptr_field('tp_descr_set')
Expand All @@ -282,12 +299,11 @@ def c_defn(self):
result += '#if PY_VERSION_HEX >= 0x02060000\n' % self.__dict__
result += ' 0, /*tp_version_tag*/\n' % self.__dict__
result += '#endif\n' % self.__dict__
result += '};\n' % self.__dict__
result +='\n'
result += '\n'
return result

def c_invoke_type_ready(self):
return (' if (PyType_Ready(&%(identifier)s) < 0)\n'
return (' if (PyType_Ready((PyTypeObject*)&%(identifier)s) < 0)\n'
' goto error;\n'
'\n') % self.__dict__

Expand Down
27 changes: 21 additions & 6 deletions gcc-python-callgraph.c
@@ -1,6 +1,6 @@
/*
Copyright 2011 David Malcolm <dmalcolm@redhat.com>
Copyright 2011 Red Hat, Inc.
Copyright 2011, 2012 David Malcolm <dmalcolm@redhat.com>
Copyright 2011, 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -120,40 +120,55 @@ gcc_python_make_wrapper_cgraph_edge(struct cgraph_edge * edge)
{
struct PyGccCallgraphEdge *obj = NULL;

obj = PyObject_New(struct PyGccCallgraphEdge, &gcc_CallgraphEdgeType);
obj = PyGccWrapper_New(struct PyGccCallgraphEdge, &gcc_CallgraphEdgeType);
if (!obj) {
goto error;
}

obj->edge = edge;
/* FIXME: do we need to do something for the GCC GC? */

return (PyObject*)obj;

error:
return NULL;
}

void
wrtp_mark_for_PyGccCallgraphEdge(PyGccCallgraphEdge *wrapper)
{
/* Mark the underlying object (recursing into its fields): */
gt_ggc_mx_cgraph_edge(wrapper->edge);
}


PyObject *
real_make_cgraph_node_wrapper(void *ptr)
{
struct cgraph_node * node = (struct cgraph_node *)ptr;
struct PyGccCallgraphNode *obj = NULL;

obj = PyObject_New(struct PyGccCallgraphNode, &gcc_CallgraphNodeType);
obj = PyGccWrapper_New(struct PyGccCallgraphNode,
&gcc_CallgraphNodeType);
if (!obj) {
goto error;
}

obj->node = node;
/* FIXME: do we need to do something for the GCC GC? */

return (PyObject*)obj;

error:
return NULL;
}

void
wrtp_mark_for_PyGccCallgraphNode(PyGccCallgraphNode *wrapper)
{
/* Mark the underlying object (recursing into its fields): */
gt_ggc_mx_cgraph_node(wrapper->node);
}


static PyObject *cgraph_node_wrapper_cache = NULL;
PyObject *
gcc_python_make_wrapper_cgraph_node(struct cgraph_node * node)
Expand Down
31 changes: 25 additions & 6 deletions gcc-python-cfg.c
Expand Up @@ -42,20 +42,27 @@ gcc_python_make_wrapper_edge(edge e)
Py_RETURN_NONE;
}

obj = PyObject_New(struct PyGccEdge, &gcc_EdgeType);
obj = PyGccWrapper_New(struct PyGccEdge, &gcc_EdgeType);
if (!obj) {
goto error;
}

obj->e = e;
/* FIXME: do we need to do something for the GCC GC? */

return (PyObject*)obj;

error:
return NULL;
}

void
wrtp_mark_for_PyGccEdge(PyGccEdge *wrapper)
{
/* Mark the underlying object (recursing into its fields): */
gt_ggc_mx_edge_def(wrapper->e);
}


/*
"struct basic_block_def" is declared in basic-block.h, c.f:
struct GTY((chain_next ("%h.next_bb"), chain_prev ("%h.prev_bb"))) basic_block_def {
Expand Down Expand Up @@ -348,7 +355,7 @@ real_make_basic_block_wrapper(void *ptr)
Py_RETURN_NONE;
}

obj = PyObject_New(struct PyGccBasicBlock, &gcc_BasicBlockType);
obj = PyGccWrapper_New(struct PyGccBasicBlock, &gcc_BasicBlockType);
if (!obj) {
goto error;
}
Expand Down Expand Up @@ -415,14 +422,21 @@ real_make_basic_block_wrapper(void *ptr)
#endif

obj->bb = bb;
/* FIXME: do we need to do something for the GCC GC? */

return (PyObject*)obj;

error:
return NULL;
}

void
wrtp_mark_for_PyGccBasicBlock(PyGccBasicBlock *wrapper)
{
/* Mark the underlying object (recursing into its fields): */
gt_ggc_mx_basic_block_def(wrapper->bb);
}


static PyObject *basic_block_wrapper_cache = NULL;
PyObject *
gcc_python_make_wrapper_basic_block(basic_block bb)
Expand Down Expand Up @@ -507,20 +521,25 @@ gcc_python_make_wrapper_cfg(struct control_flow_graph *cfg)
Py_RETURN_NONE;
}

obj = PyObject_New(struct PyGccCfg, &gcc_CfgType);
obj = PyGccWrapper_New(struct PyGccCfg, &gcc_CfgType);
if (!obj) {
goto error;
}

obj->cfg = cfg;
/* FIXME: do we need to do something for the GCC GC? */

return (PyObject*)obj;

error:
return NULL;
}

void
wrtp_mark_for_PyGccCfg(PyGccCfg *wrapper)
{
/* Mark the underlying object (recursing into its fields): */
gt_ggc_mx_control_flow_graph(wrapper->cfg);
}

/*
PEP-7
Expand Down
6 changes: 4 additions & 2 deletions gcc-python-compat.h
@@ -1,6 +1,6 @@
/*
Copyright 2011 David Malcolm <dmalcolm@redhat.com>
Copyright 2011 Red Hat, Inc.
Copyright 2011, 2012 David Malcolm <dmalcolm@redhat.com>
Copyright 2011, 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -50,6 +50,8 @@ extern int
dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
bool is_stmt);

/* Within gcc/gcc-internal.h, not exposed by plugin API */
extern bool ggc_force_collect;

/* From c-family/c-common.h */
extern tree c_sizeof_or_alignof_type (location_t, tree, bool, int);
Expand Down
2 changes: 1 addition & 1 deletion gcc-python-diagnostics.c
Expand Up @@ -115,7 +115,7 @@ gcc_python_warning(PyObject *self, PyObject *args, PyObject *kwargs)
assert(opt_obj);

/* If a gcc.Option was given, extract the code: */
if (Py_TYPE(opt_obj) == &gcc_OptionType) {
if (Py_TYPE(opt_obj) == (PyTypeObject*)&gcc_OptionType) {
opt_code = ((PyGccOption*)opt_obj)->opt_code;

/* Ugly workaround; see this function: */
Expand Down
14 changes: 10 additions & 4 deletions gcc-python-function.c
@@ -1,6 +1,6 @@
/*
Copyright 2011 David Malcolm <dmalcolm@redhat.com>
Copyright 2011 Red Hat, Inc.
Copyright 2011, 2012 David Malcolm <dmalcolm@redhat.com>
Copyright 2011, 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -112,20 +112,26 @@ gcc_python_make_wrapper_function(struct function *fun)
#endif
#endif

obj = PyObject_New(struct PyGccFunction, &gcc_FunctionType);
obj = PyGccWrapper_New(struct PyGccFunction, &gcc_FunctionType);
if (!obj) {
goto error;
}

obj->fun = fun;
/* FIXME: do we need to do something for the GCC GC? */

return (PyObject*)obj;

error:
return NULL;
}

void
wrtp_mark_for_PyGccFunction(PyGccFunction *wrapper)
{
/* Mark the underlying object (recursing into its fields): */
gt_ggc_mx_function(wrapper->fun);
}


/*
PEP-7
Expand Down

0 comments on commit 58d65a1

Please sign in to comment.