def test_register(self) -> None: op = LoadInt(5) self.block.ops.append(op) fn = FuncIR( FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)), [self.reg], [self.block]) value_names = generate_names_for_ir(fn.arg_regs, fn.blocks) emitter = Emitter(EmitterContext(NameGenerator([['mod']])), value_names) generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments assert_string_arrays_equal([ 'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', ' CPyTagged cpy_r_i0;\n', 'CPyL0: ;\n', ' cpy_r_i0 = 10;\n', '}\n', ], result, msg='Generated code invalid')
def test_simple(self) -> None: self.block.ops.append(Return(self.reg)) fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments assert_string_arrays_equal( [ 'CPyTagged CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', 'CPyL0: ;\n', ' return cpy_r_arg;\n', '}\n', ], result, msg='Generated code invalid')
def test_register(self) -> None: self.env.temp_index = 0 op = LoadInt(5) self.block.ops.append(op) self.env.add_op(op) fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) generate_native_function(fn, emitter, 'prog.py', 'prog', False) result = emitter.fragments assert_string_arrays_equal( [ 'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', ' CPyTagged cpy_r_i0;\n', 'CPyL0: ;\n', ' cpy_r_i0 = 10;\n', '}\n', ], result, msg='Generated code invalid')
def generate_c_for_modules(self) -> List[Tuple[str, str]]: file_contents = [] multi_file = self.use_shared_lib and self.multi_file base_emitter = Emitter(self.context) # Optionally just include the runtime library c files to # reduce the number of compiler invocations needed if self.compiler_options.include_runtime_files: base_emitter.emit_line('#include "CPy.c"') base_emitter.emit_line('#include "getargs.c"') base_emitter.emit_line('#include "__native{}.h"'.format( self.short_group_suffix)) base_emitter.emit_line('#include "__native_internal{}.h"'.format( self.short_group_suffix)) emitter = base_emitter for (_, literal), identifier in self.literals.items(): if isinstance(literal, int): symbol = emitter.static_name(identifier, None) self.declare_global('CPyTagged ', symbol) else: self.declare_static_pyobject(identifier, emitter) for module_name, module in self.modules: if multi_file: emitter = Emitter(self.context) emitter.emit_line('#include "__native{}.h"'.format( self.short_group_suffix)) emitter.emit_line('#include "__native_internal{}.h"'.format( self.short_group_suffix)) self.declare_module(module_name, emitter) self.declare_internal_globals(module_name, emitter) self.declare_imports(module.imports, emitter) for cl in module.classes: if cl.is_ext_class: generate_class(cl, module_name, emitter) # Generate Python extension module definitions and module initialization functions. self.generate_module_def(emitter, module_name, module) for fn in module.functions: emitter.emit_line() generate_native_function(fn, emitter, self.source_paths[module_name], module_name) if fn.name != TOP_LEVEL_NAME: emitter.emit_line() generate_wrapper_function(fn, emitter, self.source_paths[module_name], module_name) if multi_file: name = ('__native_{}.c'.format( emitter.names.private_name(module_name))) file_contents.append((name, ''.join(emitter.fragments))) # The external header file contains type declarations while # the internal contains declarations of functions and objects # (which are shared between shared libraries via dynamic # exports tables and not accessed directly.) ext_declarations = Emitter(self.context) ext_declarations.emit_line('#ifndef MYPYC_NATIVE{}_H'.format( self.group_suffix)) ext_declarations.emit_line('#define MYPYC_NATIVE{}_H'.format( self.group_suffix)) ext_declarations.emit_line('#include <Python.h>') ext_declarations.emit_line('#include <CPy.h>') declarations = Emitter(self.context) declarations.emit_line('#ifndef MYPYC_NATIVE_INTERNAL{}_H'.format( self.group_suffix)) declarations.emit_line('#define MYPYC_NATIVE_INTERNAL{}_H'.format( self.group_suffix)) declarations.emit_line('#include <Python.h>') declarations.emit_line('#include <CPy.h>') declarations.emit_line('#include "__native{}.h"'.format( self.short_group_suffix)) declarations.emit_line() declarations.emit_line('int CPyGlobalsInit(void);') declarations.emit_line() for module_name, module in self.modules: self.declare_finals(module_name, module.final_names, declarations) for cl in module.classes: generate_class_type_decl(cl, emitter, ext_declarations, declarations) for fn in module.functions: generate_function_declaration(fn, declarations) for lib in sorted(self.context.group_deps): elib = exported_name(lib) short_lib = exported_name(lib.split('.')[-1]) declarations.emit_lines( '#include <{}>'.format( os.path.join(group_dir(lib), "__native_{}.h".format(short_lib))), 'struct export_table_{} exports_{};'.format(elib, elib)) sorted_decls = self.toposort_declarations() emitter = base_emitter self.generate_globals_init(emitter) emitter.emit_line() for declaration in sorted_decls: decls = ext_declarations if declaration.is_type else declarations if not declaration.is_type: decls.emit_lines('extern {}'.format(declaration.decl[0]), *declaration.decl[1:]) # If there is a definition, emit it. Otherwise repeat the declaration # (without an extern). if declaration.defn: emitter.emit_lines(*declaration.defn) else: emitter.emit_lines(*declaration.decl) else: decls.emit_lines(*declaration.decl) if self.group_name: self.generate_export_table(ext_declarations, emitter) self.generate_shared_lib_init(emitter) ext_declarations.emit_line('#endif') declarations.emit_line('#endif') output_dir = group_dir(self.group_name) if self.group_name else '' return file_contents + [ (os.path.join(output_dir, '__native{}.c'.format( self.short_group_suffix)), ''.join(emitter.fragments)), (os.path.join( output_dir, '__native_internal{}.h'.format( self.short_group_suffix)), ''.join( declarations.fragments)), (os.path.join(output_dir, '__native{}.h'.format( self.short_group_suffix)), ''.join( ext_declarations.fragments)), ]
def generate_c_for_modules(self) -> List[Tuple[str, str]]: file_contents = [] multi_file = self.use_shared_lib and self.multi_file # Collect all literal refs in IR. for _, module in self.modules: for fn in module.functions: collect_literals(fn, self.context.literals) base_emitter = Emitter(self.context) # Optionally just include the runtime library c files to # reduce the number of compiler invocations needed if self.compiler_options.include_runtime_files: for name in RUNTIME_C_FILES: base_emitter.emit_line(f'#include "{name}"') base_emitter.emit_line( f'#include "__native{self.short_group_suffix}.h"') base_emitter.emit_line( f'#include "__native_internal{self.short_group_suffix}.h"') emitter = base_emitter self.generate_literal_tables() for module_name, module in self.modules: if multi_file: emitter = Emitter(self.context) emitter.emit_line( f'#include "__native{self.short_group_suffix}.h"') emitter.emit_line( f'#include "__native_internal{self.short_group_suffix}.h"') self.declare_module(module_name, emitter) self.declare_internal_globals(module_name, emitter) self.declare_imports(module.imports, emitter) for cl in module.classes: if cl.is_ext_class: generate_class(cl, module_name, emitter) # Generate Python extension module definitions and module initialization functions. self.generate_module_def(emitter, module_name, module) for fn in module.functions: emitter.emit_line() generate_native_function(fn, emitter, self.source_paths[module_name], module_name) if fn.name != TOP_LEVEL_NAME: emitter.emit_line() if is_fastcall_supported(fn, emitter.capi_version): generate_wrapper_function( fn, emitter, self.source_paths[module_name], module_name) else: generate_legacy_wrapper_function( fn, emitter, self.source_paths[module_name], module_name) if multi_file: name = ( f'__native_{emitter.names.private_name(module_name)}.c') file_contents.append((name, ''.join(emitter.fragments))) # The external header file contains type declarations while # the internal contains declarations of functions and objects # (which are shared between shared libraries via dynamic # exports tables and not accessed directly.) ext_declarations = Emitter(self.context) ext_declarations.emit_line( f'#ifndef MYPYC_NATIVE{self.group_suffix}_H') ext_declarations.emit_line( f'#define MYPYC_NATIVE{self.group_suffix}_H') ext_declarations.emit_line('#include <Python.h>') ext_declarations.emit_line('#include <CPy.h>') declarations = Emitter(self.context) declarations.emit_line( f'#ifndef MYPYC_NATIVE_INTERNAL{self.group_suffix}_H') declarations.emit_line( f'#define MYPYC_NATIVE_INTERNAL{self.group_suffix}_H') declarations.emit_line('#include <Python.h>') declarations.emit_line('#include <CPy.h>') declarations.emit_line( f'#include "__native{self.short_group_suffix}.h"') declarations.emit_line() declarations.emit_line('int CPyGlobalsInit(void);') declarations.emit_line() for module_name, module in self.modules: self.declare_finals(module_name, module.final_names, declarations) for cl in module.classes: generate_class_type_decl(cl, emitter, ext_declarations, declarations) for fn in module.functions: generate_function_declaration(fn, declarations) for lib in sorted(self.context.group_deps): elib = exported_name(lib) short_lib = exported_name(lib.split('.')[-1]) declarations.emit_lines( '#include <{}>'.format( os.path.join(group_dir(lib), f"__native_{short_lib}.h")), f'struct export_table_{elib} exports_{elib};') sorted_decls = self.toposort_declarations() emitter = base_emitter self.generate_globals_init(emitter) emitter.emit_line() for declaration in sorted_decls: decls = ext_declarations if declaration.is_type else declarations if not declaration.is_type: decls.emit_lines(f'extern {declaration.decl[0]}', *declaration.decl[1:]) # If there is a definition, emit it. Otherwise repeat the declaration # (without an extern). if declaration.defn: emitter.emit_lines(*declaration.defn) else: emitter.emit_lines(*declaration.decl) else: decls.emit_lines(*declaration.decl) if self.group_name: self.generate_export_table(ext_declarations, emitter) self.generate_shared_lib_init(emitter) ext_declarations.emit_line('#endif') declarations.emit_line('#endif') output_dir = group_dir(self.group_name) if self.group_name else '' return file_contents + [ (os.path.join(output_dir, f'__native{self.short_group_suffix}.c'), ''.join(emitter.fragments)), (os.path.join(output_dir, f'__native_internal{self.short_group_suffix}.h'), ''.join(declarations.fragments)), (os.path.join(output_dir, f'__native{self.short_group_suffix}.h'), ''.join(ext_declarations.fragments)), ]