[gcc-python-plugin] Implement __repr__ for gcc.IntegerCst and gcc.StringCst
by David Hugh Malcolm
commit 2855f528ecc3b21b5adec0eb6fae18222935200b
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Jul 29 15:44:11 2011 -0400
Implement __repr__ for gcc.IntegerCst and gcc.StringCst
gcc-python-tree.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
gcc-python-wrappers.h | 6 ++++++
gcc-python.c | 40 ++++++++++++++++++++++++----------------
gcc-python.h | 4 ++++
generate-tree-c.py | 6 ++++++
5 files changed, 86 insertions(+), 16 deletions(-)
---
diff --git a/gcc-python-tree.c b/gcc-python-tree.c
index d8ce3e0..8c226d8 100644
--- a/gcc-python-tree.c
+++ b/gcc-python-tree.c
@@ -263,6 +263,52 @@ gcc_IntegerConstant_get_constant(struct PyGccTree * self, void *closure)
}
PyObject *
+gcc_IntegerConstant_repr(struct PyGccTree * self)
+{
+ tree type = TREE_TYPE(self->t);
+ char buf[512];
+
+ gcc_python_double_int_as_text(TREE_INT_CST(self->t),
+ TYPE_UNSIGNED(type),
+ buf, sizeof(buf));
+ return gcc_python_string_from_format("%s(%s)",
+ Py_TYPE(self)->tp_name,
+ buf);
+}
+
+PyObject *
+gcc_StringConstant_repr(struct PyGccTree * self)
+{
+ PyObject *str_obj;
+ PyObject *result = NULL;
+
+ str_obj = gcc_python_string_or_none(TREE_STRING_POINTER(self->t));
+ if (!str_obj) {
+ return NULL;
+ }
+#if PY_MAJOR_VERSION >= 3
+ result = gcc_python_string_from_format("%s(%R)",
+ Py_TYPE(self)->tp_name,
+ str_obj);
+#else
+ {
+ PyObject *str_repr;
+ str_repr = PyObject_Repr(str_obj);
+ if (!str_repr) {
+ Py_DECREF(str_obj);
+ return NULL;
+ }
+ result = gcc_python_string_from_format("%s(%s)",
+ Py_TYPE(self)->tp_name,
+ PyString_AsString(str_repr));
+ Py_DECREF(str_repr);
+ }
+#endif
+ Py_DECREF(str_obj);
+ return result;
+}
+
+PyObject *
gcc_TypeDecl_get_pointer(struct PyGccTree *self, void *closure)
{
tree decl_type = TREE_TYPE(self->t);
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index e33936f..05424b4 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -111,6 +111,12 @@ PyObject *
gcc_IntegerConstant_get_constant(struct PyGccTree * self, void *closure);
PyObject *
+gcc_IntegerConstant_repr(struct PyGccTree * self);
+
+PyObject *
+gcc_StringConstant_repr(struct PyGccTree * self);
+
+PyObject *
gcc_TypeDecl_get_pointer(struct PyGccTree *self, void *closure);
/* gcc-python-gimple.c: */
diff --git a/gcc-python.c b/gcc-python.c
index 27aedb6..0eefd53 100644
--- a/gcc-python.c
+++ b/gcc-python.c
@@ -1032,28 +1032,36 @@ gcc_python_string_or_none(const char *str_or_null)
}
}
-PyObject *
-gcc_python_int_from_double_int(double_int di, bool is_unsigned)
-{
- /*
- "double_int" is declared in gcc/double-int.h as a pair of HOST_WIDE_INT.
- These in turn are defined in gcc/hwint.h as a #define to one of "long",
- "long long", or "__int64".
+/*
+ "double_int" is declared in gcc/double-int.h as a pair of HOST_WIDE_INT.
+ These in turn are defined in gcc/hwint.h as a #define to one of "long",
+ "long long", or "__int64".
- It appears that they can be interpreted as either "unsigned" or "signed"
- */
+ It appears that they can be interpreted as either "unsigned" or "signed"
- /* How to convert this to a PyLong object?
- We "cheat", and take it through the decimal representation, then convert
- from decimal. This is probably slow, but is (I hope) at least correct.
- */
- char buf[512]; /* FIXME */
+ How to convert this to other types?
+ We "cheat", and take it through the decimal representation, then convert
+ from decimal. This is probably slow, but is (I hope) at least correct.
+*/
+void
+gcc_python_double_int_as_text(double_int di, bool is_unsigned,
+ char *out, int bufsize)
+{
FILE *f;
+ assert(out);
+ assert(bufsize > 256); /* FIXME */
- buf[0] = '\0';
- f = fmemopen(buf, sizeof(buf), "w");
+ out[0] = '\0';
+ f = fmemopen(out, bufsize, "w");
dump_double_int (f, di, is_unsigned);
fclose(f);
+}
+
+PyObject *
+gcc_python_int_from_double_int(double_int di, bool is_unsigned)
+{
+ char buf[512]; /* FIXME */
+ gcc_python_double_int_as_text(di, is_unsigned, buf, sizeof(buf));
return PyLong_FromString(buf, NULL, 10);
}
diff --git a/gcc-python.h b/gcc-python.h
index 9f15eb7..ce8265f 100644
--- a/gcc-python.h
+++ b/gcc-python.h
@@ -199,6 +199,10 @@ gcc_python_string_or_none(const char *str_or_null);
PyObject *
VEC_tree_as_PyList(VEC(tree,gc) *vec_nodes);
+void
+gcc_python_double_int_as_text(double_int di, bool is_unsigned,
+ char *out, int bufsize);
+
PyObject *
gcc_python_int_from_double_int(double_int di, bool is_unsigned);
diff --git a/generate-tree-c.py b/generate-tree-c.py
index dbc6d73..ebb35ac 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -293,6 +293,8 @@ def generate_tree_code_classes():
getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [])
tp_as_number = None
+ tp_repr = None
+ tp_str = None
def get_getter_identifier(name):
return 'gcc_%s_get_%s' % (cc, name)
@@ -320,6 +322,7 @@ def generate_tree_code_classes():
add_simple_getter('constant',
'gcc_python_string_from_string(TREE_STRING_POINTER(self->t))',
'The actual value of this constant, as a str')
+ tp_repr = '(reprfunc)gcc_StringConstant_repr'
if cc == 'IntegerCst':
getsettable.add_gsdef('constant',
@@ -330,6 +333,7 @@ def generate_tree_code_classes():
tp_as_number = number_methods.identifier
number_methods.nb_int = 'gcc_IntegerConstant_get_constant'
cu.add_defn(number_methods.c_defn())
+ tp_repr = '(reprfunc)gcc_IntegerConstant_repr'
# TYPE_QUALS for various foo_TYPE classes:
if tree_type.SYM in ('VOID_TYPE', 'INTEGER_TYPE', 'REAL_TYPE',
@@ -470,6 +474,8 @@ def generate_tree_code_classes():
tp_new = 'PyType_GenericNew',
tp_base = '&%s' % base_type,
tp_getset = getsettable.identifier,
+ tp_str = tp_str,
+ tp_repr = tp_repr,
)
if tp_as_number:
pytype.tp_as_number = '&%s' % tp_as_number
12 years, 9 months
[gcc-python-plugin] Initial wrapping of the Register Transfer Language
by David Hugh Malcolm
commit fc4d7fa39cce0a8e78d5352a96a22f063633119d
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Jul 28 19:31:46 2011 -0400
Initial wrapping of the Register Transfer Language
Makefile | 8 ++
docs/cfg.rst | 5 +
docs/misc.rst | 1 +
docs/rtl.rst | 193 ++++++++++++++++++++++++++++++++++++++++
gcc-python-cfg.c | 49 ++++++++++
gcc-python-rtl.c | 204 +++++++++++++++++++++++++++++++++++++++++++
gcc-python-wrappers.h | 16 ++++
gcc-python.c | 2 +
gcc-python.h | 11 +++
generate-cfg-c.py | 4 +
generate-rtl-c.py | 193 ++++++++++++++++++++++++++++++++++++++++
maketreetypes.py | 23 +++++
rtl-types.txt.in | 4 +
tests/plugin/rtl/input.c | 33 +++++++
tests/plugin/rtl/script.py | 41 +++++++++
tests/plugin/rtl/stdout.txt | 26 ++++++
16 files changed, 813 insertions(+), 0 deletions(-)
---
diff --git a/Makefile b/Makefile
index 105c64e..8cc2f30 100644
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,7 @@ PLUGIN_SOURCE_FILES= \
gcc-python-parameter.c \
gcc-python-pass.c \
gcc-python-pretty-printer.c \
+ gcc-python-rtl.c \
gcc-python-tree.c \
gcc-python-variable.c \
autogenerated-callgraph.c \
@@ -40,6 +41,7 @@ PLUGIN_SOURCE_FILES= \
autogenerated-parameter.c \
autogenerated-pass.c \
autogenerated-pretty-printer.c \
+ autogenerated-rtl.c \
autogenerated-tree.c \
autogenerated-variable.c
@@ -104,6 +106,9 @@ clean:
autogenerated-gimple-types.txt: gimple-types.txt.in
cpp $(CFLAGS) $^ -o $@
+autogenerated-rtl-types.txt: rtl-types.txt.in
+ cpp $(CFLAGS) $^ -o $@
+
autogenerated-tree-types.txt: tree-types.txt.in
cpp $(CFLAGS) $^ -o $@
@@ -137,6 +142,9 @@ autogenerated-pretty-printer.c: cpybuilder.py generate-pretty-printer-c.py
autogenerated-tree.c: cpybuilder.py 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
+ python generate-rtl-c.py > $@
+
autogenerated-variable.c: cpybuilder.py generate-variable-c.py autogenerated-gimple-types.txt maketreetypes.py
$(PYTHON) generate-variable-c.py > $@
diff --git a/docs/cfg.rst b/docs/cfg.rst
index bb61b1f..ed6b7fa 100644
--- a/docs/cfg.rst
+++ b/docs/cfg.rst
@@ -131,6 +131,11 @@ You can get at the control flow graph of a :py:class:`gcc.Function` via its
The list of :py:class:`gcc.Gimple` instructions, if appropriate for this
pass, or None
+ .. py:attribute:: rtl
+
+ The list of :py:class:`gcc.Rtl` expressions, if appropriate for this
+ pass, or None
+
.. py:class:: gcc.Edge
diff --git a/docs/misc.rst b/docs/misc.rst
index 0c707e1..bd6dcc0 100644
--- a/docs/misc.rst
+++ b/docs/misc.rst
@@ -29,3 +29,4 @@ scripts.
parameters.rst
versions.rst
tables-of-passes.rst
+ rtl.rst
diff --git a/docs/rtl.rst b/docs/rtl.rst
new file mode 100644
index 0000000..7a8d434
--- /dev/null
+++ b/docs/rtl.rst
@@ -0,0 +1,193 @@
+.. Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+
+Register Transfer Language (RTL)
+================================
+
+.. py:class:: gcc.Rtl
+
+ A wrapper around GCC's `struct rtx_def` type: an expression within GCC's
+ Register Transfer Language
+
+ .. py:attribute:: loc
+
+ The :py:class:`gcc.Location` of this expression, or None
+
+ .. py:attribute:: operands
+
+ The operands of this expression, as a tuple. The precise type of the
+ operands will vary by subclass.
+
+There are numerous subclasses. However, this part of the API is much less
+polished than the rest of the plugin.
+
+ .. Here's a dump of the class hierarchy, from help(gcc):
+ .. Rtl
+ .. RtxAutoinc
+ .. RtlPostDec
+ .. RtlPostInc
+ .. RtlPostModify
+ .. RtlPreDec
+ .. RtlPreInc
+ .. RtlPreModify
+ .. RtxBinArith
+ .. RtlAshift
+ .. RtlAshiftrt
+ .. RtlCompare
+ .. RtlDiv
+ .. RtlLshiftrt
+ .. RtlMinus
+ .. RtlMod
+ .. RtlRotate
+ .. RtlRotatert
+ .. RtlSsAshift
+ .. RtlSsDiv
+ .. RtlSsMinus
+ .. RtlUdiv
+ .. RtlUmod
+ .. RtlUsAshift
+ .. RtlUsDiv
+ .. RtlUsMinus
+ .. RtlVecConcat
+ .. RtlVecSelect
+ .. RtxBitfieldOps
+ .. RtlSignExtract
+ .. RtlZeroExtract
+ .. RtxCommArith
+ .. RtlAnd
+ .. RtlIor
+ .. RtlMult
+ .. RtlPlus
+ .. RtlSmax
+ .. RtlSmin
+ .. RtlSsMult
+ .. RtlSsPlus
+ .. RtlUmax
+ .. RtlUmin
+ .. RtlUsMult
+ .. RtlUsPlus
+ .. RtlXor
+ .. RtxCommCompare
+ .. RtlEq
+ .. RtlLtgt
+ .. RtlNe
+ .. RtlOrdered
+ .. RtlUneq
+ .. RtlUnordered
+ .. RtxCompare
+ .. RtlGe
+ .. RtlGeu
+ .. RtlGt
+ .. RtlGtu
+ .. RtlLe
+ .. RtlLeu
+ .. RtlLt
+ .. RtlLtu
+ .. RtlUnge
+ .. RtlUngt
+ .. RtlUnle
+ .. RtlUnlt
+ .. RtxConstObj
+ .. RtlConst
+ .. RtlConstDouble
+ .. RtlConstFixed
+ .. RtlConstInt
+ .. RtlConstVector
+ .. RtlHigh
+ .. RtlLabelRef
+ .. RtlSymbolRef
+ .. RtxExtra
+ .. RtlAddrDiffVec
+ .. RtlAddrVec
+ .. RtlAsmInput
+ .. RtlAsmOperands
+ .. RtlBarrier
+ .. RtlCall
+ .. RtlClobber
+ .. RtlCodeLabel
+ .. RtlCondExec
+ .. RtlEhReturn
+ .. RtlExprList
+ .. RtlInsnList
+ .. RtlNote
+ .. RtlParallel
+ .. RtlPrefetch
+ .. RtlReturn
+ .. RtlSequence
+ .. RtlSet
+ .. RtlStrictLowPart
+ .. RtlSubreg
+ .. RtlTrapIf
+ .. RtlUnknown
+ .. RtlUnspec
+ .. RtlUnspecVolatile
+ .. RtlUse
+ .. RtlVarLocation
+ .. RtxInsn
+ .. RtlCallInsn
+ .. RtlDebugInsn
+ .. RtlInsn
+ .. RtlJumpInsn
+ .. RtxMatch
+ .. RtlAddress
+ .. RtxObj
+ .. RtlCc0
+ .. RtlConcat
+ .. RtlConcatn
+ .. RtlConstString
+ .. RtlDebugExpr
+ .. RtlDebugImplicitPtr
+ .. RtlLoSum
+ .. RtlMem
+ .. RtlPc
+ .. RtlReg
+ .. RtlScratch
+ .. RtlValue
+ .. RtxTernary
+ .. RtlFma
+ .. RtlIfThenElse
+ .. RtlVecMerge
+ .. RtxUnary
+ .. RtlAbs
+ .. RtlBswap
+ .. RtlClz
+ .. RtlCtz
+ .. RtlFfs
+ .. RtlFix
+ .. RtlFloat
+ .. RtlFloatExtend
+ .. RtlFloatTruncate
+ .. RtlFractConvert
+ .. RtlNeg
+ .. RtlNot
+ .. RtlParity
+ .. RtlPopcount
+ .. RtlSatFract
+ .. RtlSignExtend
+ .. RtlSqrt
+ .. RtlSsAbs
+ .. RtlSsNeg
+ .. RtlSsTruncate
+ .. RtlTruncate
+ .. RtlUnsignedFix
+ .. RtlUnsignedFloat
+ .. RtlUnsignedFractConvert
+ .. RtlUnsignedSatFract
+ .. RtlUsNeg
+ .. RtlUsTruncate
+ .. RtlVecDuplicate
+ .. RtlZeroExtend
diff --git a/gcc-python-cfg.c b/gcc-python-cfg.c
index 1e2f6ef..e326fe0 100644
--- a/gcc-python-cfg.c
+++ b/gcc-python-cfg.c
@@ -22,6 +22,7 @@
#include "gcc-python-wrappers.h"
#include "basic-block.h"
+#include "rtl.h"
/*
"struct edge_def" is declared in basic-block.h, c.f:
@@ -185,6 +186,54 @@ gcc_BasicBlock_get_phi_nodes(PyGccBasicBlock *self, void *closure)
return gcc_python_gimple_seq_to_list(self->bb->il.gimple->phi_nodes);
}
+PyObject *
+gcc_BasicBlock_get_rtl(PyGccBasicBlock *self, void *closure)
+{
+ PyObject *result = NULL;
+
+ rtx insn;
+
+ if (!(self->bb->flags & BB_RTL)) {
+ Py_RETURN_NONE;
+ }
+
+#if 0
+ /* Debugging help: */
+ {
+ fprintf(stderr, "--BEGIN--\n");
+ FOR_BB_INSNS(self->bb, insn) {
+ print_rtl_single (stderr, insn);
+ }
+ fprintf(stderr, "-- END --\n");
+ }
+#endif
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ FOR_BB_INSNS(self->bb, insn) {
+ PyObject *obj;
+
+ obj = gcc_python_make_wrapper_rtl(insn);
+ if (!obj) {
+ goto error;
+ }
+
+ if (PyList_Append(result, obj)) {
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
+
/*
Force a 1-1 mapping between pointer values and wrapper objects
*/
diff --git a/gcc-python-rtl.c b/gcc-python-rtl.c
new file mode 100644
index 0000000..54dc18c
--- /dev/null
+++ b/gcc-python-rtl.c
@@ -0,0 +1,204 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "gcc-python.h"
+#include "gcc-python-wrappers.h"
+#include "gcc-python-compat.h"
+#include "rtl.h"
+#include "tree-flow.h"
+#include "tree-flow-inline.h"
+
+PyObject *
+gcc_Rtl_get_location(struct PyGccRtl *self, void *closure)
+{
+ int locator = INSN_LOCATOR (self->insn);
+ if (locator && insn_file (self->insn)) {
+ return gcc_python_make_wrapper_location(locator_location(locator));
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject *
+get_operand_as_object(const_rtx in_rtx, int idx, char fmt)
+{
+ const char *str;
+
+ /* The operand types are described in gcc/rtl.c */
+ switch (fmt) {
+
+ case 'T': /* pointer to a string, with special meaning */
+ str = XTMPL (in_rtx, idx);
+ goto string;
+
+ case 'S': /* optional pointer to a string */
+ case 's': /* a pointer to a string */
+ str = XSTR (in_rtx, idx);
+ string:
+ return gcc_python_string_or_none(str);
+
+ case '0': /* unused, or used in a phase-dependent manner */
+ Py_RETURN_NONE; /* for now */
+
+ case 'e': /* pointer to an rtl expression */
+ /* Nested expression: */
+ return gcc_python_make_wrapper_rtl(XEXP (in_rtx, idx));
+
+ case 'E':
+ case 'V':
+ /* Nested list of expressions */
+ {
+ PyObject *list = PyList_New(XVECLEN (in_rtx, idx));
+ int j;
+ if (!list) {
+ return NULL;
+ }
+ for (j = 0; j < XVECLEN (in_rtx, idx); j++) {
+ PyObject *item = gcc_python_make_wrapper_rtl(XVECEXP (in_rtx, idx, j));
+ if (!item) {
+ Py_DECREF(list);
+ return NULL;
+ }
+ if (-1 == PyList_Append(list, item)) {
+ Py_DECREF(item);
+ Py_DECREF(list);
+ return NULL;
+ }
+ }
+ return list;
+ }
+
+ case 'w':
+ return gcc_python_int_from_long(XWINT (in_rtx, idx));
+
+ case 'i':
+ return gcc_python_int_from_long(XINT (in_rtx, idx));
+
+ case 'n':
+ /* Return NOTE_INSN names rather than integer codes. */
+ return gcc_python_string_or_none(GET_NOTE_INSN_NAME (XINT (in_rtx, idx)));
+
+ case 'u': /* a pointer to another insn */
+ Py_RETURN_NONE; /* for now */
+
+ case 't':
+ return gcc_python_make_wrapper_tree(XTREE (in_rtx, idx));
+
+ case '*':
+ Py_RETURN_NONE; /* for now */
+
+ case 'B':
+ return gcc_python_make_wrapper_basic_block(XBBDEF (in_rtx, idx));
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+PyObject *
+gcc_Rtl_get_operands(struct PyGccRtl *self, void *closure)
+{
+ const int length = GET_RTX_LENGTH (GET_CODE (self->insn));
+ PyObject *result;
+ int i;
+ const char *format_ptr;
+
+ result = PyTuple_New(length);
+ if (!result) {
+ return NULL;
+ }
+
+ format_ptr = GET_RTX_FORMAT (GET_CODE (self->insn));
+ for (i = 0; i < length; i++) {
+ PyObject *item = get_operand_as_object(self->insn, i, *format_ptr++);
+ if (!item) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(result, i, item);
+ }
+
+ return result;
+}
+
+PyObject *
+gcc_Rtl_repr(struct PyGccRtl * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_Rtl_str(struct PyGccRtl * self)
+{
+ /*
+ We use print_rtl_single() which takes a FILE*, so
+ we need an fmemopen buffer to write to:
+ */
+ char buf[2048]; /* FIXME */
+ FILE *f;
+
+ buf[0] = '\0';
+ f = fmemopen(buf, sizeof(buf), "w");
+ if (!f) {
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ print_rtl_single (f, self->insn);
+
+ fclose(f);
+
+ return gcc_python_string_from_string(buf);
+}
+
+PyObject*
+gcc_python_make_wrapper_rtl(struct rtx_def *insn)
+{
+ struct PyGccRtl *rtl_obj = NULL;
+ PyTypeObject* tp;
+
+ if (!insn) {
+ Py_RETURN_NONE;
+ }
+
+ tp = gcc_python_autogenerated_rtl_type_for_stmt(insn);
+ assert(tp);
+
+ rtl_obj = PyObject_New(struct PyGccRtl, tp);
+ if (!rtl_obj) {
+ goto error;
+ }
+
+ rtl_obj->insn = insn;
+ /* FIXME: do we need to do something for the GCC GC? */
+
+ return (PyObject*)rtl_obj;
+
+error:
+ return NULL;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index fd4be65..e33936f 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -74,6 +74,9 @@ PyObject *
gcc_BasicBlock_get_phi_nodes(PyGccBasicBlock *self, void *closure);
PyObject *
+gcc_BasicBlock_get_rtl(PyGccBasicBlock *self, void *closure);
+
+PyObject *
gcc_Cfg_get_basic_blocks(PyGccCfg *self, void *closure);
/* gcc-python-tree.c: */
@@ -174,6 +177,19 @@ gcc_python_pretty_printer_as_string(PyObject *obj);
void
gcc_PrettyPrinter_dealloc(PyObject *obj);
+/* gcc-python-rtl.c: */
+PyObject *
+gcc_Rtl_get_location(struct PyGccRtl *self, void *closure);
+
+PyObject *
+gcc_Rtl_get_operands(struct PyGccRtl *self, void *closure);
+
+PyObject *
+gcc_Rtl_repr(struct PyGccRtl * self);
+
+PyObject *
+gcc_Rtl_str(struct PyGccRtl * self);
+
PyObject *
gcc_tree_list_from_chain(tree t);
diff --git a/gcc-python.c b/gcc-python.c
index aebcd5a..27aedb6 100644
--- a/gcc-python.c
+++ b/gcc-python.c
@@ -978,6 +978,7 @@ plugin_init (struct plugin_name_args *plugin_info,
autogenerated_parameter_init_types(); /* FIXME: error checking! */
autogenerated_pass_init_types(); /* FIXME: error checking! */
autogenerated_pretty_printer_init_types(); /* FIXME: error checking! */
+ autogenerated_rtl_init_types(); /* FIXME: error checking! */
autogenerated_tree_init_types(); /* FIXME: error checking! */
autogenerated_variable_init_types(); /* FIXME: error checking! */
@@ -992,6 +993,7 @@ plugin_init (struct plugin_name_args *plugin_info,
autogenerated_parameter_add_types(gcc_python_globals.module);
autogenerated_pass_add_types(gcc_python_globals.module);
autogenerated_pretty_printer_add_types(gcc_python_globals.module);
+ autogenerated_rtl_add_types(gcc_python_globals.module);
autogenerated_tree_add_types(gcc_python_globals.module);
autogenerated_variable_add_types(gcc_python_globals.module);
diff --git a/gcc-python.h b/gcc-python.h
index c2d0131..9f15eb7 100644
--- a/gcc-python.h
+++ b/gcc-python.h
@@ -116,6 +116,11 @@ DECLARE_SIMPLE_WRAPPER(PyGccParameter,
param_num,
compiler_param, param_num)
+DECLARE_SIMPLE_WRAPPER(PyGccRtl,
+ gcc_RtlType,
+ rtl,
+ struct rtx_def *, insn)
+
DECLARE_SIMPLE_WRAPPER(PyGccTree,
gcc_TreeType,
tree, tree, t)
@@ -167,6 +172,12 @@ int autogenerated_pretty_printer_init_types(void);
void autogenerated_pretty_printer_add_types(PyObject *m);
+/* autogenerated-rtl.c */
+int autogenerated_rtl_init_types(void);
+void autogenerated_rtl_add_types(PyObject *m);
+PyTypeObject * gcc_python_autogenerated_rtl_type_for_stmt(struct rtx_def *insn);
+
+
/* autogenerated-tree.c */
int autogenerated_tree_init_types(void);
void autogenerated_tree_add_types(PyObject *m);
diff --git a/generate-cfg-c.py b/generate-cfg-c.py
index 1c22dec..c9e9ea8 100644
--- a/generate-cfg-c.py
+++ b/generate-cfg-c.py
@@ -100,6 +100,10 @@ def generate_basic_block():
'gcc_BasicBlock_get_phi_nodes',
None,
'The list of gcc.GimplePhi phoney functions, if appropriate for this pass, or None'),
+ PyGetSetDef('rtl',
+ 'gcc_BasicBlock_get_rtl',
+ None,
+ 'The list of gcc.Rtl instructions, if appropriate for this pass, or None'),
],
identifier_prefix='gcc_BasicBlock',
typename='PyGccBasicBlock')
diff --git a/generate-rtl-c.py b/generate-rtl-c.py
new file mode 100644
index 0000000..b516009
--- /dev/null
+++ b/generate-rtl-c.py
@@ -0,0 +1,193 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+from maketreetypes import iter_rtl_expr_types #, iter_rtl_struct_types
+
+from cpybuilder import *
+
+cu = CompilationUnit()
+cu.add_include('gcc-python.h')
+cu.add_include('gcc-python-wrappers.h')
+cu.add_include('gcc-plugin.h')
+cu.add_include("rtl.h")
+
+modinit_preinit = ''
+modinit_postinit = ''
+
+#rtl_struct_types = list(iter_rtl_struct_types())
+rtl_expr_types = list(iter_rtl_expr_types())
+#print rtl_types
+
+# Should be a three-level hierarchy:
+#
+# - Rtl base class
+# - intermediate subclasses, one per enum rtx_class
+# - concrete subclasses, one per enum rtx_code
+
+def generate_rtl_base_class():
+ #
+ # Generate the gcc.Rtl class:
+ #
+ global modinit_preinit
+ global modinit_postinit
+ '''
+ cu.add_defn("""
+static PyObject *
+gcc_Rtl_get_block(struct PyGccRtl *self, void *closure)
+{
+ return gcc_python_make_wrapper_tree(rtl_block(self->stmt));
+}
+""")
+ '''
+ getsettable = PyGetSetDefTable('gcc_Rtl_getset_table', [])
+ getsettable.add_gsdef('loc',
+ 'gcc_Rtl_get_location',
+ None,
+ 'Source code location of this expression, as a gcc.Location')
+ getsettable.add_gsdef('operands',
+ 'gcc_Rtl_get_operands',
+ None,
+ 'Operands of this expression, as a tuple')
+ cu.add_defn(getsettable.c_defn())
+
+ pytype = PyTypeObject(identifier = 'gcc_RtlType',
+ localname = 'Rtl',
+ tp_name = 'gcc.Rtl',
+ struct_name = 'struct PyGccRtl',
+ tp_new = 'PyType_GenericNew',
+ tp_getset = getsettable.identifier,
+ tp_repr = '(reprfunc)gcc_Rtl_repr',
+ tp_str = '(reprfunc)gcc_Rtl_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_rtl_base_class()
+
+# enum rtx_class from gcc/rtl.h (as seen in 4.6.0):
+enum_rtx_class = ('RTX_COMPARE',
+ 'RTX_COMM_COMPARE',
+ 'RTX_BIN_ARITH',
+ 'RTX_COMM_ARITH',
+ 'RTX_UNARY',
+ 'RTX_EXTRA',
+ 'RTX_MATCH',
+ 'RTX_INSN',
+ 'RTX_OBJ',
+ 'RTX_CONST_OBJ',
+ 'RTX_TERNARY',
+ 'RTX_BITFIELD_OPS',
+ 'RTX_AUTOINC')
+
+def generate_intermediate_rtx_class_subclasses():
+ global modinit_preinit
+ global modinit_postinit
+
+ for rtx_class in enum_rtx_class:
+ cc = camel_case(rtx_class)
+ pytype = PyTypeObject(identifier = 'gcc_RtlClassType%sType' % cc,
+ localname = 'RtlClassType' + cc,
+ tp_name = 'gcc.%s' % cc,
+ struct_name = 'struct PyGccRtl',
+ tp_new = 'PyType_GenericNew',
+ tp_base = '&gcc_RtlType',
+ #tp_getset = getsettable.identifier,
+ #tp_repr = '(reprfunc)gcc_Rtl_repr',
+ #tp_str = '(reprfunc)gcc_Rtl_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_intermediate_rtx_class_subclasses()
+
+
+def generate_concrete_rtx_code_subclasses():
+ global modinit_preinit
+ global modinit_postinit
+
+ for expr_type in rtl_expr_types:
+
+ cc = expr_type.camel_cased_string()
+
+ getsettable = None
+ if getsettable:
+ cu.add_defn(getsettable.c_defn())
+
+ pytype = PyTypeObject(identifier = 'gcc_%sType' % cc,
+ localname = cc,
+ tp_name = 'gcc.%s' % cc,
+ struct_name = 'struct PyGccRtl',
+ tp_new = 'PyType_GenericNew',
+ #tp_base = '&gcc_RtlType',
+ tp_base = ('&gcc_RtlClassType%sType'
+ % camel_case(expr_type.CLASS)),
+ tp_getset = getsettable.identifier if getsettable else None,
+ tp_repr = '(reprfunc)gcc_Rtl_repr',
+ tp_str = '(reprfunc)gcc_Rtl_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_concrete_rtx_code_subclasses()
+
+def generate_rtl_code_map():
+ cu.add_defn('\n/* Map from GCC rtl codes to PyTypeObject* */\n')
+ cu.add_defn('PyTypeObject *pytype_for_rtx_code[] = {\n')
+ for expr_type in rtl_expr_types:
+ cc = expr_type.camel_cased_string()
+ cu.add_defn(' &gcc_%sType, /* %s */\n' % (cc, expr_type.ENUM))
+ cu.add_defn('};\n\n')
+
+ cu.add_defn("""
+PyTypeObject*
+gcc_python_autogenerated_rtl_type_for_stmt(struct rtx_def *insn)
+{
+ enum rtx_code code = insn->code;
+
+ /* printf("code:%i\\n", code); */
+ assert(code >= 0);
+ assert(code < LAST_AND_UNUSED_RTL_CODE);
+ return pytype_for_rtx_code[code];
+}
+""")
+
+generate_rtl_code_map()
+
+cu.add_defn("""
+int autogenerated_rtl_init_types(void)
+{
+""" + modinit_preinit + """
+ return 1;
+
+error:
+ return 0;
+}
+""")
+
+cu.add_defn("""
+void autogenerated_rtl_add_types(PyObject *m)
+{
+""" + modinit_postinit + """
+}
+""")
+
+
+print(cu.as_str())
+
diff --git a/maketreetypes.py b/maketreetypes.py
index b7640ef..d29f46d 100644
--- a/maketreetypes.py
+++ b/maketreetypes.py
@@ -82,3 +82,26 @@ def iter_gimple_struct_types():
# print 'UNMATCHED: ', line
assert(line.startswith('#') or line.strip() == '' or line.startswith('GIMPLE_'))
f.close()
+
+# Corresponds to a DEF_RTL_EXPR macro from rtl.def
+class DefRtlExpr(namedtuple('DefRtlExpr', 'ENUM, NAME, FORMAT, CLASS')):
+ def camel_cased_string(self):
+ return 'Rtl' + camel_case(self.ENUM)
+
+def iter_rtl_expr_types():
+ import re
+ f = open('autogenerated-rtl-types.txt')
+ for line in f:
+ # e.g.
+ # rtl_expr: DEBUG_EXPR, "debug_expr", "0", RTX_OBJ
+ m = re.match('rtl_expr: (.+), "(.+)", (.*), (.+)', line)
+ if m:
+ #print m.groups()
+ yield DefRtlExpr(ENUM=m.group(1),
+ NAME=m.group(2),
+ FORMAT=m.group(3),
+ CLASS=m.group(4))
+ else:
+ #print 'UNMATCHED: %r' % line
+ assert(line.startswith('#') or line.strip() == '' or line.startswith('GIMPLE_'))
+ f.close()
diff --git a/rtl-types.txt.in b/rtl-types.txt.in
new file mode 100644
index 0000000..4c0a5c7
--- /dev/null
+++ b/rtl-types.txt.in
@@ -0,0 +1,4 @@
+#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) \
+ rtl_expr: ENUM, NAME, FORMAT, CLASS
+#include "rtl.def"
+#undef DEF_RTL_EXPR
diff --git a/tests/plugin/rtl/input.c b/tests/plugin/rtl/input.c
new file mode 100644
index 0000000..b86fb3f
--- /dev/null
+++ b/tests/plugin/rtl/input.c
@@ -0,0 +1,33 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ printf("Hello, python\n");
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/plugin/rtl/script.py b/tests/plugin/rtl/script.py
new file mode 100644
index 0000000..f2c8226
--- /dev/null
+++ b/tests/plugin/rtl/script.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+import gcc
+help(gcc.Rtl)
+
+def on_pass_execution(p, fn):
+ if p.properties_provided & gcc.PROP_rtl:
+ # For this pass, "fn" will be an instance of gcc.Function:
+ if not fn:
+ return
+
+ if fn.cfg:
+ for bb in fn.cfg.basic_blocks:
+ if bb.rtl:
+ for i,stmt in enumerate(bb.rtl):
+ assert isinstance(stmt, gcc.Rtl)
+ # Ensure that we can evaluate the "operands" attribute
+ # on every stmt we see:
+ stmt.operands
+ # print(' rtl[%i]:' % i)
+
+# Wire up our callback:
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ on_pass_execution)
+
diff --git a/tests/plugin/rtl/stdout.txt b/tests/plugin/rtl/stdout.txt
new file mode 100644
index 0000000..2720e05
--- /dev/null
+++ b/tests/plugin/rtl/stdout.txt
@@ -0,0 +1,26 @@
+Help on class Rtl in module gcc:
+
+class Rtl(__builtin__.object)
+ | Methods defined here:
+ |
+ | __repr__(...)
+ | x.__repr__() <==> repr(x)
+ |
+ | __str__(...)
+ | x.__str__() <==> str(x)
+ |
+ | ----------------------------------------------------------------------
+ | Data descriptors defined here:
+ |
+ | loc
+ | Source code location of this expression, as a gcc.Location
+ |
+ | operands
+ | Operands of this expression, as a tuple
+ |
+ | ----------------------------------------------------------------------
+ | Data and other attributes defined here:
+ |
+ | __new__ = <built-in method __new__ of type object>
+ | T.__new__(S, ...) -> a new object with type S, a subtype of T
+
12 years, 9 months
[gcc-python-plugin] Get rid of the PyRuntime class
by David Hugh Malcolm
commit 629182bacb7c2fa35accfbd005b4d2153c36fa7b
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Jul 28 16:29:22 2011 -0400
Get rid of the PyRuntime class
PyRuntime was an attempt to run the tests against multiple pythons.
However, it was clumsy, and contained hardcoded values (for python 2.7)
This patch removes it, instead simply building with sys.executable
Doing so means that we're now building and running the selftests against
the version of python that the plugin was built against, rather than
hardcoding /usr/bin/python2.7
In particular, when building the plugin for Python 3, it means that the
selftests are now testing compiling against the Python 3 headers.
Doing this located some real bugs for this case, which this patch also
fixes.
cpybuilder.py | 48 ----------
libcpychecker/PyArg_ParseTuple.py | 10 ++-
libcpychecker/refcounts.py | 4 +-
libcpychecker/types.py | 13 +++
run-test-suite.py | 5 +-
testcpybuilder.py | 99 +++++++++++---------
testcpychecker.py | 17 ++--
.../PyArg_ParseTuple/correct_codes_S_and_U/input.c | 20 +++-
8 files changed, 105 insertions(+), 111 deletions(-)
---
diff --git a/cpybuilder.py b/cpybuilder.py
index 10a2f46..a76165b 100644
--- a/cpybuilder.py
+++ b/cpybuilder.py
@@ -516,51 +516,3 @@ class PyVersionInfo(namedtuple('PyVersionInfo', 'major minor micro releaselevel
micro=int(m.group(3)),
releaselevel=m.group(4),
serial=int(m.group(5)))
-
-class PyRuntime:
- def __init__(self, executable, config):
- self.executable = executable
- self.config = config
-
- pydebug = self.run_command('import sys; print(hasattr(sys, "getobjects"))').strip()
- assert pydebug in ('False', 'True')
- self.pydebug = (pydebug == 'True')
-
- versiontext = self.run_command('import sys; print(sys.version_info)').strip()
- self.versioninfo = PyVersionInfo.from_text(versiontext)
-
- def is_py3k(self):
- return self.versioninfo.major == 3
-
- def get_build_flags(self):
- return self.get_config_value(['--cflags', '--ldflags'])
-
- def get_config_value(self, flags):
- args = [self.config] + flags
- p = Popen(args, stdout=PIPE, stderr=PIPE)
- out, err = p.communicate()
- if six.PY3:
- out = out.decode()
- err = err.decode()
- return ' '.join(out.splitlines()).strip()
-
- def run_command(self, cmd, checkoutput=True):
- p = Popen([self.executable, '-c', cmd],
- stdout=PIPE, stderr=PIPE)
- out, err = p.communicate()
- if six.PY3:
- out = out.decode()
- err = err.decode()
- if p.returncode != 0:
- raise PyRuntimeError(self, cmd, out, err, p)
- return out
-
- #def compile_simple_module(self, sm):
-
- def get_module_filename(self, name):
- # FIXME: this is a Fedora-ism:
- # FIXME: support for 3.2 onwards also?
- if self.pydebug:
- return '%s_d.so' % name
- else:
- return '%s.so' % name
diff --git a/libcpychecker/PyArg_ParseTuple.py b/libcpychecker/PyArg_ParseTuple.py
index fbac9d2..f422944 100644
--- a/libcpychecker/PyArg_ParseTuple.py
+++ b/libcpychecker/PyArg_ParseTuple.py
@@ -327,8 +327,14 @@ class PyArgParseFmt:
else:
result.add_argument('u', [Py_UNICODE().pointer.pointer])
elif c == 'S':
- result.add_argument('S', [(get_PyStringObject().pointer.pointer,
- get_PyObject().pointer.pointer)])
+ if is_py3k():
+ # S (bytes) [PyBytesObject *] (or PyObject *)
+ result.add_argument('S', [(get_PyBytesObject().pointer.pointer,
+ get_PyObject().pointer.pointer)])
+ else:
+ # S (string) [PyStringObject *] (or PyObject *)
+ result.add_argument('S', [(get_PyStringObject().pointer.pointer,
+ get_PyObject().pointer.pointer)])
elif c == 'U':
result.add_argument('U', [(get_PyUnicodeObject().pointer.pointer,
get_PyObject().pointer.pointer)])
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index a6a07e8..2e82dcf 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -104,7 +104,7 @@ class GenericTpDealloc(AbstractValue):
# for this state by assigning it with a special "DeallocatedMemory"
# value
# Clear the value for any fields within the region:
- for k, v in region.fields.iteritems():
+ for k, v in region.fields.items():
if v in new.value_for_region:
del new.value_for_region[v]
# Set the default value for the whole region to be "DeallocatedMemory"
@@ -631,7 +631,7 @@ def dump_traces_to_stdout(traces):
print(' %s' % endstate.exception_rvalue)
if i + 1 < len(traces):
- print
+ sys.stdout.write('\n')
def check_refcounts(fun, dump_traces=False, show_traces=False):
"""
diff --git a/libcpychecker/types.py b/libcpychecker/types.py
index cfc42dc..8b1647b 100644
--- a/libcpychecker/types.py
+++ b/libcpychecker/types.py
@@ -21,6 +21,15 @@ Helper functions for looking up various CPython implementation types.
import gcc
from gccutils import get_global_typedef
+def is_py3k():
+ """
+ Is the Python.h we're compiling against python 3?
+ """
+ if get_global_typedef('PyStringObject'):
+ return False
+ else:
+ return True
+
def get_Py_ssize_t():
return get_global_typedef('Py_ssize_t')
@@ -54,3 +63,7 @@ def get_PyUnicodeObject():
def get_Py_complex():
return get_global_typedef('Py_complex')
+
+# Python 3:
+def get_PyBytesObject():
+ return get_global_typedef('PyBytesObject')
diff --git a/run-test-suite.py b/run-test-suite.py
index c0b9960..5f746b2 100644
--- a/run-test-suite.py
+++ b/run-test-suite.py
@@ -63,7 +63,8 @@ class TestStream:
# replace long literals with int literals:
expdata = re.sub('([0-9]+)L', '\g<1>', expdata)
expdata = re.sub('(0x[0-9a-f]+)L', '\g<1>', expdata)
-
+ expdata = expdata.replace('struct PyStringObject',
+ 'struct PyBytesObject')
# The expected data is for 64-bit builds of Python
# Fix it up for 32-bit builds as necessary:
if six.MAXSIZE == 0x7fffffff:
@@ -239,7 +240,7 @@ num_passes = 0
failed_tests = []
for testdir in testdirs:
try:
- print('%s: ' % testdir),
+ sys.stdout.write('%s: ' % testdir)
run_test(testdir)
print('OK')
num_passes += 1
diff --git a/testcpybuilder.py b/testcpybuilder.py
index e8b7f70..fef73ec 100644
--- a/testcpybuilder.py
+++ b/testcpybuilder.py
@@ -15,6 +15,8 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
+
+from distutils import sysconfig as sc
import os
import shutil
from subprocess import Popen, PIPE
@@ -26,13 +28,13 @@ import six
from cpybuilder import *
-
-# FIXME: this will need tweaking:
-pyruntimes = [PyRuntime('/usr/bin/python2.7', '/usr/bin/python2.7-config'),
- #PyRuntime('/usr/bin/python2.7-debug', '/usr/bin/python2.7-debug-config'),
- #PyRuntime('/usr/bin/python3.2mu', '/usr/bin/python3.2mu-config'),
- #PyRuntime('/usr/bin/python3.2dmu', '/usr/bin/python3.2dmu-config')
- ]
+def get_module_filename(name):
+ # FIXME: this is a Fedora-ism:
+ # FIXME: support for 3.2 onwards also?
+ if hasattr(sys, "getobjects"):
+ return '%s_d.so' % name
+ else:
+ return '%s.so' % name
class CompilationError(CommandError):
def __init__(self, bm):
@@ -43,16 +45,15 @@ class CompilationError(CommandError):
return 'compiling: %s' % ' '.join(self.bm.args)
class BuiltModule:
- """A test build of a SimpleModule for a PyRuntime, done in a tempdir"""
- def __init__(self, sm, runtime):
+ """A test build of a SimpleModule using sys.executable, done in a tempdir"""
+ def __init__(self, sm):
self.sm = sm
- self.runtime = runtime
- def write_src(self, extra_cflags = None):
+ def write_src(self, modname, extra_cflags = None):
self.tmpdir = tempfile.mkdtemp()
- self.srcfile = os.path.join(self.tmpdir, 'example.c')
- self.modfile = os.path.join(self.tmpdir, self.runtime.get_module_filename('example'))
+ self.srcfile = os.path.join(self.tmpdir, '%s.c' % modname)
+ self.modfile = os.path.join(self.tmpdir, get_module_filename(modname))
f = open(self.srcfile, 'w')
f.write(self.sm.cu.as_str())
@@ -60,16 +61,17 @@ class BuiltModule:
def compile_src(self, extra_cflags = None):
- cflags = self.runtime.get_build_flags()
self.args = ['gcc']
self.args += ['-o', self.modfile]
- self.args += cflags.split()
+ self.args += ['-I' + sc.get_python_inc(),
+ '-I' + sc.get_python_inc(plat_specific=True)]
+ self.args += sc.get_config_var('CFLAGS').split()
self.args += ['-Wall', '-Werror'] # during testing
self.args += ['-shared'] # not sure why this is necessary
if extra_cflags:
self.args += extra_cflags
self.args += [self.srcfile]
- #print self.args
+ # print(self.args)
env = dict(os.environ)
env['LC_ALL'] = 'C'
@@ -85,24 +87,29 @@ class BuiltModule:
raise CompilationError(self)
assert os.path.exists(self.modfile)
+ # print(self.modfile)
- def build(self, extra_cflags = None):
- self.write_src()
+ def build(self, modname, extra_cflags = None):
+ self.write_src(modname)
self.compile_src(extra_cflags)
- def run_command(self, cmd):
- """Run the command (using the appropriate PyRuntime), adjusting sys.path first"""
- out = self.runtime.run_command('import sys; sys.path.append("%s"); %s' % (self.tmpdir, cmd))
- return out
-
def cleanup(self):
shutil.rmtree(self.tmpdir)
class SimpleTest(unittest.TestCase):
- #def __init__(self, pyruntime):
- # self.pytun
+
+ # We'll be manipulating sys.path during the test
+ # Save a copy, and restore it after each test:
+ def setUp(self):
+ self.saved_sys_path = sys.path
+ sys.path = sys.path[:]
+
+ def tearDown(self):
+ sys.path = self.saved_sys_path
+
def test_simple_compilation(self):
# Verify building and running a trivial module (against multiple Python runtimes)
+ MODNAME = 'simple_compilation'
sm = SimpleModule()
sm.cu.add_decl("""
@@ -123,23 +130,24 @@ example_hello(PyObject *self, PyObject *args)
METH_VARARGS, 'Return a greeting.')])
sm.cu.add_defn(methods.c_defn())
- sm.add_module_init('example', modmethods=methods, moddoc='This is a doc string')
+ sm.add_module_init(MODNAME, modmethods=methods, moddoc='This is a doc string')
# print sm.cu.as_str()
- for runtime in pyruntimes:
- # Build the module:
- bm = BuiltModule(sm, runtime)
- bm.build()
+ # Build the module:
+ bm = BuiltModule(sm)
+ bm.build(MODNAME)
- # Verify that it built:
- out = bm.run_command('import example; print(example.hello())')
- self.assertEqual(out, "Hello world!\n")
+ # Verify that it built:
+ sys.path.append(bm.tmpdir)
+ import simple_compilation
+ self.assertEqual(simple_compilation.hello(), 'Hello world!')
- # Cleanup successful test runs:
- bm.cleanup()
+ # Cleanup successful test runs:
+ bm.cleanup()
def test_module_with_type(self):
# Verify an extension with a type
+ MODNAME = 'module_with_type'
sm = SimpleModule()
sm.cu.add_decl("""
@@ -164,20 +172,21 @@ struct PyExampleType {
struct_name = 'struct PyExampleType',
tp_repr = 'example_Example_repr')
- sm.add_module_init('example', modmethods=None, moddoc='This is a doc string')
+ sm.add_module_init(MODNAME, modmethods=None, moddoc='This is a doc string')
# print sm.cu.as_str()
- for runtime in pyruntimes:
- # Build the module:
- bm = BuiltModule(sm, runtime)
- bm.build()
+ # Build the module:
+ bm = BuiltModule(sm)
+ bm.build(MODNAME)
- # Verify that it built:
- out = bm.run_command('import example; print(repr(example.ExampleType()))')
- self.assertEqual(out, "example.ExampleType('')\n")
+ # Verify that it built:
+ sys.path.append(bm.tmpdir)
+ import module_with_type
+ self.assertEqual(repr(module_with_type.ExampleType()),
+ "example.ExampleType('')")
- # Cleanup successful test runs:
- bm.cleanup()
+ # Cleanup successful test runs:
+ bm.cleanup()
def test_version_parsing(self):
vi = PyVersionInfo.from_text("sys.version_info(major=2, minor=7, micro=1, releaselevel='final', serial=0)")
diff --git a/testcpychecker.py b/testcpychecker.py
index 8eae4ca..54beb69 100644
--- a/testcpychecker.py
+++ b/testcpychecker.py
@@ -23,12 +23,9 @@ import unittest
import six
-from testcpybuilder import BuiltModule, PyRuntime, SimpleModule, CompilationError
+from testcpybuilder import BuiltModule, SimpleModule, CompilationError
from cpybuilder import PyMethodTable, PyMethodDef, METH_VARARGS
-#FIXME:
-pyruntime = PyRuntime('/usr/bin/python2.7', '/usr/bin/python2.7-config')
-
class ExpectedErrorNotFound(CompilationError):
def __init__(self, expected_err, actual_err, bm):
CompilationError.__init__(self, bm)
@@ -59,7 +56,7 @@ class AnalyzerTests(unittest.TestCase):
'-fplugin-arg-python-script=cpychecker.py'])
def build_module(self, bm):
- bm.write_src()
+ bm.write_src('example')
self.compile_src(bm)
def assertNoErrors(self, src):
@@ -68,7 +65,7 @@ class AnalyzerTests(unittest.TestCase):
else:
sm = SimpleModule()
sm.cu.add_defn(src)
- bm = BuiltModule(sm, pyruntime)
+ bm = BuiltModule(sm)
self.build_module(bm)
bm.cleanup()
return bm
@@ -79,9 +76,9 @@ class AnalyzerTests(unittest.TestCase):
else:
sm = SimpleModule()
sm.cu.add_defn(src)
- bm = BuiltModule(sm, pyruntime)
+ bm = BuiltModule(sm)
try:
- bm.write_src()
+ bm.write_src('example')
experr = experr.replace('$(SRCFILE)', bm.srcfile)
self.compile_src(bm)
except CompilationError:
@@ -127,7 +124,11 @@ socket_htons(PyObject *self, PyObject *args)
return NULL;
}
x2 = (int)htons((short)x1);
+#if PY_MAJOR_VERSION >= 3
+ return PyLong_FromLong(x2);
+#else
return PyInt_FromLong(x2);
+#endif
}
"""
self.assertFindsError(src,
diff --git a/tests/cpychecker/PyArg_ParseTuple/correct_codes_S_and_U/input.c b/tests/cpychecker/PyArg_ParseTuple/correct_codes_S_and_U/input.c
index c7e8fde..2a8883b 100644
--- a/tests/cpychecker/PyArg_ParseTuple/correct_codes_S_and_U/input.c
+++ b/tests/cpychecker/PyArg_ParseTuple/correct_codes_S_and_U/input.c
@@ -38,11 +38,15 @@ less_rigid_code_S(PyObject *self, PyObject *args)
Both of these ought to be acceptable for code "S":
*/
PyObject *S_baseobj;
- PyStringObject *S_stringobj;
+#if PY_MAJOR_VERSION >= 3
+ PyBytesObject *S_subobj;
+#else
+ PyStringObject *S_subobj;
+#endif
if (!PyArg_ParseTuple(args, "SS",
- &S_baseobj,
- &S_stringobj)) {
+ &S_baseobj,
+ &S_subobj)) {
return NULL;
}
@@ -59,10 +63,18 @@ less_rigid_code_U(PyObject *self, PyObject *args)
PyUnicodeObject *U_uniobj;
if (!PyArg_ParseTuple(args, "UU",
- &U_baseobj,
+ &U_baseobj,
&U_uniobj)) {
return NULL;
}
Py_RETURN_NONE;
}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
12 years, 9 months
[gcc-python-plugin] Improve the comment within examples/show-callgraph.py
by David Hugh Malcolm
commit d6630ed34b0af544ca14427d4839dd60e23abccb
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 19:50:52 2011 -0400
Improve the comment within examples/show-callgraph.py
examples/show-callgraph.py | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
---
diff --git a/examples/show-callgraph.py b/examples/show-callgraph.py
index 54be076..c465a4e 100644
--- a/examples/show-callgraph.py
+++ b/examples/show-callgraph.py
@@ -22,6 +22,12 @@ from gccutils import callgraph_to_dot, invoke_dot
def on_pass_execution(p, fn):
if p.name == '*free_lang_data':
+ # The '*free_lang_data' pass is called once, rather than per-function,
+ # and occurs immediately after "*build_cgraph_edges", which is the
+ # pass that initially builds the callgraph
+ #
+ # So at this point we're likely to get a good view of the callgraph
+ # before further optimization passes manipulate it:
dot = callgraph_to_dot()
invoke_dot(dot)
12 years, 9 months
[gcc-python-plugin] Wrap GCC's callgraph information, exposing it via a Python API
by David Hugh Malcolm
commit 6c63c68ba85134c003977b5e4e8cd4347abbf859
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 19:39:24 2011 -0400
Wrap GCC's callgraph information, exposing it via a Python API
Introduce:
gcc.CallgraphNode
gcc.CallgraphEdge
gcc.get_callgraph_nodes()
gccutils.callgraph_to_dot
New sample script:
examples/show-callgraph.py
Document the above
Add selftest
Makefile | 5 +
docs/callgraph.rst | 71 +++++++++++++++
docs/examples.rst | 21 +++++
docs/misc.rst | 1 +
docs/sample-callgraph.png | Bin 0 -> 6441 bytes
docs/tree.rst | 5 +
examples/show-callgraph.py | 29 ++++++
gcc-python-callgraph.c | 171 +++++++++++++++++++++++++++++++++++++
gcc-python-wrappers.h | 25 ++++++
gcc-python.c | 41 +++++++++
gcc-python.h | 14 +++
gccutils.py | 31 +++++++
generate-callgraph-c.py | 130 ++++++++++++++++++++++++++++
generate-tree-c.py | 3 +
tests/plugin/callgraph/input.c | 47 ++++++++++
tests/plugin/callgraph/script.py | 48 ++++++++++
tests/plugin/callgraph/stdout.txt | 94 ++++++++++++++++++++
17 files changed, 736 insertions(+), 0 deletions(-)
---
diff --git a/Makefile b/Makefile
index c378595..105c64e 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ GCC=gcc
PLUGIN_SOURCE_FILES= \
gcc-python.c \
+ gcc-python-callgraph.c \
gcc-python-cfg.c \
gcc-python-closure.c \
gcc-python-function.c \
@@ -30,6 +31,7 @@ PLUGIN_SOURCE_FILES= \
gcc-python-pretty-printer.c \
gcc-python-tree.c \
gcc-python-variable.c \
+ autogenerated-callgraph.c \
autogenerated-cfg.c \
autogenerated-option.c \
autogenerated-function.c \
@@ -105,6 +107,9 @@ autogenerated-gimple-types.txt: gimple-types.txt.in
autogenerated-tree-types.txt: tree-types.txt.in
cpp $(CFLAGS) $^ -o $@
+autogenerated-callgraph.c: cpybuilder.py generate-callgraph-c.py
+ python generate-callgraph-c.py > $@
+
autogenerated-cfg.c: cpybuilder.py generate-cfg-c.py
$(PYTHON) generate-cfg-c.py > $@
diff --git a/docs/callgraph.rst b/docs/callgraph.rst
new file mode 100644
index 0000000..13d1970
--- /dev/null
+++ b/docs/callgraph.rst
@@ -0,0 +1,71 @@
+.. Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+
+Interprocedural analysis (IPA)
+==============================
+GCC builds a "call graph", recording which functions call which other
+functions, and it uses this for various optimizations.
+
+It is constructed by the `"*build_cgraph_edges"` pass.
+
+In case it's of interest, it is available via the following Python API:
+
+.. py:function:: gcc.get_callgraph_nodes()
+
+ Get a list of all :py:class:`gcc.CallgraphNode` instances
+
+.. py:function:: gccutils.callgraph_to_dot()
+
+ Return the GraphViz source for a rendering of the current callgraph, as a
+ string.
+
+ Here's an example of such a rendering:
+
+ .. figure:: sample-callgraph.png
+ :alt: image of a call graph
+
+.. py:class:: gcc.CallgraphNode
+
+ .. py:attribute:: decl
+
+ The :py:class:`gcc.FunctionDecl` for this node within the callgraph
+
+ .. py:attribute:: callees
+
+ The function calls made by this function, as a list of :py:class:`gcc.CallgraphEdge` instances
+
+ .. py:attribute:: callers
+
+ The places that call this function, as a list of :py:class:`gcc.CallgraphEdge` instances
+
+ Internally, this wraps a `struct cgraph_node *`
+
+.. py:class:: gcc.CallgraphEdge
+
+ .. py:attribute:: caller
+
+ The function that makes this call, as a :py:class:`gcc.CallgraphNode`
+
+ .. py:attribute:: callee
+
+ The function that is called here, as a :py:class:`gcc.CallgraphNode`
+
+ .. py:attribute:: call_stmt
+
+ The :py:class:`gcc.GimpleCall` statememt for the function call
+
+ Internally, this wraps a `struct cgraph_edge *`
diff --git a/docs/examples.rst b/docs/examples.rst
index cb448cd..00ddeac 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -139,3 +139,24 @@ The Python code for this is:
.. literalinclude:: ../examples/show-ssa.py
:lines: 17-
:language: python
+
+`show-callgraph.py`
+-------------------
+
+This simple script sends GCC's interprocedural analysis data through GraphViz.
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/show-callgraph.py test.c
+
+It generates an image file showing which functions call which other functions,
+and opens it in a viewer.
+
+.. figure:: sample-callgraph.png
+ :alt: image of a call graph
+
+The Python code for this is:
+
+ .. literalinclude:: ../examples/show-callgraph.py
+ :lines: 18-
+ :language: python
diff --git a/docs/misc.rst b/docs/misc.rst
index ec1f831..0c707e1 100644
--- a/docs/misc.rst
+++ b/docs/misc.rst
@@ -24,6 +24,7 @@ scripts.
.. toctree::
+ callgraph.rst
options.rst
parameters.rst
versions.rst
diff --git a/docs/sample-callgraph.png b/docs/sample-callgraph.png
new file mode 100644
index 0000000..7437458
Binary files /dev/null and b/docs/sample-callgraph.png differ
diff --git a/docs/tree.rst b/docs/tree.rst
index 566a605..0ef2912 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -217,6 +217,11 @@ Declarations
The :py:class:`gcc.ResultDecl` representing the return value of this
function
+ .. py:attribute:: callgraph_node
+
+ The :py:class:`gcc.CallgraphNode` for this function declaration, or
+ `None`
+
.. Declaration
.. ClassMethodDecl
.. ConstDecl
diff --git a/examples/show-callgraph.py b/examples/show-callgraph.py
new file mode 100644
index 0000000..54be076
--- /dev/null
+++ b/examples/show-callgraph.py
@@ -0,0 +1,29 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+# Sample python script, to be run by our gcc plugin
+# Show the call graph (interprocedural analysis), using GraphViz
+import gcc
+from gccutils import callgraph_to_dot, invoke_dot
+
+def on_pass_execution(p, fn):
+ if p.name == '*free_lang_data':
+ dot = callgraph_to_dot()
+ invoke_dot(dot)
+
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ on_pass_execution)
diff --git a/gcc-python-callgraph.c b/gcc-python-callgraph.c
new file mode 100644
index 0000000..4aad047
--- /dev/null
+++ b/gcc-python-callgraph.c
@@ -0,0 +1,171 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "gcc-python.h"
+#include "gcc-python-wrappers.h"
+
+/*
+ Wrapper for various types in gcc/cgraph.h
+ struct cgraph_edge *
+ struct cgraph_node *
+*/
+
+PyObject *
+gcc_CallgraphEdge_repr(struct PyGccCallgraphEdge * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphEdge_str(struct PyGccCallgraphEdge * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphNode_repr(struct PyGccCallgraphNode * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphNode_str(struct PyGccCallgraphNode * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphNode_get_callees(struct PyGccCallgraphNode * self)
+{
+ PyObject *result;
+ struct cgraph_edge *edge;
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ for (edge = self->node->callees; edge ; edge = edge->next_callee) {
+ PyObject *obj_var = gcc_python_make_wrapper_cgraph_edge(edge);
+ if (!obj_var) {
+ goto error;
+ }
+ if (-1 == PyList_Append(result, obj_var)) {
+ Py_DECREF(obj_var);
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
+PyObject *
+gcc_CallgraphNode_get_callers(struct PyGccCallgraphNode * self)
+{
+ PyObject *result;
+ struct cgraph_edge *edge;
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ for (edge = self->node->callers; edge ; edge = edge->next_caller) {
+ PyObject *obj_var = gcc_python_make_wrapper_cgraph_edge(edge);
+ if (!obj_var) {
+ goto error;
+ }
+ if (-1 == PyList_Append(result, obj_var)) {
+ Py_DECREF(obj_var);
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
+PyObject *
+gcc_python_make_wrapper_cgraph_edge(struct cgraph_edge * edge)
+{
+ struct PyGccCallgraphEdge *obj = NULL;
+
+ obj = PyObject_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;
+}
+
+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);
+ if (!obj) {
+ goto error;
+ }
+
+ obj->node = node;
+ /* FIXME: do we need to do something for the GCC GC? */
+
+ return (PyObject*)obj;
+
+error:
+ return NULL;
+}
+
+static PyObject *cgraph_node_wrapper_cache = NULL;
+PyObject *
+gcc_python_make_wrapper_cgraph_node(struct cgraph_node * node)
+{
+ return gcc_python_lazily_create_wrapper(&cgraph_node_wrapper_cache,
+ node,
+ real_make_cgraph_node_wrapper);
+}
+
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index 2a82d88..fd4be65 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -23,12 +23,34 @@
#include "gcc-python.h"
#include "tree-pass.h"
#include "opts.h"
+#include "cgraph.h"
PyMODINIT_FUNC initoptpass(void);
+/* gcc-python-callgraph.c: */
+PyObject *
+gcc_CallgraphEdge_repr(struct PyGccCallgraphEdge * self);
+
+PyObject *
+gcc_CallgraphEdge_str(struct PyGccCallgraphEdge * self);
+
+PyObject *
+gcc_CallgraphNode_repr(struct PyGccCallgraphNode * self);
+
+PyObject *
+gcc_CallgraphNode_str(struct PyGccCallgraphNode * self);
+
+extern PyObject *
+gcc_CallgraphNode_get_callers(struct PyGccCallgraphNode * self);
+
+extern PyObject *
+gcc_CallgraphNode_get_callees(struct PyGccCallgraphNode * self);
+
+/* gcc-python-pass.c: */
extern PyObject *
gcc_python_make_wrapper_pass(struct opt_pass *pass);
+/* gcc-python-location.c: */
PyObject *
gcc_Location_repr(struct PyGccLocation * self);
@@ -38,6 +60,7 @@ gcc_Location_str(struct PyGccLocation * self);
PyObject *
gcc_Location_richcompare(PyObject *o1, PyObject *o2, int op);
+/* gcc-python-cfg.c: */
PyObject *
gcc_BasicBlock_get_preds(PyGccBasicBlock *self, void *closure);
@@ -53,6 +76,7 @@ gcc_BasicBlock_get_phi_nodes(PyGccBasicBlock *self, void *closure);
PyObject *
gcc_Cfg_get_basic_blocks(PyGccCfg *self, void *closure);
+/* gcc-python-tree.c: */
PyObject *
gcc_Tree_str(struct PyGccTree * self);
@@ -86,6 +110,7 @@ gcc_IntegerConstant_get_constant(struct PyGccTree * self, void *closure);
PyObject *
gcc_TypeDecl_get_pointer(struct PyGccTree *self, void *closure);
+/* gcc-python-gimple.c: */
PyObject *
gcc_Gimple_repr(struct PyGccGimple * self);
diff --git a/gcc-python.c b/gcc-python.c
index 850c62d..aebcd5a 100644
--- a/gcc-python.c
+++ b/gcc-python.c
@@ -605,6 +605,42 @@ gcc_python_get_gcc_version(PyObject *self, PyObject *args)
return gcc_version_to_object(actual_gcc_version);
}
+static PyObject *
+gcc_python_get_callgraph_nodes(PyObject *self, PyObject *args)
+{
+ PyObject *result;
+ struct cgraph_node *node;
+
+ /* For debugging, see GCC's dump of things: */
+ if (0) {
+ fprintf(stderr, "----------------BEGIN----------------\n");
+ dump_cgraph (stderr);
+ fprintf(stderr, "---------------- END ----------------\n");
+ }
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ for (node = cgraph_nodes; node; node = node->next) {
+ PyObject *obj_var = gcc_python_make_wrapper_cgraph_node(node);
+ if (!obj_var) {
+ goto error;
+ }
+ if (-1 == PyList_Append(result, obj_var)) {
+ Py_DECREF(obj_var);
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
static PyMethodDef GccMethods[] = {
{"register_callback",
(PyCFunction)gcc_python_register_callback,
@@ -662,6 +698,9 @@ static PyMethodDef GccMethods[] = {
{"get_gcc_version", gcc_python_get_gcc_version, METH_VARARGS,
"Get the gcc.Version for this version of GCC"},
+ {"get_callgraph_nodes", gcc_python_get_callgraph_nodes, METH_VARARGS,
+ "Get a list of all gcc.CallgraphNode instances"},
+
/* Sentinel: */
{NULL, NULL, 0, NULL}
};
@@ -930,6 +969,7 @@ plugin_init (struct plugin_name_args *plugin_info,
PyStructSequence_InitType(&GccVersionType, &gcc_version_desc);
+ autogenerated_callgraph_init_types(); /* FIXME: error checking! */
autogenerated_cfg_init_types(); /* FIXME: error checking! */
autogenerated_function_init_types(); /* FIXME: error checking! */
autogenerated_gimple_init_types(); /* FIXME: error checking! */
@@ -943,6 +983,7 @@ plugin_init (struct plugin_name_args *plugin_info,
+ autogenerated_callgraph_add_types(gcc_python_globals.module);
autogenerated_cfg_add_types(gcc_python_globals.module);
autogenerated_function_add_types(gcc_python_globals.module);
autogenerated_gimple_add_types(gcc_python_globals.module);
diff --git a/gcc-python.h b/gcc-python.h
index 4e88b71..c2d0131 100644
--- a/gcc-python.h
+++ b/gcc-python.h
@@ -61,6 +61,16 @@
\
/* end of macro */
+DECLARE_SIMPLE_WRAPPER(PyGccCallgraphEdge,
+ gcc_CallgraphEdgeType,
+ cgraph_edge,
+ struct cgraph_edge *, edge)
+
+DECLARE_SIMPLE_WRAPPER(PyGccCallgraphNode,
+ gcc_CallgraphNodeType,
+ cgraph_node,
+ struct cgraph_node *, node)
+
DECLARE_SIMPLE_WRAPPER(PyGccPass,
gcc_PassType,
pass,
@@ -115,6 +125,10 @@ DECLARE_SIMPLE_WRAPPER(PyGccVariable,
variable,
struct varpool_node *, var)
+/* autogenerated-callgraph.c */
+int autogenerated_callgraph_init_types(void);
+void autogenerated_callgraph_add_types(PyObject *m);
+
/* autogenerated-cfg.c */
int autogenerated_cfg_init_types(void);
void autogenerated_cfg_add_types(PyObject *m);
diff --git a/gccutils.py b/gccutils.py
index 45924fe..fbed897 100644
--- a/gccutils.py
+++ b/gccutils.py
@@ -553,3 +553,34 @@ class Table(object):
out.write(self._colsep)
out.write(self._sepchar * width)
out.write('\n')
+
+class CallgraphPrettyPrinter(DotPrettyPrinter):
+ def node_id(self, cgn):
+ return 'cgn%i' % id(cgn)
+
+ def node_to_dot_label(self, cgn):
+ return str(cgn.decl.name)
+
+ def edge_to_dot(self, e):
+ attrliststr = ''
+ return (' %s -> %s %s;\n'
+ % (self.node_id(e.caller),
+ self.node_id(e.callee),
+ attrliststr))
+
+ def to_dot(self):
+ result = 'digraph Callgraph {\n'
+ #result += ' subgraph cluster_callgraph {\n'
+ result += ' node [shape=box];\n'
+ for cgn in gcc.get_callgraph_nodes():
+ result += (' %s [label=<%s>];\n'
+ % (self.node_id(cgn), self.node_to_dot_label(cgn)))
+ for edge in cgn.callers:
+ result += self.edge_to_dot(edge)
+ #result += ' }\n'
+ result += '}\n'
+ return result
+
+def callgraph_to_dot():
+ pp = CallgraphPrettyPrinter()
+ return pp.to_dot()
diff --git a/generate-callgraph-c.py b/generate-callgraph-c.py
new file mode 100644
index 0000000..c2beb28
--- /dev/null
+++ b/generate-callgraph-c.py
@@ -0,0 +1,130 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+from cpybuilder import *
+
+cu = CompilationUnit()
+cu.add_include('gcc-python.h')
+cu.add_include('gcc-python-wrappers.h')
+cu.add_include('gcc-plugin.h')
+cu.add_include("cgraph.h")
+
+modinit_preinit = ''
+modinit_postinit = ''
+
+def generate_callgraph_edge():
+ #
+ # Generate the gcc.CallgraphEdge class:
+ #
+ global modinit_preinit
+ global modinit_postinit
+
+ getsettable = PyGetSetDefTable('gcc_CallgraphEdge_getset_table', [],
+ identifier_prefix='gcc_CallgraphEdge',
+ typename='PyGccCallgraphEdge')
+ getsettable.add_simple_getter(cu,
+ 'caller',
+ 'gcc_python_make_wrapper_cgraph_node(self->edge->caller)',
+ 'The function that makes this call, as a gcc.CallgraphNode')
+ getsettable.add_simple_getter(cu,
+ 'callee',
+ 'gcc_python_make_wrapper_cgraph_node(self->edge->callee)',
+ 'The function that is called here, as a gcc.CallgraphNode')
+ getsettable.add_simple_getter(cu,
+ 'call_stmt',
+ 'gcc_python_make_wrapper_gimple(self->edge->call_stmt)',
+ 'The gcc.GimpleCall statememt for the function call')
+ cu.add_defn(getsettable.c_defn())
+
+ pytype = PyTypeObject(identifier = 'gcc_CallgraphEdgeType',
+ localname = 'CallgraphEdge',
+ tp_name = 'gcc.CallgraphEdge',
+ struct_name = 'struct PyGccCallgraphEdge',
+ tp_new = 'PyType_GenericNew',
+ tp_getset = getsettable.identifier,
+ tp_repr = '(reprfunc)gcc_CallgraphEdge_repr',
+ tp_str = '(reprfunc)gcc_CallgraphEdge_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_callgraph_edge()
+
+def generate_callgraph_node():
+ #
+ # Generate the gcc.CallgraphNode class:
+ #
+ global modinit_preinit
+ global modinit_postinit
+
+ getsettable = PyGetSetDefTable('gcc_CallgraphNode_getset_table', [],
+ identifier_prefix='gcc_CallgraphNode',
+ typename='PyGccCallgraphNode')
+ # FIXME: add getters
+ getsettable.add_simple_getter(cu,
+ 'decl',
+ 'gcc_python_make_wrapper_tree(self->node->decl)',
+ 'The gcc.FunctionDecl for this node')
+ getsettable.add_gsdef('callees',
+ 'gcc_CallgraphNode_get_callees',
+ None,
+ 'The function calls made by this function, as a list of gcc.CallgraphEdge')
+ getsettable.add_gsdef('callers',
+ 'gcc_CallgraphNode_get_callers',
+ None,
+ 'The places that call this function, as a list of gcc.CallgraphEdge')
+ cu.add_defn(getsettable.c_defn())
+
+ # see gcc/cgraph.c: dump_cgraph_node (FILE *f, struct cgraph_node *node)
+
+ pytype = PyTypeObject(identifier = 'gcc_CallgraphNodeType',
+ localname = 'CallgraphNode',
+ tp_name = 'gcc.CallgraphNode',
+ struct_name = 'struct PyGccCallgraphNode',
+ tp_new = 'PyType_GenericNew',
+ tp_getset = getsettable.identifier,
+ tp_repr = '(reprfunc)gcc_CallgraphNode_repr',
+ tp_str = '(reprfunc)gcc_CallgraphNode_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_callgraph_node()
+
+cu.add_defn("""
+int autogenerated_callgraph_init_types(void)
+{
+""" + modinit_preinit + """
+ return 1;
+
+error:
+ return 0;
+}
+""")
+
+cu.add_defn("""
+void autogenerated_callgraph_add_types(PyObject *m)
+{
+""" + modinit_postinit + """
+}
+""")
+
+
+
+print(cu.as_str())
diff --git a/generate-tree-c.py b/generate-tree-c.py
index 70fd7d3..dbc6d73 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -445,6 +445,9 @@ def generate_tree_code_classes():
add_simple_getter('result',
'gcc_python_make_wrapper_tree(DECL_RESULT_FLD(self->t))',
'The gcc.ResultDecl for the return value')
+ add_simple_getter('callgraph_node',
+ 'gcc_python_make_wrapper_cgraph_node(cgraph_get_node(self->t))',
+ 'The gcc.CallgraphNode for this function declaration, or None')
if tree_type.SYM == 'SSA_NAME':
# c.f. "struct GTY(()) tree_ssa_name":
diff --git a/tests/plugin/callgraph/input.c b/tests/plugin/callgraph/input.c
new file mode 100644
index 0000000..0794207
--- /dev/null
+++ b/tests/plugin/callgraph/input.c
@@ -0,0 +1,47 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+
+void foo()
+{
+}
+
+void bar(void)
+{
+ foo();
+ foo();
+ foo();
+}
+
+void baz(void)
+{
+ bar();
+ bar();
+ bar();
+}
+
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/plugin/callgraph/script.py b/tests/plugin/callgraph/script.py
new file mode 100644
index 0000000..46a02d9
--- /dev/null
+++ b/tests/plugin/callgraph/script.py
@@ -0,0 +1,48 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+# Verify examining details of functions
+
+import gcc
+from gccutils import pprint
+
+help(gcc.CallgraphNode)
+help(gcc.CallgraphEdge)
+
+from gccutils import callgraph_to_dot, invoke_dot
+
+def on_pass_execution(p, fn):
+ #if p.name == '*warn_function_return':
+ if p.name == '*free_lang_data':
+ if 0:
+ dot = callgraph_to_dot()
+ invoke_dot(dot)
+
+ for cgn in gcc.get_callgraph_nodes():
+ print('cgn:')
+ # print(dir(cgn))
+ print(' cgn.decl: %r' % cgn.decl)
+ print(' cgn.callers: %r' % cgn.callers)
+ print(' cgn.callees: %r' % cgn.callees)
+ for e in cgn.callers:
+ print(e)
+ print('e.caller: %r' % e.caller)
+ print('e.callee: %r' % e.callee)
+ print('e.call_stmt: %r %s' % (e.call_stmt, e.call_stmt))
+
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ on_pass_execution)
diff --git a/tests/plugin/callgraph/stdout.txt b/tests/plugin/callgraph/stdout.txt
new file mode 100644
index 0000000..f9560e3
--- /dev/null
+++ b/tests/plugin/callgraph/stdout.txt
@@ -0,0 +1,94 @@
+Help on class CallgraphNode in module gcc:
+
+class CallgraphNode(__builtin__.object)
+ | Methods defined here:
+ |
+ | __repr__(...)
+ | x.__repr__() <==> repr(x)
+ |
+ | __str__(...)
+ | x.__str__() <==> str(x)
+ |
+ | ----------------------------------------------------------------------
+ | Data descriptors defined here:
+ |
+ | callees
+ | The function calls made by this function, as a list of gcc.CallgraphEdge
+ |
+ | callers
+ | The places that call this function, as a list of gcc.CallgraphEdge
+ |
+ | decl
+ | The gcc.FunctionDecl for this node
+ |
+ | ----------------------------------------------------------------------
+ | Data and other attributes defined here:
+ |
+ | __new__ = <built-in method __new__ of type object>
+ | T.__new__(S, ...) -> a new object with type S, a subtype of T
+
+Help on class CallgraphEdge in module gcc:
+
+class CallgraphEdge(__builtin__.object)
+ | Methods defined here:
+ |
+ | __repr__(...)
+ | x.__repr__() <==> repr(x)
+ |
+ | __str__(...)
+ | x.__str__() <==> str(x)
+ |
+ | ----------------------------------------------------------------------
+ | Data descriptors defined here:
+ |
+ | call_stmt
+ | The gcc.GimpleCall statememt for the function call
+ |
+ | callee
+ | The function that is called here, as a gcc.CallgraphNode
+ |
+ | caller
+ | The function that makes this call, as a gcc.CallgraphNode
+ |
+ | ----------------------------------------------------------------------
+ | Data and other attributes defined here:
+ |
+ | __new__ = <built-in method __new__ of type object>
+ | T.__new__(S, ...) -> a new object with type S, a subtype of T
+
+cgn:
+ cgn.decl: gcc.FunctionDecl('baz')
+ cgn.callers: []
+ cgn.callees: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+cgn:
+ cgn.decl: gcc.FunctionDecl('bar')
+ cgn.callers: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+ cgn.callees: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() bar ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() bar ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() bar ();
+cgn:
+ cgn.decl: gcc.FunctionDecl('foo')
+ cgn.callers: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+ cgn.callees: []
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() foo ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() foo ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() foo ();
12 years, 9 months
[gcc-python-plugin] bump specfile to 0.5
by David Hugh Malcolm
commit beeca608edc1aa51d086976663e92c118cc2f2f2
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 15:29:36 2011 -0400
bump specfile to 0.5
gcc-python-plugin.spec | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
---
diff --git a/gcc-python-plugin.spec b/gcc-python-plugin.spec
index ef0fa93..04efaa6 100644
--- a/gcc-python-plugin.spec
+++ b/gcc-python-plugin.spec
@@ -1,5 +1,5 @@
Name: gcc-python-plugin
-Version: 0.4
+Version: 0.5
Release: 1%{?dist}
Summary: GCC plugin that embeds Python
@@ -326,9 +326,13 @@ CheckPlugin \
%doc COPYING
%doc docs/_build/html
# Example scripts:
-%doc show-ssa.py show-docs.py
+%doc examples
%changelog
+* Wed Jul 27 2011 David Malcolm <dmalcolm(a)redhat.com> - 0.5-1
+- 0.5
+- examples are now in an "examples" subdirectory
+
* Tue Jul 26 2011 David Malcolm <dmalcolm(a)redhat.com> - 0.4-1
- 0.4
- add requirement on pygments
12 years, 10 months
[gcc-python-plugin] Created tag v0.5
by David Hugh Malcolm
The unsigned tag 'v0.5' was created.
Tagger: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 15:07:09 2011 -0400
0.5
Changes since the last tag 'v0.4':
David Malcolm (7):
Update the specfile to 0.4: the selftests now work (for me) on all 4 builds of python, on both i686 and x86_64
Override all locale information with LC_ALL=C when running selftests
Revamp support for options in selftests
Add note about ccache
Improvements to the example scripts
Split up examples within the docs
Fix gcc.Pass.__repr__
12 years, 10 months
[gcc-python-plugin] Fix gcc.Pass.__repr__
by David Hugh Malcolm
commit d11edd582aa849f40551a7bfe06d2f2efc366c3c
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 14:53:16 2011 -0400
Fix gcc.Pass.__repr__
gcc-python-pass.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/gcc-python-pass.c b/gcc-python-pass.c
index 2b4b7ef..454ec72 100644
--- a/gcc-python-pass.c
+++ b/gcc-python-pass.c
@@ -28,7 +28,7 @@
PyObject *
gcc_Pass_repr(struct PyGccPass *self)
{
- return gcc_python_string_from_format("gcc.%s(name='%s')",
+ return gcc_python_string_from_format("%s(name='%s')",
Py_TYPE(self)->tp_name,
self->pass->name);
}
12 years, 10 months
[gcc-python-plugin] Split up examples within the docs
by David Hugh Malcolm
commit 85af0127fa778e0b536e0740bb39d05341fe9492
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 14:52:49 2011 -0400
Split up examples within the docs
docs/examples.rst | 32 -------------------------------
docs/index.rst | 1 +
docs/working-with-c.rst | 48 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 49 insertions(+), 32 deletions(-)
---
diff --git a/docs/examples.rst b/docs/examples.rst
index c31fea5..cb448cd 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -15,38 +15,6 @@
along with this program. If not, see
<http://www.gnu.org/licenses/>.
-Working with C code
-===================
-
-"Hello world"
--------------
-
-Here's a simple "hello world" C program:
-
- .. literalinclude:: ../tests/examples/hello-world/input.c
- :lines: 19-26
- :language: c
-
-Here's a Python script that locates the function at one pass within the
-compile and prints various interesting things about it:
-
- .. literalinclude:: ../tests/examples/hello-world/script.py
- :lines: 19-
- :language: python
-
-We can run the script during the compile like this:
-
- .. code-block:: bash
-
- ./gcc-with-python script.py test.c
-
-Here's the expected output:
-
- .. literalinclude:: ../tests/examples/hello-world/stdout.txt
-
-Notice how the call to `printf` has already been optimized into a call
-to `__builtin_puts`.
-
Example scripts
===============
diff --git a/docs/index.rst b/docs/index.rst
index cd4c4be..06c3636 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -31,6 +31,7 @@ Contents:
basics.rst
gcc-overview.rst
examples.rst
+ working-with-c.rst
cfg.rst
tree.rst
gimple.rst
diff --git a/docs/working-with-c.rst b/docs/working-with-c.rst
new file mode 100644
index 0000000..eb8251e
--- /dev/null
+++ b/docs/working-with-c.rst
@@ -0,0 +1,48 @@
+.. Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+
+Working with C code
+===================
+
+"Hello world"
+-------------
+
+Here's a simple "hello world" C program:
+
+ .. literalinclude:: ../tests/examples/hello-world/input.c
+ :lines: 19-26
+ :language: c
+
+Here's a Python script that locates the function at one pass within the
+compile and prints various interesting things about it:
+
+ .. literalinclude:: ../tests/examples/hello-world/script.py
+ :lines: 19-
+ :language: python
+
+We can run the script during the compile like this:
+
+ .. code-block:: bash
+
+ ./gcc-with-python script.py test.c
+
+Here's the expected output:
+
+ .. literalinclude:: ../tests/examples/hello-world/stdout.txt
+
+Notice how the call to `printf` has already been optimized into a call
+to `__builtin_puts`.
12 years, 10 months
[gcc-python-plugin] Improvements to the example scripts
by David Hugh Malcolm
commit facb66fd92715f106ca1a693426c46418856ab30
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 14:47:58 2011 -0400
Improvements to the example scripts
Move the two example scripts below a new "examples" script, and clean them up
Add:
examples/show-gimple.py
examples/show-passes.py
Document the example scripts
Makefile | 2 +-
docs/basics.rst | 37 +++++++++-
docs/examples.rst | 125 +++++++++++++++++++++++++++++++
docs/gcc-overview.rst | 4 +-
show-docs.py => examples/show-docs.py | 0
show-ssa.py => examples/show-gimple.py | 24 ++----
show-docs.py => examples/show-passes.py | 10 ++-
show-ssa.py => examples/show-ssa.py | 19 +----
8 files changed, 183 insertions(+), 38 deletions(-)
---
diff --git a/Makefile b/Makefile
index 3b85d3a..c378595 100644
--- a/Makefile
+++ b/Makefile
@@ -169,7 +169,7 @@ test-suite: plugin
$(PYTHON) run-test-suite.py
show-ssa: plugin
- ./gcc-with-python show-ssa.py test.c
+ ./gcc-with-python examples/show-ssa.py test.c
html: docs/tables-of-passes.rst docs/passes.svg
cd docs && $(MAKE) html
diff --git a/docs/basics.rst b/docs/basics.rst
index e84fd09..5a2b63e 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -86,7 +86,7 @@ internals of each function in `test.c` (within its SSA representation):
.. code-block:: bash
- ./gcc-with-python show-ssa.py test.c
+ ./gcc-with-python examples/show-ssa.py test.c
Most of the rest of this document describes the Python API visible for
@@ -216,7 +216,7 @@ Currently useful callback events
.. py:data:: gcc.PLUGIN_PASS_EXECUTION
- Called when GCC runs one of its passes on a function
+ Called when GCC is about to run one of its passes.
Arguments passed to the callback are:
@@ -227,6 +227,39 @@ Currently useful callback events
and each can be invoked zero or more times per function (in the code being
compiled)
+ More precisely, some passes have a "gate check": the pass first checks a
+ condition, and only executes if the condition is true.
+
+ Any callback registered with `gcc.PLUGIN_PASS_EXECUTION` will get called
+ if this condition succeeds.
+
+ The actual work of the pass is done after the callbacks return.
+
+ In pseudocode::
+
+ if pass.has_gate_condition:
+ if !pass.test_gate_condition():
+ return
+ invoke_all_callbacks()
+ actually_do_the_pass()
+
+ For passes working on individual functions, all of the above is done
+ per-function.
+
+ To connect to a specific pass, you can simply add a conditional based on the
+ name of the pass::
+
+ def my_callback(ps, fun):
+ if ps.name != '*warn_function_return':
+ # Not the pass we want
+ return
+ # Do something here
+ print(fun.decl.name)
+
+ gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ my_callback)
+
+
.. py:data:: gcc.PLUGIN_PRE_GENERICIZE
Arguments passed to the callback are:
diff --git a/docs/examples.rst b/docs/examples.rst
index eb8251e..c31fea5 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -46,3 +46,128 @@ Here's the expected output:
Notice how the call to `printf` has already been optimized into a call
to `__builtin_puts`.
+
+Example scripts
+===============
+
+There are various sample scripts located in the `examples` subdirectory.
+
+Once you've built the plugin (with `make`), you can run them via:
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/NAME-OF-SCRIPT.py test.c
+
+`show-docs.py`
+--------------
+
+ A trivial script to make it easy to read the builtin documentation for the
+ gcc API:
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/show-docs.py test.c
+
+ with this source:
+
+ .. literalinclude:: ../examples/show-docs.py
+ :lines: 17-
+ :language: python
+
+ giving output::
+
+ Help on built-in module gcc:
+
+ NAME
+ gcc
+
+ FILE
+ (built-in)
+
+ CLASSES
+ __builtin__.object
+ BasicBlock
+ Cfg
+ Edge
+ Function
+ Gimple
+ (truncated)
+
+`show-passes.py`
+----------------
+
+You can see the passes being executed via:
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/show-passes.py test.c
+
+This is a simple script that registers a trivial callback:
+
+ .. literalinclude:: ../examples/show-passes.py
+ :lines: 17-
+ :language: python
+
+Sample output, showing passes being called on two different functions (`main`
+and `helper_function`)::
+
+ (gcc.GimplePass(name='*warn_unused_result'), gcc.Function('main'))
+ (gcc.GimplePass(name='omplower'), gcc.Function('main'))
+ (gcc.GimplePass(name='lower'), gcc.Function('main'))
+ (gcc.GimplePass(name='eh'), gcc.Function('main'))
+ (gcc.GimplePass(name='cfg'), gcc.Function('main'))
+ (gcc.GimplePass(name='*warn_function_return'), gcc.Function('main'))
+ (gcc.GimplePass(name='*build_cgraph_edges'), gcc.Function('main'))
+ (gcc.GimplePass(name='*warn_unused_result'), gcc.Function('helper_function'))
+ (gcc.GimplePass(name='omplower'), gcc.Function('helper_function'))
+ (gcc.GimplePass(name='lower'), gcc.Function('helper_function'))
+ (gcc.GimplePass(name='eh'), gcc.Function('helper_function'))
+ (gcc.GimplePass(name='cfg'), gcc.Function('helper_function'))
+ [...truncated...]
+
+
+`show-gimple.py`
+----------------
+
+A simple script for viewing each function in the source file after it's been
+converted to "GIMPLE" form, using GraphViz to visualize the control flow graph:
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/show-gimple.py test.c
+
+It will generate a file `test.png` for each function, and opens it in an image
+viewer.
+
+ .. figure:: sample-gimple-cfg.png
+ :scale: 50 %
+ :alt: image of a control flow graph in GIMPLE form
+
+The Python code for this is:
+
+ .. literalinclude:: ../examples/show-gimple.py
+ :lines: 19-
+ :language: python
+
+`show-ssa.py`
+-------------
+
+This is similar to `show-gimple.py`, but shows each function after the GIMPLE
+has been converted to Static Single Assignment form ("SSA"):
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/show-ssa.py test.c
+
+As before, it generates an image file for each function and opens it in a
+viewer.
+
+.. figure:: sample-gimple-ssa-cfg.png
+ :scale: 50 %
+ :alt: image of a control flow graph in GIMPLE SSA form
+
+The Python code for this is:
+
+ .. literalinclude:: ../examples/show-ssa.py
+ :lines: 17-
+ :language: python
diff --git a/docs/gcc-overview.rst b/docs/gcc-overview.rst
index 84a361a..014d3ae 100644
--- a/docs/gcc-overview.rst
+++ b/docs/gcc-overview.rst
@@ -99,7 +99,7 @@ It's far easier to see the GIMPLE using:
.. code-block:: bash
- ./gcc-with-python show-gimple.py test.c
+ ./gcc-with-python examples/show-gimple.py test.c
which generates bitmaps showing the "control flow graph" of the functions in
the file, with source on the left-hand side, and GIMPLE on the right-hand side:
@@ -153,7 +153,7 @@ Here's our example function, after conversion to GIMPLE SSA:
.. code-block:: bash
- ./gcc-with-python show-ssa.py test.c
+ ./gcc-with-python examples/show-ssa.py test.c
.. figure:: sample-gimple-ssa-cfg.png
:scale: 50 %
diff --git a/show-docs.py b/examples/show-docs.py
similarity index 100%
copy from show-docs.py
copy to examples/show-docs.py
diff --git a/show-ssa.py b/examples/show-gimple.py
similarity index 58%
copy from show-ssa.py
copy to examples/show-gimple.py
index 7ede514..c4a6a9d 100644
--- a/show-ssa.py
+++ b/examples/show-gimple.py
@@ -15,29 +15,19 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
-# Sample python script, to be run by our gcc plugin (see "make test")
-# Show the SSA form of each function, using GraphViz
+# Sample python script, to be run by our gcc plugin
+# Show the GIMPLE form of each function, using GraphViz
import gcc
from gccutils import get_src_for_loc, cfg_to_dot, invoke_dot
def my_pass_execution_callback(*args, **kwargs):
- #print('my_pass_execution_callback was called: args=%r kwargs=%r' % (args, kwargs))
- #help(args[0])
(optpass, fun) = args
- if not optpass.properties_required & (1<<5):
- return
- if fun:
- print('fun.cfg: %r' % fun.cfg)
- if fun.cfg:
- #print help(fun.cfg)
- print('fun.cfg.basic_blocks: %r' % fun.cfg.basic_blocks)
- print('fun.cfg.entry: %r' % fun.cfg.entry)
- print('fun.cfg.exit: %r' % fun.cfg.exit)
- print('fun.cfg.entry.succs: %r' % fun.cfg.entry.succs)
- print('fun.cfg.exit.preds: %r' % fun.cfg.exit.preds)
-
+ if optpass.name == '*warn_function_return':
+ # (the CFG should be set up by this point, and the GIMPLE is not yet
+ # in SSA form)
+ if fun and fun.cfg:
dot = cfg_to_dot(fun.cfg, fun.decl.name)
- # print(dot)
+ # print dot
invoke_dot(dot)
gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
diff --git a/show-docs.py b/examples/show-passes.py
similarity index 72%
rename from show-docs.py
rename to examples/show-passes.py
index 29897cd..e083bde 100644
--- a/show-docs.py
+++ b/examples/show-passes.py
@@ -15,5 +15,13 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
+# Sample python script, to be run by our gcc plugin
+# Show all the passes that get executed
+
import gcc
-help(gcc)
+def my_pass_execution_callback(*args, **kwargs):
+ (optpass, fun) = args
+ print(args)
+
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ my_pass_execution_callback)
diff --git a/show-ssa.py b/examples/show-ssa.py
similarity index 63%
rename from show-ssa.py
rename to examples/show-ssa.py
index 7ede514..c289412 100644
--- a/show-ssa.py
+++ b/examples/show-ssa.py
@@ -15,27 +15,16 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
-# Sample python script, to be run by our gcc plugin (see "make test")
+# Sample python script, to be run by our gcc plugin
# Show the SSA form of each function, using GraphViz
import gcc
from gccutils import get_src_for_loc, cfg_to_dot, invoke_dot
def my_pass_execution_callback(*args, **kwargs):
- #print('my_pass_execution_callback was called: args=%r kwargs=%r' % (args, kwargs))
- #help(args[0])
(optpass, fun) = args
- if not optpass.properties_required & (1<<5):
- return
- if fun:
- print('fun.cfg: %r' % fun.cfg)
- if fun.cfg:
- #print help(fun.cfg)
- print('fun.cfg.basic_blocks: %r' % fun.cfg.basic_blocks)
- print('fun.cfg.entry: %r' % fun.cfg.entry)
- print('fun.cfg.exit: %r' % fun.cfg.exit)
- print('fun.cfg.entry.succs: %r' % fun.cfg.entry.succs)
- print('fun.cfg.exit.preds: %r' % fun.cfg.exit.preds)
-
+ if optpass.name == 'veclower':
+ # (the SSA form of each function should have just been set up)
+ if fun and fun.cfg:
dot = cfg_to_dot(fun.cfg, fun.decl.name)
# print(dot)
invoke_dot(dot)
12 years, 10 months