I'm the maintainer of a new feature for the (not-yet-released) GCC 5: libgccjit: a way to build gcc as a shared library, suitable for generating code in-process. See: https://gcc.gnu.org/wiki/JIT
I've been experimenting with embedding it within PyPy - my thought was that gcc has great breadth of hardware support, so maybe PyPy could use libgccjit as a fallback backend for targets which don't yet have their own pypy jit backends. I'm attaching the work I've got so far, in patch form; I apologize for the rough work-in-progress nature of the patch. It has: * a toy example of calling libgccjit from cffi, to build and run code in process (see rpython/jit/backend/libgccjit/cffi_bindings.py). * doing the same from rffi (see rpython/jit/backend/libgccjit/rffi_bindings.py and rpython/jit/backend/libgccjit/test/test_rffi_bindings.py) These seem to work: the translator builds binaries that call into my library, which builds machine code "on the fly". Is there a way to do this without going through the translation step? * the beginnings of a JIT backend: I hack up rpython/jit/backend/detect_cpu.py to always use: rpython/jit/backend/libgccjit/runner.py and this merely raises an exception, albeit dumping the operations seen in loops. My thinking is that I ought to be able to use the rffi bindings of libgccjit to implement the backend, and somehow turn the operations I'm seeing into calls into my libgccjit API. Does this sound useful, and am I on the right track here? Is there documentation about the meaning of the various kinds of operations within a to-be-JITted-loop? Thanks Dave
diff --git a/rpython/jit/backend/detect_cpu.py b/rpython/jit/backend/detect_cpu.py --- a/rpython/jit/backend/detect_cpu.py +++ b/rpython/jit/backend/detect_cpu.py @@ -99,6 +99,9 @@ def getcpuclassname(backend_name="auto"): + # FIXME: + return 'rpython.jit.backend.libgccjit.runner', 'CPU' + if backend_name == "auto": backend_name = autodetect() backend_name = backend_name.replace('_', '-') diff --git a/rpython/jit/backend/libgccjit/cffi_bindings.py b/rpython/jit/backend/libgccjit/cffi_bindings.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/libgccjit/cffi_bindings.py @@ -0,0 +1,149 @@ +import os +import re +from collections import namedtuple + +# Hacks: +INSTALL_PATH = '/home/david/coding-3/gcc-git-jit-pypy/install' +INCLUDE_DIR = os.path.join(INSTALL_PATH, 'include') +LIB_DIR = os.path.join(INSTALL_PATH, 'lib') +BIN_DIR = os.path.join(INSTALL_PATH, 'bin') + +def append_to_envvar_path(envvar, path): + if envvar in os.environ: + os.environ[envvar] = path + ':' + os.environ[envvar] + else: + os.environ[envvar] = path + print('%s=%s' % (envvar, os.environ[envvar])) + +# It appears that we need to override os.environ['LD_LIBRARY_PATH'] +# before importing cffi for it to take account of this. +append_to_envvar_path('LD_LIBRARY_PATH', LIB_DIR) +# actually, for some reason I get: +# File "/usr/lib64/python2.7/site-packages/cffi/vengine_cpy.py", line 124, in load_library +# raise ffiplatform.VerificationError(error) +# cffi.ffiplatform.VerificationError: importing '/home/david/coding-3/pypy-libgccjit/rpython/jit/backend/libgccjit/__pycache__/_cffi__x5c2f8978xf4274cdc.so': libgccjit.so.0: cannot open shared object file: No such file or directory +# if LD_LIBRARY_PATH isn't set up before python starts up; issue with imp.load_dynamic ? + +# The library requires the correct driver to be in the PATH: +append_to_envvar_path('PATH', BIN_DIR) + +os.system('env') + +import cffi + +ffi = cffi.FFI() + +with open(os.path.join(INCLUDE_DIR, 'libgccjit.h')) as f: + libgccjit_h_content = f.read() + +def toy_preprocessor(content): + """ + ffi.cdef can't handle preprocessor directives. + We only have the idempotency guards and ifdef __cplusplus; + strip them out. + """ + State = namedtuple('State', ('line', 'accepting_text')) + macros = {} + result = [] # list of lines + states = [State('default', accepting_text=True)] + for line in content.splitlines(): + if 0: + print(repr(line)) + + m = re.match('#ifndef\s+(\S+)', line) + if m: + states.append(State(line, + accepting_text=(m.group(1) not in macros)) ) + continue + m = re.match('#ifdef\s+(\S+)', line) + + if m: + states.append(State(line, + accepting_text=(m.group(1) in macros)) ) + continue + + m = re.match('#define\s+(\S+)', line) + if m: + macros[m.group(1)] = '' + continue + + m = re.match('#endif\s*', line) + if m: + states.pop() + continue + + if states[-1].accepting_text: + result.append(line) + + return '\n'.join(result) + +libgccjit_h_content = toy_preprocessor(libgccjit_h_content) + +# print(libgccjit_h_content) + +ffi.cdef(libgccjit_h_content) + +lib = ffi.verify('#include "libgccjit.h"', + libraries=['gccjit'], + library_dirs=[LIB_DIR], + include_dirs=[INCLUDE_DIR]) + +ctxt = lib.gcc_jit_context_acquire() +print ctxt + +lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + 1) +lib.gcc_jit_context_set_int_option(ctxt, + lib.GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + 3) +lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, + 1) +lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, + 1) +lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, + 1) + +int_type = lib.gcc_jit_context_get_type(ctxt, lib.GCC_JIT_TYPE_INT) +param = lib.gcc_jit_context_new_param(ctxt, ffi.NULL, int_type, "input") +fn = lib.gcc_jit_context_new_function(ctxt, + ffi.NULL, + lib.GCC_JIT_FUNCTION_EXPORTED, + int_type, + "add_one_to", + 1, [param], 0) +v_res = lib.gcc_jit_function_new_local(fn, ffi.NULL, int_type, "v_res") + +b_initial = lib.gcc_jit_function_new_block(fn, "initial") + +c_one = lib.gcc_jit_context_new_rvalue_from_int(ctxt, int_type, 1) + +op_add = lib.gcc_jit_context_new_binary_op(ctxt, ffi.NULL, + lib.GCC_JIT_BINARY_OP_PLUS, + int_type, + lib.gcc_jit_param_as_rvalue(param), + c_one) +lib.gcc_jit_block_add_assignment(b_initial, ffi.NULL, + v_res, + op_add) + +lib.gcc_jit_block_end_with_return(b_initial, ffi.NULL, + lib.gcc_jit_lvalue_as_rvalue(v_res)) + +jit_result = lib.gcc_jit_context_compile(ctxt) + +lib.gcc_jit_context_release(ctxt) + +fn_ptr = lib.gcc_jit_result_get_code(jit_result, "add_one_to") +if not fn_ptr: + raise Exception("fn_ptr is NULL") +print('fn_ptr: %r' % fn_ptr) + +fn_result = ffi.cast("int(*)(int)", fn_ptr)(41) +print('fn_result: %r' % fn_result) +assert fn_result == 42 + +lib.gcc_jit_result_release(jit_result) diff --git a/rpython/jit/backend/libgccjit/rffi_bindings.py b/rpython/jit/backend/libgccjit/rffi_bindings.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/libgccjit/rffi_bindings.py @@ -0,0 +1,249 @@ +import os + +# Hacks: +INSTALL_PATH = '/home/david/coding-3/gcc-git-jit-pypy/install' +INCLUDE_DIR = os.path.join(INSTALL_PATH, 'include') +LIB_DIR = os.path.join(INSTALL_PATH, 'lib') +BIN_DIR = os.path.join(INSTALL_PATH, 'bin') + +def append_to_envvar_path(envvar, path): + if envvar in os.environ: + os.environ[envvar] = path + ':' + os.environ[envvar] + else: + os.environ[envvar] = path + print('%s=%s' % (envvar, os.environ[envvar])) + +# It appears that we need to override os.environ['LD_LIBRARY_PATH'] +# before importing cffi for it to take account of this. +append_to_envvar_path('LD_LIBRARY_PATH', LIB_DIR) +# actually, for some reason I get: +# File "/usr/lib64/python2.7/site-packages/cffi/vengine_cpy.py", line 124, in load_library +# raise ffiplatform.VerificationError(error) +# cffi.ffiplatform.VerificationError: importing '/home/david/coding-3/pypy-libgccjit/rpython/jit/backend/libgccjit/__pycache__/_cffi__x5c2f8978xf4274cdc.so': libgccjit.so.0: cannot open shared object file: No such file or directory +# if LD_LIBRARY_PATH isn't set up before python starts up; issue with imp.load_dynamic ? + +# The library requires the correct driver to be in the PATH: +append_to_envvar_path('PATH', BIN_DIR) + +from rpython.rtyper.lltypesystem import rffi +from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rtyper.lltypesystem.rffi import * +from rpython.rtyper.lltypesystem import lltype + +def make_eci(): + eci = ExternalCompilationInfo(includes=['libgccjit.h'], + include_dirs=[INCLUDE_DIR], + libraries=['gccjit'], + library_dirs=[LIB_DIR]) + return eci + +class Library: + def __init__(self, eci): + self.eci = eci + + # Opaque types: + self.GCC_JIT_CONTEXT_P = lltype.Ptr(COpaque(name='gcc_jit_context', + compilation_info=eci)) + self.GCC_JIT_RESULT_P = lltype.Ptr(COpaque(name='gcc_jit_result', + compilation_info=eci)) + self.GCC_JIT_TYPE_P = lltype.Ptr(COpaque(name='gcc_jit_type', + compilation_info=eci)) + self.GCC_JIT_LOCATION_P = lltype.Ptr(COpaque(name='gcc_jit_location', + compilation_info=eci)) + self.GCC_JIT_PARAM_P = lltype.Ptr(COpaque(name='gcc_jit_param', + compilation_info=eci)) + self.GCC_JIT_LVALUE_P = lltype.Ptr(COpaque(name='gcc_jit_lvalue', + compilation_info=eci)) + self.GCC_JIT_RVALUE_P = lltype.Ptr(COpaque(name='gcc_jit_rvalue', + compilation_info=eci)) + self.GCC_JIT_FUNCTION_P = lltype.Ptr(COpaque(name='gcc_jit_function', + compilation_info=eci)) + self.GCC_JIT_BLOCK_P = lltype.Ptr(COpaque(name='gcc_jit_block', + compilation_info=eci)) + + self.PARAM_P_P = lltype.Ptr(lltype.Array(self.GCC_JIT_PARAM_P, + hints={'nolength': True})) + + # Entrypoints: + for returntype, name, paramtypes in [ + (self.GCC_JIT_CONTEXT_P, + 'gcc_jit_context_acquire', []), + + (lltype.Void, + 'gcc_jit_context_release', [self.GCC_JIT_CONTEXT_P]), + + (lltype.Void, + 'gcc_jit_context_set_int_option', [self.GCC_JIT_CONTEXT_P, + INT, # FIXME: enum gcc_jit_int_option opt, + INT]), + (lltype.Void, + 'gcc_jit_context_set_bool_option', [self.GCC_JIT_CONTEXT_P, + INT, # FIXME: enum gcc_jit_bool_option opt, + INT]), + + (self.GCC_JIT_RESULT_P, + 'gcc_jit_context_compile', [self.GCC_JIT_CONTEXT_P]), + + + (VOIDP, + 'gcc_jit_result_get_code', [self.GCC_JIT_RESULT_P, + CCHARP]), + + (lltype.Void, + 'gcc_jit_result_release', [self.GCC_JIT_RESULT_P]), + + ############################################################ + # Types + ############################################################ + (self.GCC_JIT_TYPE_P, + 'gcc_jit_context_get_type', [self.GCC_JIT_CONTEXT_P, + INT]), + + ############################################################ + # Constructing functions. + ############################################################ + (self.GCC_JIT_PARAM_P, + 'gcc_jit_context_new_param', [self.GCC_JIT_CONTEXT_P, + self.GCC_JIT_LOCATION_P, + self.GCC_JIT_TYPE_P, + CCHARP]), + (self.GCC_JIT_LVALUE_P, + 'gcc_jit_param_as_lvalue', [self.GCC_JIT_PARAM_P]), + (self.GCC_JIT_RVALUE_P, + 'gcc_jit_param_as_rvalue', [self.GCC_JIT_PARAM_P]), + + (self.GCC_JIT_FUNCTION_P, + 'gcc_jit_context_new_function', [self.GCC_JIT_CONTEXT_P, + self.GCC_JIT_LOCATION_P, + INT, # enum gcc_jit_function_kind kind, + self.GCC_JIT_TYPE_P, + CCHARP, + INT, + self.PARAM_P_P, + INT]), + (self.GCC_JIT_LVALUE_P, + 'gcc_jit_function_new_local', [self.GCC_JIT_FUNCTION_P, + self.GCC_JIT_LOCATION_P, + self.GCC_JIT_TYPE_P, + CCHARP]), + + (self.GCC_JIT_BLOCK_P, + 'gcc_jit_function_new_block', [self.GCC_JIT_FUNCTION_P, + CCHARP]), + + ############################################################ + # lvalues, rvalues and expressions. + ############################################################ + (self.GCC_JIT_RVALUE_P, + 'gcc_jit_lvalue_as_rvalue', [self.GCC_JIT_LVALUE_P]), + + # Integer constants. + (self.GCC_JIT_RVALUE_P, + 'gcc_jit_context_new_rvalue_from_int', [self.GCC_JIT_CONTEXT_P, + self.GCC_JIT_TYPE_P, + INT]), + (self.GCC_JIT_RVALUE_P, + 'gcc_jit_context_zero', [self.GCC_JIT_CONTEXT_P, + self.GCC_JIT_TYPE_P]), + (self.GCC_JIT_RVALUE_P, + 'gcc_jit_context_one', [self.GCC_JIT_CONTEXT_P, + self.GCC_JIT_TYPE_P]), + + (self.GCC_JIT_RVALUE_P, + 'gcc_jit_context_new_binary_op', [self.GCC_JIT_CONTEXT_P, + self.GCC_JIT_LOCATION_P, + INT, # enum gcc_jit_binary_op op, + self.GCC_JIT_TYPE_P, + self.GCC_JIT_RVALUE_P, + self.GCC_JIT_RVALUE_P]), + + ############################################################ + # Statement-creation. + ############################################################ + (lltype.Void, + 'gcc_jit_block_add_assignment', [self.GCC_JIT_BLOCK_P, + self.GCC_JIT_LOCATION_P, + self.GCC_JIT_LVALUE_P, + self.GCC_JIT_RVALUE_P]), + (lltype.Void, + 'gcc_jit_block_end_with_return', [self.GCC_JIT_BLOCK_P, + self.GCC_JIT_LOCATION_P, + self.GCC_JIT_RVALUE_P]), + ]: + self.add_entrypoint(returntype, name, paramtypes) + + # Enum values: + self.make_enum_values("""GCC_JIT_STR_OPTION_PROGNAME""") + + self.make_enum_values("""GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL""") + + self.make_enum_values("""GCC_JIT_BOOL_OPTION_DEBUGINFO, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, + GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, + GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, + GCC_JIT_BOOL_OPTION_SELFCHECK_GC, + GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, + """) + + self.make_enum_values("""GCC_JIT_TYPE_VOID, + GCC_JIT_TYPE_VOID_PTR, + GCC_JIT_TYPE_BOOL, + GCC_JIT_TYPE_CHAR, + GCC_JIT_TYPE_SIGNED_CHAR, + GCC_JIT_TYPE_UNSIGNED_CHAR, + GCC_JIT_TYPE_SHORT, + GCC_JIT_TYPE_UNSIGNED_SHORT, + GCC_JIT_TYPE_INT, + GCC_JIT_TYPE_UNSIGNED_INT, + GCC_JIT_TYPE_LONG, + GCC_JIT_TYPE_UNSIGNED_LONG, + GCC_JIT_TYPE_LONG_LONG, + GCC_JIT_TYPE_UNSIGNED_LONG_LONG, + GCC_JIT_TYPE_FLOAT, + GCC_JIT_TYPE_DOUBLE, + GCC_JIT_TYPE_LONG_DOUBLE, + GCC_JIT_TYPE_CONST_CHAR_PTR, + GCC_JIT_TYPE_SIZE_T, + GCC_JIT_TYPE_FILE_PTR, + GCC_JIT_TYPE_COMPLEX_FLOAT, + GCC_JIT_TYPE_COMPLEX_DOUBLE, + GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE""") + + self.make_enum_values("""GCC_JIT_FUNCTION_EXPORTED, + GCC_JIT_FUNCTION_INTERNAL, + GCC_JIT_FUNCTION_IMPORTED, + GCC_JIT_FUNCTION_ALWAYS_INLINE""") + + self.make_enum_values( + """ + GCC_JIT_BINARY_OP_PLUS, + GCC_JIT_BINARY_OP_MINUS, + GCC_JIT_BINARY_OP_MULT, + GCC_JIT_BINARY_OP_DIVIDE, + GCC_JIT_BINARY_OP_MODULO, + GCC_JIT_BINARY_OP_BITWISE_AND, + GCC_JIT_BINARY_OP_BITWISE_XOR, + GCC_JIT_BINARY_OP_BITWISE_OR, + GCC_JIT_BINARY_OP_LOGICAL_AND, + GCC_JIT_BINARY_OP_LOGICAL_OR, + GCC_JIT_BINARY_OP_LSHIFT, + GCC_JIT_BINARY_OP_RSHIFT + """) + + self.null_location_ptr = lltype.nullptr(self.GCC_JIT_LOCATION_P.TO) + + + def add_entrypoint(self, returntype, name, paramtypes): + setattr(self, name, + llexternal(name, paramtypes, returntype, + compilation_info=self.eci)) + + def make_enum_values(self, lines): + for value, name in enumerate(lines.split(',')): + name = name.strip() + if name: + setattr(self, name, value) + + diff --git a/rpython/jit/backend/libgccjit/runner.py b/rpython/jit/backend/libgccjit/runner.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/libgccjit/runner.py @@ -0,0 +1,32 @@ +#from rpython.jit.backend import model +from rpython.jit.backend.libgccjit.assembler import AssemblerLibgccjit +from rpython.jit.backend.llsupport.llmodel import AbstractLLCPU + +#class CPU(model.AbstractCPU): +class CPU(AbstractLLCPU): + def __init__(self, rtyper, stats, opts=None, translate_support_code=False, + gcdescr=None): + AbstractLLCPU.__init__(self, rtyper, stats, opts, + translate_support_code, gcdescr) + + def setup(self): + self.assembler = AssemblerLibgccjit(self) + + def compile_loop(self, inputargs, operations, looptoken, + log=True, name='', logger=None): + import sys + sys.stderr.write('compile_loop:\n') + for i, arg in enumerate(inputargs): + sys.stderr.write(' arg[%i] = %r\n' % (i, arg)) + sys.stderr.write(' type(arg[%i]) = %r\n' % (i, type(arg))) + for i, op in enumerate(operations): + sys.stderr.write(' op[%i] = %r\n' % (i, op)) + sys.stderr.write(' type(op[%i]) = %r\n' % (i, type(op))) + sys.stderr.write(' looptoken: %r\n' % (looptoken, )) + sys.stderr.write(' log: %r\n' % (log, )) + sys.stderr.write(' name: %r\n' % (name, )) + sys.stderr.write(' logger: %r\n' % (logger, )) + sys.stderr.write('compile_loop: %r\n' % locals ()) + #raise NotImplementedError + return self.assembler.assemble_loop(inputargs, operations, looptoken, log, + name, logger) diff --git a/rpython/jit/backend/libgccjit/test/__init__.py b/rpython/jit/backend/libgccjit/test/__init__.py new file mode 100644 diff --git a/rpython/jit/backend/libgccjit/test/test_basic.py b/rpython/jit/backend/libgccjit/test/test_basic.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/libgccjit/test/test_basic.py @@ -0,0 +1,15 @@ +import py +from rpython.jit.backend.detect_cpu import getcpuclass +from rpython.jit.metainterp.test import support, test_ajit + +class JitLibgccjitMixin(support.LLJitMixin): + type_system = 'lltype' + CPUClass = getcpuclass() + + def check_jumps(self, maxcount): + pass + +class TestBasic(JitLibgccjitMixin, test_ajit.BaseLLtypeTests): + # for the individual tests see + # ====> ../../../metainterp/test/test_basic.py + pass diff --git a/rpython/jit/backend/libgccjit/test/test_rffi_bindings.py b/rpython/jit/backend/libgccjit/test/test_rffi_bindings.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/libgccjit/test/test_rffi_bindings.py @@ -0,0 +1,160 @@ + +import py +import sys +from rpython.rtyper.lltypesystem.rffi import * +from rpython.rtyper.lltypesystem.rffi import _keeper_for_type # crap +from rpython.rlib.rposix import get_errno, set_errno +from rpython.translator.c.test.test_genc import compile as compile_c +from rpython.rtyper.lltypesystem.lltype import Signed, Ptr, Char, malloc +from rpython.rtyper.lltypesystem import lltype +from rpython.translator import cdir +from rpython.tool.udir import udir +from rpython.rtyper.test.test_llinterp import interpret +from rpython.annotator.annrpython import RPythonAnnotator +from rpython.rtyper.rtyper import RPythonTyper +from rpython.translator.backendopt.all import backend_optimizations +from rpython.translator.translator import graphof +from rpython.conftest import option +from rpython.flowspace.model import summary +from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rlib.rarithmetic import r_singlefloat + +""" +def test_string(): + eci = ExternalCompilationInfo(includes=['string.h']) + z = llexternal('strlen', [CCHARP], Signed, compilation_info=eci) + + def f(): + s = str2charp("xxx") + res = z(s) + free_charp(s) + return res + + xf = compile_c(f, [], backendopt=False) + assert xf() == 3 +""" + +from rpython.jit.backend.libgccjit.rffi_bindings import make_eci, Library + +def test_compile_empty_context(): + eci = make_eci() + + lib = Library(eci) + + def f(): + ctxt = lib.gcc_jit_context_acquire() + result = lib.gcc_jit_context_compile(ctxt) + lib.gcc_jit_context_release(ctxt) + lib.gcc_jit_result_release(result) + + f1 = compile_c(f, [], backendopt=False) + f1 () + #assert False # to see stderr + +def make_param_array(lib, l): + array = lltype.malloc(lib.PARAM_P_P.TO, + len(l), + flavor='raw') # of maybe gc? + for i in range(len(l)): + array[i] = l[i] + return array + # FIXME: don't leak! + +def test_compile_add_one_to(): + eci = make_eci() + + lib = Library(eci) + + ft = lltype.FuncType([INT], INT)#, abi="C") + ftp = lltype.Ptr(ft) + + def f(): + ctxt = lib.gcc_jit_context_acquire() + + lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + 1) + lib.gcc_jit_context_set_int_option(ctxt, + lib.GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + 3) + lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, + 1) + lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, + 1) + lib.gcc_jit_context_set_bool_option(ctxt, + lib.GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, + 1) + t_int = lib.gcc_jit_context_get_type(ctxt, lib.GCC_JIT_TYPE_INT) + param = lib.gcc_jit_context_new_param(ctxt, + lib.null_location_ptr, + t_int, + "input") + # FIXME: how to build an array of params at this level? + # see liststr2charpp in rffi.py + + param_array = make_param_array(lib, [param]) + fn = lib.gcc_jit_context_new_function(ctxt, + lib.null_location_ptr, + lib.GCC_JIT_FUNCTION_EXPORTED, + t_int, + "add_one_to", + 1, param_array, 0) + lltype.free(param_array, flavor='raw') + + v_res = lib.gcc_jit_function_new_local(fn, + lib.null_location_ptr, + t_int, + "v_res") + b_initial = lib.gcc_jit_function_new_block(fn, "initial") + c_one = lib.gcc_jit_context_new_rvalue_from_int(ctxt, t_int, 1) + op_add = lib.gcc_jit_context_new_binary_op(ctxt, + lib.null_location_ptr, + lib.GCC_JIT_BINARY_OP_PLUS, + t_int, + lib.gcc_jit_param_as_rvalue(param), + c_one) + lib.gcc_jit_block_add_assignment(b_initial, lib.null_location_ptr, + v_res, + op_add) + lib.gcc_jit_block_end_with_return(b_initial, lib.null_location_ptr, + lib.gcc_jit_lvalue_as_rvalue(v_res)) + + jit_result = lib.gcc_jit_context_compile(ctxt) + lib.gcc_jit_context_release(ctxt) + if not jit_result: + # FIXME: get error from context + raise Exception("jit_result is NULL") + + fn_ptr = lib.gcc_jit_result_get_code(jit_result, "add_one_to") + if not fn_ptr: + raise Exception("fn_ptr is NULL") + print('fn_ptr: %s' % fn_ptr) + + #ft = lltype.FuncType([INT], INT)#, abi="C") + # looks like we can't build a FuncType inside RPython + # but we can use one built outside: + print(ft) + print(ftp) + + typed_fn_ptr = cast(ftp, fn_ptr) + print(typed_fn_ptr) + fn_result = typed_fn_ptr (r_int(41)) + #print('fn_result: %d' % fn_result) + #assert fn_result == r_int(42) + + # and it looks like we can't create a functionptr from this + # FuncType: + #funcptr = lltype.functionptr(ft) + + lib.gcc_jit_result_release(jit_result) + + return int(fn_result) + + f1 = compile_c(f, [], backendopt=False) + assert f1() == 42 + #assert False # to see stderr + +# TODO: test of an error +# should turn it into an exception, and capture the error
_______________________________________________ pypy-dev mailing list pypy-dev@python.org https://mail.python.org/mailman/listinfo/pypy-dev