From f3781b105435bf36219ff5c5283b4c99e8e6cf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20M=C3=BCller?= <147368808+philip-paul-mueller@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:17:16 +0100 Subject: [PATCH] Better mangling of the state struct in the code generator (#1413) This PR addresses issue #1396, by modifying the way how the name of the state struct is created. --- dace/codegen/compiled_sdfg.py | 3 ++- .../codegen/instrumentation/data/data_dump.py | 2 +- dace/codegen/targets/cpp.py | 16 ++++++++++++ dace/codegen/targets/cpu.py | 2 +- dace/codegen/targets/cuda.py | 23 +++++++--------- dace/codegen/targets/fpga.py | 2 +- dace/codegen/targets/framecode.py | 26 ++++++++++--------- dace/codegen/targets/intel_fpga.py | 7 +++-- dace/codegen/targets/mpi.py | 11 ++++---- dace/codegen/targets/xilinx.py | 6 +++-- dace/config_schema.yml | 9 +++++++ 11 files changed, 67 insertions(+), 40 deletions(-) diff --git a/dace/codegen/compiled_sdfg.py b/dace/codegen/compiled_sdfg.py index 8a132f3df3..7de385cead 100644 --- a/dace/codegen/compiled_sdfg.py +++ b/dace/codegen/compiled_sdfg.py @@ -239,6 +239,7 @@ def get_state_struct(self) -> ctypes.Structure: return ctypes.cast(self._libhandle, ctypes.POINTER(self._try_parse_state_struct())).contents def _try_parse_state_struct(self) -> Optional[Type[ctypes.Structure]]: + from dace.codegen.targets.cpp import mangle_dace_state_struct_name # Avoid import cycle # the path of the main sdfg file containing the state struct main_src_path = os.path.join(os.path.dirname(os.path.dirname(self._lib._library_filename)), "src", "cpu", self._sdfg.name + ".cpp") @@ -247,7 +248,7 @@ def _try_parse_state_struct(self) -> Optional[Type[ctypes.Structure]]: code_flat = code.replace("\n", " ") # try to find the first struct definition that matches the name we are looking for in the sdfg file - match = re.search(f"struct {self._sdfg.name}_t {{(.*?)}};", code_flat) + match = re.search(f"struct {mangle_dace_state_struct_name(self._sdfg)} {{(.*?)}};", code_flat) if match is None or len(match.groups()) != 1: return None diff --git a/dace/codegen/instrumentation/data/data_dump.py b/dace/codegen/instrumentation/data/data_dump.py index 859f78bd79..2217524d19 100644 --- a/dace/codegen/instrumentation/data/data_dump.py +++ b/dace/codegen/instrumentation/data/data_dump.py @@ -195,7 +195,7 @@ def __init__(self): def _generate_report_setter(self, sdfg: SDFG) -> str: return f''' - DACE_EXPORTED void __dace_set_instrumented_data_report({sdfg.name}_t *__state, const char *dirpath) {{ + DACE_EXPORTED void __dace_set_instrumented_data_report({cpp.mangle_dace_state_struct_name(sdfg)} *__state, const char *dirpath) {{ __state->serializer->set_folder(dirpath); }} ''' diff --git a/dace/codegen/targets/cpp.py b/dace/codegen/targets/cpp.py index 3d26f76214..9687fb1783 100644 --- a/dace/codegen/targets/cpp.py +++ b/dace/codegen/targets/cpp.py @@ -34,6 +34,22 @@ from dace.codegen.dispatcher import TargetDispatcher +def mangle_dace_state_struct_name(sdfg: Union[SDFG, str]) -> str: + """This function creates a unique type name for the `SDFG`'s state `struct`. + + The function uses the `compiler.codegen_state_struct_suffix` + configuration entry for deriving the type name of the state `struct`. + + :param sdfg: The SDFG for which the name should be generated. + """ + name = sdfg if isinstance(sdfg, str) else sdfg.name + state_suffix = Config.get("compiler", "codegen_state_struct_suffix") + type_name = f"{name}{state_suffix}" + if not dtypes.validate_name(type_name): + raise ValueError(f"The mangled type name `{type_name}` of the state struct of SDFG '{name}' is invalid.") + return type_name + + def copy_expr( dispatcher, sdfg, diff --git a/dace/codegen/targets/cpu.py b/dace/codegen/targets/cpu.py index 88dda0058f..72ca554a4a 100644 --- a/dace/codegen/targets/cpu.py +++ b/dace/codegen/targets/cpu.py @@ -1490,7 +1490,7 @@ def generate_nsdfg_header(self, sdfg, state, state_id, node, memlet_references, if state_struct: toplevel_sdfg: SDFG = sdfg.sdfg_list[0] - arguments.append(f'{toplevel_sdfg.name}_t *__state') + arguments.append(f'{cpp.mangle_dace_state_struct_name(toplevel_sdfg)} *__state') # Add "__restrict__" keywords to arguments that do not alias with others in the context of this SDFG restrict_args = [] diff --git a/dace/codegen/targets/cuda.py b/dace/codegen/targets/cuda.py index fb8ae90187..b729b34088 100644 --- a/dace/codegen/targets/cuda.py +++ b/dace/codegen/targets/cuda.py @@ -1,11 +1,8 @@ # Copyright 2019-2021 ETH Zurich and the DaCe authors. All rights reserved. -import ast -import copy import ctypes import functools -import os import warnings -from typing import Any, Dict, List, Set, Tuple, Union +from typing import Dict, List, Set, Tuple, Union import networkx as nx import sympy @@ -14,7 +11,6 @@ import dace from dace import data as dt from dace import dtypes, registry -from dace import sdfg as sd from dace import subsets, symbolic from dace.codegen import common, cppunparse from dace.codegen.codeobject import CodeObject @@ -23,7 +19,7 @@ from dace.codegen.targets import cpp from dace.codegen.common import update_persistent_desc from dace.codegen.targets.cpp import (codeblock_to_cpp, cpp_array_expr, memlet_copy_to_absolute_strides, sym2cpp, - synchronize_streams, unparse_cr, unparse_cr_split) + synchronize_streams, unparse_cr, mangle_dace_state_struct_name) from dace.codegen.targets.target import IllegalCopy, TargetCodeGenerator, make_absolute from dace.config import Config from dace.frontend import operations @@ -345,12 +341,12 @@ def get_generated_codeobjects(self): {file_header} -DACE_EXPORTED int __dace_init_cuda({sdfg.name}_t *__state{params}); -DACE_EXPORTED int __dace_exit_cuda({sdfg.name}_t *__state); +DACE_EXPORTED int __dace_init_cuda({sdfg_state_name} *__state{params}); +DACE_EXPORTED int __dace_exit_cuda({sdfg_state_name} *__state); {other_globalcode} -int __dace_init_cuda({sdfg.name}_t *__state{params}) {{ +int __dace_init_cuda({sdfg_state_name} *__state{params}) {{ int count; // Check that we are able to run {backend} code @@ -389,7 +385,7 @@ def get_generated_codeobjects(self): return 0; }} -int __dace_exit_cuda({sdfg.name}_t *__state) {{ +int __dace_exit_cuda({sdfg_state_name} *__state) {{ {exitcode} // Synchronize and check for CUDA errors @@ -409,7 +405,7 @@ def get_generated_codeobjects(self): return __err; }} -DACE_EXPORTED bool __dace_gpu_set_stream({sdfg.name}_t *__state, int streamid, gpuStream_t stream) +DACE_EXPORTED bool __dace_gpu_set_stream({sdfg_state_name} *__state, int streamid, gpuStream_t stream) {{ if (streamid < 0 || streamid >= {nstreams}) return false; @@ -419,7 +415,7 @@ def get_generated_codeobjects(self): return true; }} -DACE_EXPORTED void __dace_gpu_set_all_streams({sdfg.name}_t *__state, gpuStream_t stream) +DACE_EXPORTED void __dace_gpu_set_all_streams({sdfg_state_name} *__state, gpuStream_t stream) {{ for (int i = 0; i < {nstreams}; ++i) __state->gpu_context->streams[i] = stream; @@ -427,6 +423,7 @@ def get_generated_codeobjects(self): {localcode} """.format(params=params_comma, + sdfg_state_name=mangle_dace_state_struct_name(self._global_sdfg), initcode=initcode.getvalue(), exitcode=exitcode.getvalue(), other_globalcode=self._globalcode.getvalue(), @@ -1567,7 +1564,7 @@ def generate_scope(self, sdfg, dfg_scope, state_id, function_stream, callsite_st self.scope_entry_stream = old_entry_stream self.scope_exit_stream = old_exit_stream - state_param = [f'{self._global_sdfg.name}_t *__state'] + state_param = [f'{mangle_dace_state_struct_name(self._global_sdfg)} *__state'] # Write callback function definition self._localcode.write( diff --git a/dace/codegen/targets/fpga.py b/dace/codegen/targets/fpga.py index 413cb751d6..8df8fe94fa 100644 --- a/dace/codegen/targets/fpga.py +++ b/dace/codegen/targets/fpga.py @@ -652,7 +652,7 @@ def generate_state(self, sdfg: dace.SDFG, state: dace.SDFGState, function_stream kernel_args_opencl = [] # Include state in args - kernel_args_opencl.append(f"{self._global_sdfg.name}_t *__state") + kernel_args_opencl.append(f"{cpp.mangle_dace_state_struct_name(self._global_sdfg)} *__state") kernel_args_call_host.append(f"__state") for is_output, arg_name, arg, interface_id in state_parameters: diff --git a/dace/codegen/targets/framecode.py b/dace/codegen/targets/framecode.py index b1eb42fe60..0db4062976 100644 --- a/dace/codegen/targets/framecode.py +++ b/dace/codegen/targets/framecode.py @@ -131,6 +131,7 @@ def generate_fileheader(self, sdfg: SDFG, global_stream: CodeIOStream, backend: :param global_stream: Stream to write to (global). :param backend: Whose backend this header belongs to. """ + from dace.codegen.targets.cpp import mangle_dace_state_struct_name # Avoid circular import # Hash file include if backend == 'frame': global_stream.write('#include "../../include/hash.h"\n', sdfg) @@ -181,7 +182,7 @@ def _emit_definitions(dtype: dtypes.typeclass, wrote_something: bool) -> bool: # Write state struct structstr = '\n'.join(self.statestruct) global_stream.write(f''' -struct {sdfg.name}_t {{ +struct {mangle_dace_state_struct_name(sdfg)} {{ {structstr} }}; @@ -226,6 +227,7 @@ def generate_footer(self, sdfg: SDFG, global_stream: CodeIOStream, callsite_stre :param callsite_stream: Stream to write to (at call site). """ import dace.library + from dace.codegen.targets.cpp import mangle_dace_state_struct_name # Avoid circular import fname = sdfg.name params = sdfg.signature(arglist=self.arglist) paramnames = sdfg.signature(False, for_call=True, arglist=self.arglist) @@ -255,7 +257,7 @@ def generate_footer(self, sdfg: SDFG, global_stream: CodeIOStream, callsite_stre initparamnames_comma = (', ' + initparamnames) if initparamnames else '' callsite_stream.write( f''' -DACE_EXPORTED void __program_{fname}({fname}_t *__state{params_comma}) +DACE_EXPORTED void __program_{fname}({mangle_dace_state_struct_name(fname)} *__state{params_comma}) {{ __program_{fname}_internal(__state{paramnames_comma}); }}''', sdfg) @@ -263,18 +265,17 @@ def generate_footer(self, sdfg: SDFG, global_stream: CodeIOStream, callsite_stre for target in self._dispatcher.used_targets: if target.has_initializer: callsite_stream.write( - 'DACE_EXPORTED int __dace_init_%s(%s_t *__state%s);\n' % - (target.target_name, sdfg.name, initparams_comma), sdfg) + f'DACE_EXPORTED int __dace_init_{target.target_name}({mangle_dace_state_struct_name(sdfg)} *__state{initparams_comma});\n', sdfg) if target.has_finalizer: callsite_stream.write( - 'DACE_EXPORTED int __dace_exit_%s(%s_t *__state);\n' % (target.target_name, sdfg.name), sdfg) + f'DACE_EXPORTED int __dace_exit_{target.target_name}({mangle_dace_state_struct_name(sdfg)} *__state);\n', sdfg) callsite_stream.write( f""" -DACE_EXPORTED {sdfg.name}_t *__dace_init_{sdfg.name}({initparams}) +DACE_EXPORTED {mangle_dace_state_struct_name(sdfg)} *__dace_init_{sdfg.name}({initparams}) {{ int __result = 0; - {sdfg.name}_t *__state = new {sdfg.name}_t; + {mangle_dace_state_struct_name(sdfg)} *__state = new {mangle_dace_state_struct_name(sdfg)}; """, sdfg) @@ -306,7 +307,7 @@ def generate_footer(self, sdfg: SDFG, global_stream: CodeIOStream, callsite_stre return __state; }} -DACE_EXPORTED int __dace_exit_{sdfg.name}({sdfg.name}_t *__state) +DACE_EXPORTED int __dace_exit_{sdfg.name}({mangle_dace_state_struct_name(sdfg)} *__state) {{ int __err = 0; """, sdfg) @@ -352,6 +353,7 @@ def generate_external_memory_management(self, sdfg: SDFG, callsite_stream: CodeI can be ``CPU_Heap`` or any other ``dtypes.StorageType``); and (2) set the externally-allocated pointer to the generated code's internal state (``__dace_set_external_memory_``). """ + from dace.codegen.targets.cpp import mangle_dace_state_struct_name # Avoid circular import # Collect external arrays ext_arrays: Dict[dtypes.StorageType, List[Tuple[SDFG, str, data.Data]]] = collections.defaultdict(list) @@ -374,7 +376,7 @@ def generate_external_memory_management(self, sdfg: SDFG, callsite_stream: CodeI # Size query functions callsite_stream.write( f''' -DACE_EXPORTED size_t __dace_get_external_memory_size_{storage.name}({sdfg.name}_t *__state{initparams_comma}) +DACE_EXPORTED size_t __dace_get_external_memory_size_{storage.name}({mangle_dace_state_struct_name(sdfg)} *__state{initparams_comma}) {{ return {sym2cpp(size)}; }} @@ -383,7 +385,7 @@ def generate_external_memory_management(self, sdfg: SDFG, callsite_stream: CodeI # Pointer set functions callsite_stream.write( f''' -DACE_EXPORTED void __dace_set_external_memory_{storage.name}({sdfg.name}_t *__state, char *ptr{initparams_comma}) +DACE_EXPORTED void __dace_set_external_memory_{storage.name}({mangle_dace_state_struct_name(sdfg)} *__state, char *ptr{initparams_comma}) {{''', sdfg) offset = 0 @@ -828,7 +830,6 @@ def generate_code(self, code, and a set of targets that have been used in the generation of this SDFG. """ - if len(sdfg_id) == 0 and sdfg.sdfg_id != 0: sdfg_id = '_%d' % sdfg.sdfg_id @@ -923,6 +924,7 @@ def generate_code(self, # Get all environments used in the generated code, including # dependent environments import dace.library # Avoid import loops + from dace.codegen.targets.cpp import mangle_dace_state_struct_name self.environments = dace.library.get_environments_and_dependencies(self._dispatcher.used_environments) self.generate_header(sdfg, header_global_stream, header_stream) @@ -931,7 +933,7 @@ def generate_code(self, params = sdfg.signature(arglist=self.arglist) if params: params = ', ' + params - function_signature = ('void __program_%s_internal(%s_t *__state%s)\n{\n' % (sdfg.name, sdfg.name, params)) + function_signature = f'void __program_{sdfg.name}_internal({mangle_dace_state_struct_name(sdfg)}*__state{params})\n{{' self.generate_footer(sdfg, footer_global_stream, footer_stream) self.generate_external_memory_management(sdfg, footer_stream) diff --git a/dace/codegen/targets/intel_fpga.py b/dace/codegen/targets/intel_fpga.py index d3c46b0069..03a04fda41 100644 --- a/dace/codegen/targets/intel_fpga.py +++ b/dace/codegen/targets/intel_fpga.py @@ -3,8 +3,6 @@ import functools import copy import itertools -import os -import re from six import StringIO import numpy as np @@ -143,19 +141,20 @@ def get_generated_codeobjects(self): params_comma = ', ' + params_comma host_code.write(""" -DACE_EXPORTED int __dace_init_intel_fpga({sdfg.name}_t *__state{signature}) {{{emulation_flag} +DACE_EXPORTED int __dace_init_intel_fpga({sdfg_state_name} *__state{signature}) {{{emulation_flag} __state->fpga_context = new dace_fpga_context(); __state->fpga_context->Get().MakeProgram({kernel_file_name}); return 0; }} -DACE_EXPORTED int __dace_exit_intel_fpga({sdfg.name}_t *__state) {{ +DACE_EXPORTED int __dace_exit_intel_fpga({sdfg_state_name} *__state) {{ delete __state->fpga_context; return 0; }} {host_code}""".format(signature=params_comma, sdfg=self._global_sdfg, + sdfg_state_name=cpp.mangle_dace_state_struct_name(self._global_sdfg), emulation_flag=emulation_flag, kernel_file_name=kernel_file_name, host_code="".join([ diff --git a/dace/codegen/targets/mpi.py b/dace/codegen/targets/mpi.py index 419334ba5a..0bb2b67a7e 100644 --- a/dace/codegen/targets/mpi.py +++ b/dace/codegen/targets/mpi.py @@ -4,6 +4,7 @@ from dace.codegen.prettycode import CodeIOStream from dace.codegen.codeobject import CodeObject from dace.codegen.targets.target import TargetCodeGenerator, make_absolute +from dace.codegen.targets.cpp import mangle_dace_state_struct_name from dace.sdfg import nodes, SDFG from dace.config import Config @@ -45,10 +46,10 @@ def get_generated_codeobjects(self): {file_header} -DACE_EXPORTED int __dace_init_mpi({sdfg.name}_t *__state{params}); -DACE_EXPORTED int __dace_exit_mpi({sdfg.name}_t *__state); +DACE_EXPORTED int __dace_init_mpi({sdfg_state_name} *__state{params}); +DACE_EXPORTED int __dace_exit_mpi({sdfg_state_name} *__state); -int __dace_init_mpi({sdfg.name}_t *__state{params}) {{ +int __dace_init_mpi({sdfg_state_name} *__state{params}) {{ int isinit = 0; if (MPI_Initialized(&isinit) != MPI_SUCCESS) return 1; @@ -66,7 +67,7 @@ def get_generated_codeobjects(self): return 0; }} -int __dace_exit_mpi({sdfg.name}_t *__state) {{ +int __dace_exit_mpi({sdfg_state_name} *__state) {{ MPI_Comm_free(&__dace_mpi_comm); MPI_Finalize(); @@ -74,7 +75,7 @@ def get_generated_codeobjects(self): __dace_comm_size); return 0; }} -""".format(params=params_comma, sdfg=sdfg, file_header=fileheader.getvalue()), 'cpp', MPICodeGen, 'MPI') +""".format(params=params_comma, sdfg=sdfg, sdfg_state_name=mangle_dace_state_struct_name(sdfg), file_header=fileheader.getvalue()), 'cpp', MPICodeGen, 'MPI') return [codeobj] @staticmethod diff --git a/dace/codegen/targets/xilinx.py b/dace/codegen/targets/xilinx.py index 5d82cfeafc..0c562c59c5 100644 --- a/dace/codegen/targets/xilinx.py +++ b/dace/codegen/targets/xilinx.py @@ -7,6 +7,7 @@ import re import numpy as np import ast + import dace from dace import data as dt, registry, dtypes, subsets from dace.config import Config @@ -141,7 +142,7 @@ def get_generated_codeobjects(self): params_comma = ', ' + params_comma host_code.write(""" -DACE_EXPORTED int __dace_init_xilinx({sdfg.name}_t *__state{signature}) {{ +DACE_EXPORTED int __dace_init_xilinx({sdfg_state_name} *__state{signature}) {{ {environment_variables} __state->fpga_context = new dace_fpga_context(); @@ -149,13 +150,14 @@ def get_generated_codeobjects(self): return 0; }} -DACE_EXPORTED int __dace_exit_xilinx({sdfg.name}_t *__state) {{ +DACE_EXPORTED int __dace_exit_xilinx({sdfg_state_name} *__state) {{ delete __state->fpga_context; return 0; }} {host_code}""".format(signature=params_comma, sdfg=self._global_sdfg, + sdfg_state_name=cpp.mangle_dace_state_struct_name(self._global_sdfg), environment_variables=set_env_vars, kernel_file_name=kernel_file_name, host_code="".join([ diff --git a/dace/config_schema.yml b/dace/config_schema.yml index 08a427aa52..063815e319 100644 --- a/dace/config_schema.yml +++ b/dace/config_schema.yml @@ -164,6 +164,15 @@ required: of the code generator that generated it. Used for debugging code generation. + codegen_state_struct_suffix: + type: str + default: "_state_t" + title: Suffix used by the code generator to mangle the state struct. + description: > + For every SDFG the code generator is is processing a state struct is generated. + The typename of this struct is derived by appending this value to the SDFG's name. + Note that the suffix may only contains letters, digits and underscores. + default_data_types: type : str default: Python