Peano
Loading...
Searching...
No Matches
kernels.py
Go to the documentation of this file.
1import jinja2
2import os
3import subprocess
4import exahype2.dslhype as DSL
5
7 function,
8 template_parameters,
9 functor_arguments,
10 solver_filename,
11 solver_includes,
12 solver_namespace,
13 solver_dictionary,
14 enabled_pde_terms,
15 use_mlir = True
16):
17 cpp_trees = []
18 mlir_trees = []
19
20 cpp_printer = DSL.printers.CPPPrinter(spaces_per_tab=4)
21 omp_printer = DSL.printers.OMPPrinter(spaces_per_tab=4)
22 sycl_printer = DSL.printers.SYCLPrinter(spaces_per_tab=4)
23 if use_mlir:
24 mlir_printer = DSL.printers.MLIRPrinter(spaces_per_tab=4, use_omp=False)
25 mlir_omp_printer = DSL.printers.MLIRPrinter(spaces_per_tab=4, use_omp=True)
26
27 cpp_trees.append(DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms))
28 cpp_trees.append(DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms, stateless = True))
29 cpp_trees.append(DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms, stateless = True, use_accelerator=True, use_memory_manager=True))
30
31 if not os.path.exists("kernels"):
32 os.makedirs("kernels")
33
34 declaration_file = open(f"kernels/{solver_filename}.h", "w")
35 definition_file = open(f"kernels/{solver_filename}.cpph", "w")
36 declaration_file.write("#pragma once\n")
37 declaration_file.write(solver_includes)
38 for i, tree in enumerate(cpp_trees):
39 if not tree.use_accelerator:
40 declaration_file.write(tree.print_declaration(cpp_printer))
41 declaration_file.write(tree.print_declaration_with_timer(cpp_printer))
42 definition_file.write(tree.print(cpp_printer))
43 definition_file.write(tree.print_definition_with_timer(cpp_printer))
44
45 if tree.use_accelerator:
46 declaration_file.write("#if defined (GPUOffloadingOMP)\n")
47 definition_file.write("#if defined (GPUOffloadingOMP)\n")
48 else:
49 declaration_file.write("#if defined (SharedOMP)\n")
50 definition_file.write("#if defined (SharedOMP)\n")
51
52 declaration_file.write(tree.print_declaration(omp_printer))
53 declaration_file.write(tree.print_declaration_with_timer(omp_printer))
54 definition_file.write(tree.print(omp_printer))
55 definition_file.write(tree.print_definition_with_timer(omp_printer))
56 declaration_file.write ("#endif\n")
57 definition_file.write ("#endif\n")
58
59 declaration_file.write("#if defined (GPUOffloadingSYCL)\n")
60 definition_file.write("#if defined (GPUOffloadingSYCL)\n")
61 declaration_file.write(tree.print_declaration(sycl_printer))
62 declaration_file.write(tree.print_declaration_with_timer(sycl_printer))
63 definition_file.write(tree.print(sycl_printer))
64 definition_file.write(tree.print_definition_with_timer(sycl_printer))
65 declaration_file.write ("#endif\n")
66 definition_file.write ("#endif\n")
67
68 declaration_file.write(f"""#ifdef USE_MLIR
69#include "MLIRbridge.cpph"
70#endif
71#include "{solver_filename}.cpph"
72
73""")
74 declaration_file.close()
75 definition_file.close()
76
77 # These function definitions are used to generate declarations in the MLIR bridge files
78 mlir_function_definition = DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms)
79 mlir_stateless_function_definition = DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms, stateless=True)
80 gpu_namespace = solver_namespace.copy()
81 gpu_namespace = gpu_namespace + ["mlir", "gpu"]
82 mlir_gpu_function_definition = DSL.Parser().parse(function, template_parameters, functor_arguments, gpu_namespace, enabled_pde_terms, stateless=True, use_accelerator=True)
83
84 mlir_cpu_function_definitions = [
85 mlir_function_definition,
86 mlir_stateless_function_definition
87 ]
88
89 if use_mlir:
90 file = open(f"kernels/{solver_filename}.mlir", "w")
91 file.write(DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms).print(mlir_printer))
92 file.close()
93
94 file = open(f"kernels/{solver_filename}_omp.mlir", "w")
95 file.write(DSL.Parser().parse(function, template_parameters, functor_arguments, solver_namespace, enabled_pde_terms).print(mlir_omp_printer))
96 file.close()
97
98 # Generate MLIR bridge files
99 with open("MLIRbridge.cpph", 'w') as file:
100 file.write(f"""
101#include "MemRefDescriptor.cpph"
102{solver_includes}
103// MLIR-compatible structure using memref descriptors
104struct MLIRCellData {{
105 MemRefDescriptor2D QIn; // Field 0
106 MemRefDescriptor2D QOut; // Field 1
107 MemRefDescriptorNested cellCentre; // Field 2
108 MemRefDescriptorNested cellSize; // Field 3
109 MemRefDescriptor1D t; // Field 4
110 MemRefDescriptor1D dt; // Field 5
111 MemRefDescriptor1D id; // Field 6
112 int64_t numberOfCells; // Field 7
113 int64_t memoryLocation; // Field 8
114 int64_t targetDevice; // Field 9
115 double** QOut_legacy; // Field 10
116 double* maxEigenvalue; // Field 11
117}};
118void convertToMLIRStruct(exahype2::CellData<double, double>* patchData, MLIRCellData* mlir_data);
119void convertToMLIRStructGPU(exahype2::CellData<double, double>* copyCellData, MLIRCellData* mlir_data);
120void cleanupMLIRStruct(MLIRCellData* mlir_data);
121void cleanupMLIRStructGPU(MLIRCellData* mlir_data);
122void copybackMLIRresults(exahype2::CellData<double, double>* patchData, MLIRCellData* mlir_data);
123""")
124 file.write(f"""extern "C" void {solver_dictionary["KERNEL_NAME"]}_MLIR_wrapper(exahype2::CellData<double, double>* patchData, bool use_omp);\n""")
125 for definition in mlir_cpu_function_definitions:
126 definition._arguments[0]._type = DSL.SyntaxTree.TCustom("CellData<double, double>&")
127 file.write(definition.print_declaration(mlir_printer))
128 file.write(definition.print_declaration_with_timer(mlir_printer))
129 file.write(definition.print_declaration(mlir_omp_printer))
130 file.write(definition.print_declaration_with_timer(mlir_omp_printer))
131 definition._arguments[0]._type = DSL.SyntaxTree.TCustom("MLIRCellData*")
132 file.write(definition.print_declaration(mlir_printer))
133 file.write(definition.print_declaration_with_timer(mlir_printer))
134 file.write(definition.print_declaration(mlir_omp_printer))
135 file.write(definition.print_declaration_with_timer(mlir_omp_printer))
136
137 file.write("#ifdef MLIR_GPU\n")
138 mlir_gpu_function_definition._arguments[1]._type = DSL.SyntaxTree.TCustom("MLIRCellData*")
139 file.write(mlir_gpu_function_definition.print_declaration(cpp_printer))
140 file.write(mlir_gpu_function_definition.print_declaration_with_timer(cpp_printer))
141 file.write("#endif")
142
143 argument_names = []
144 memref_constructors = """"""
145 for argument in functor_arguments:
146 if argument._is_function:
147 argument_names.append(argument.id + "_bridge")
148 else:
149 argument_names.append(argument.id + "_memref")
150 memref_constructors += f"""
151 auto {argument.id}_memref = MemRefDescriptor1D();
152 {argument.id}_memref.allocated = const_cast<double*>({solver_dictionary["SOLVER_CLASS_NAMESPACE"]}::{solver_dictionary["SOLVER_CLASS"]}::{argument.id});
153 {argument.id}_memref.aligned = const_cast<double*>({solver_dictionary["SOLVER_CLASS_NAMESPACE"]}::{solver_dictionary["SOLVER_CLASS"]}::{argument.id});
154 {argument.id}_memref.offset = 0;
155 {argument.id}_memref.sizes[0] = {solver_dictionary["SOLVER_CLASS_NAMESPACE"]}::{solver_dictionary["SOLVER_CLASS"]}::DGOrder + 1;
156 {argument.id}_memref.strides[0] = 1;
157"""
158 solver_dictionary["FUNCTION_ARGUMENTS"] = ",".join(argument_names)
159 solver_dictionary["MEMREF_CONSTRUCTORS"] = memref_constructors
160
161 template_strings = [argument.print(cpp_printer) for argument in mlir_function_definition._template]
162 solver_dictionary["TEMPLATE_DEFINITION"] = "template <" + ",".join(template_strings) + ">\n"
163 filepath = os.path.realpath(__file__)
164 filepath_root = filepath[0:filepath.find("dslhype/")]
165 with open(filepath_root + "dslhype/MLIRbridge.template.cpp", 'r') as template_file:
166 mlir_bridge_template = template_file.read()
167 mlir_bridge_cpp = jinja2.Template(mlir_bridge_template).render(**solver_dictionary)
168 with open("MLIRbridge.cpp", 'w') as file:
169 file.write(mlir_bridge_cpp)
170
171def set_mlir_environment(project, filename_root, physics_file, dimensions, use_gpu, flatten_memrefs=False, inline_functions=False, nested_parallel_ops_to_for_ops=-1, imperfect_nested_loops_to_perfect=False, nested_parallel_ops_to_multidimensional=False, use_omp_tasks=False, omp_task_version="taskloop", omp_single_region=False, max_patch_size=-1, use_memory_fusion=False, use_loop_fusion=False):
172 print(f"Enabling MLIR kernel compilation support for {dimensions}D...")
173
174 serial_pipeline_builder = DSL.PipelineBuilder()
175 if flatten_memrefs:
176 serial_pipeline_builder.enable_flatten_memrefs()
177 if inline_functions:
178 serial_pipeline_builder.enable_inline_polygeist_functions(physics_file)
179 if imperfect_nested_loops_to_perfect:
180 serial_pipeline_builder.enable_imperfect_nested_loops_to_perfect()
181 if nested_parallel_ops_to_multidimensional:
182 serial_pipeline_builder.enable_nested_parallel_ops_to_multidimensional()
183 if nested_parallel_ops_to_for_ops > -1:
184 serial_pipeline_builder.enable_nested_parallel_ops_to_for_ops(nested_parallel_ops_to_for_ops)
185 if max_patch_size > -1:
186 serial_pipeline_builder.enable_memory_reuse(max_patch_size)
187 if use_memory_fusion:
188 serial_pipeline_builder.enable_memory_fusion()
189 if use_loop_fusion:
190 serial_pipeline_builder.enable_loop_fusion()
191
192 serial_pipeline = serial_pipeline_builder.generate_opt_commands()
193 project.output.makefile.d["SERIAL_PIPELINE"] = serial_pipeline
194
195 omp_pipeline_builder = DSL.PipelineBuilder()
196 omp_pipeline_builder.enable_omp(use_omp_tasks, omp_task_version, omp_single_region)
197 if flatten_memrefs:
198 omp_pipeline_builder.enable_flatten_memrefs()
199 if inline_functions:
200 omp_pipeline_builder.enable_inline_polygeist_functions(physics_file)
201 if imperfect_nested_loops_to_perfect:
202 omp_pipeline_builder.enable_imperfect_nested_loops_to_perfect()
203 if nested_parallel_ops_to_multidimensional:
204 omp_pipeline_builder.enable_nested_parallel_ops_to_multidimensional()
205 if nested_parallel_ops_to_for_ops > -1:
206 omp_pipeline_builder.enable_nested_parallel_ops_to_for_ops(nested_parallel_ops_to_for_ops)
207 if max_patch_size > -1:
208 omp_pipeline_builder.enable_memory_reuse(max_patch_size)
209 if use_memory_fusion:
210 omp_pipeline_builder.enable_memory_fusion()
211 if use_loop_fusion:
212 omp_pipeline_builder.enable_loop_fusion()
213 omp_pipeline = omp_pipeline_builder.generate_opt_commands()
214 project.output.makefile.d["OMP_PIPELINE"] = omp_pipeline
215
216 # Set environment variable for dimension detection
217 os.environ['MLIR_DIMENSIONS'] = str(dimensions)
218 filepath = os.path.realpath(__file__)
219 peano_dir = filepath[0:filepath.find("python/")]
220
221 # Add MLIR-specific files and flags
222 project.output.makefile.add_cpp_file("MLIRbridge.cpp")
223 project.output.makefile.add_CXX_flag("-DUSE_MLIR")
224
225 # NOTE: As we are using LLVM toolings for MLIR, we link against LLVM's OpenMP library
226 project.output.makefile.add_linker_flag("-lomp")
227 if use_gpu:
228 print("Enabling MLIR GPU kernel compilation support")
229 project.output.makefile.add_linker_flag("-lomptarget")
230 gpu_pipeline_builder = DSL.PipelineBuilder()
231 gpu_pipeline_builder.enable_gpu(project.output.makefile.d["GPU_VENDOR"], project.output.makefile.d["GPU_ARCH"], physics_file)
232 if imperfect_nested_loops_to_perfect:
233 gpu_pipeline_builder.enable_imperfect_nested_loops_to_perfect()
234 if nested_parallel_ops_to_multidimensional:
235 gpu_pipeline_builder.enable_nested_parallel_ops_to_multidimensional()
236 if nested_parallel_ops_to_for_ops > -1:
237 gpu_pipeline_builder.enable_nested_parallel_ops_to_for_ops(nested_parallel_ops_to_for_ops)
238 if max_patch_size > -1:
239 gpu_pipeline_builder.enable_memory_reuse(max_patch_size)
240 if use_memory_fusion:
241 gpu_pipeline_builder.enable_memory_fusion()
242 if use_loop_fusion:
243 gpu_pipeline_builder.enable_loop_fusion()
244 project.output.makefile.d["GPU_PIPELINE"] = gpu_pipeline_builder.generate_opt_commands()
245
246 project.output.makefile.d["MLIR_GPU"] = True
247 project.output.makefile.add_CXX_flag("-DMLIR_GPU")
248 project.output.makefile.add_linker_flag("-ldl")
249 project.output.makefile.add_linker_flag("-pthread")
250
251 # NOTE: The current MLIR GPU linearization and lowering pipeline uses the base MLIR
252 # but may use the OMP version in future. Update here, as appropriate
253 project.output.makefile.d["MLIR_SOURCES_GPU"] = f"{filename_root}{dimensions}d.mlir"
254 project.output.makefile.d["MLIR_LL_FILE_GPU"] = f"{filename_root}{dimensions}d_gpu.ll"
255 project.output.makefile.d["MLIR_OBJECT_GPU"] = f"{filename_root}{dimensions}d_mlir_gpu.o"
256
257 if project.output.makefile.d["GPU_VENDOR"] == "NVIDIA":
258 project.output.makefile.d["MLIR_GPU_NVIDIA"] = True
259 project.output.makefile.add_CXX_flag("-DMLIR_GPU_NVIDIA")
260 CUDA_HOME = os.path.dirname(subprocess.check_output("which nvcc", shell=True).decode().strip())
261 project.output.makefile.add_cpp_file("MLIRbridgeCUDAfunctions.cpp")
262 project.output.makefile.add_linker_flag("-lcuda")
263 project.output.makefile.add_linker_flag("-lcudart")
264 project.output.makefile.add_linker_flag("-lmlir_cuda_runtime")
265 project.output.makefile.add_linker_flag(f"-L{CUDA_HOME}/../targets/sbsa-linux/lib")
266 #peano_dir =
267 cuda_bridge_header_file = f"{peano_dir}python/exahype2/dslhype/MLIRbridgeCUDAfunctions.h"
268 cuda_bridge_file = f"{peano_dir}python/exahype2/dslhype/MLIRbridgeCUDAfunctions.cpp"
269 os.system("cp {} MLIRbridgeCUDAfunctions.h".format(cuda_bridge_header_file))
270 os.system("cp {} MLIRbridgeCUDAfunctions.cpp".format(cuda_bridge_file))
271 elif project.output.makefile.d["GPU_VENDOR"] == "AMD":
272 project.output.makefile.d["MLIR_GPU_AMD"] = True
273 project.output.makefile.add_CXX_flag("-DMLIR_GPU_AMD")
274 ROCM_HOME = os.path.dirname(subprocess.check_output("which hipcc", shell=True).decode().strip()) + "/.."
275 os.environ["ROCM_HOME"] = ROCM_HOME
276 project.output.makefile.add_cpp_file("MLIRbridgeHIPfunctions.cpp")
277 project.output.makefile.add_CXX_flag("-D__HIP_PLATFORM_AMD__")
278 project.output.makefile.add_linker_flag("-lamdhip64")
279 project.output.makefile.add_CXX_flag(f"-I{ROCM_HOME}/include")
280 project.output.makefile.add_linker_flag("-lmlir_rocm_runtime")
281 hip_bridge_header_file = f"{peano_dir}python/exahype2/dslhype/MLIRbridgeHIPfunctions.h"
282 hip_bridge_file = f"{peano_dir}python/exahype2/dslhype/MLIRbridgeHIPfunctions.cpp"
283 os.system("cp {} MLIRbridgeHIPfunctions.h".format(hip_bridge_header_file))
284 os.system("cp {} MLIRbridgeHIPfunctions.cpp".format(hip_bridge_file))
285 else:
286 raise RuntimeError("Unknown GPU vendor, please set it when configuring Peano with --with-gpu-vendor. Also set the GPU architecture with --with-gpu-arch.")
287
288 project.output.makefile.d["MLIR_ENABLED"] = True
289 project.output.makefile.d["MLIR_PHYSICS_FILE"] = f"{physics_file}"
290 project.output.makefile.d["MLIR_SOURCES"] = f"{filename_root}{dimensions}d.mlir"
291 project.output.makefile.d["MLIR_LL_FILE"] = f"{filename_root}{dimensions}d.ll"
292 project.output.makefile.d["MLIR_OBJECT"] = f"{filename_root}{dimensions}d_mlir.o"
293 project.output.makefile.d["MLIR_SOURCES_OMP"] = f"{filename_root}{dimensions}d_omp.mlir"
294 project.output.makefile.d["MLIR_LL_FILE_OMP"] = f"{filename_root}{dimensions}d_omp.ll"
295 project.output.makefile.d["MLIR_OBJECT_OMP"] = f"{filename_root}{dimensions}d_mlir_omp.o"
create_kernel_definitions(function, template_parameters, functor_arguments, solver_filename, solver_includes, solver_namespace, solver_dictionary, enabled_pde_terms, use_mlir=True)
Definition kernels.py:16
set_mlir_environment(project, filename_root, physics_file, dimensions, use_gpu, flatten_memrefs=False, inline_functions=False, nested_parallel_ops_to_for_ops=-1, imperfect_nested_loops_to_perfect=False, nested_parallel_ops_to_multidimensional=False, use_omp_tasks=False, omp_task_version="taskloop", omp_single_region=False, max_patch_size=-1, use_memory_fusion=False, use_loop_fusion=False)
Definition kernels.py:171