MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
Mim.cmake
Go to the documentation of this file.
1include(GNUInstallDirs)
2
3if(NOT DEFINED MIM_PLUGIN_LIST)
4 set(MIM_PLUGIN_LIST "" CACHE INTERNAL "MIM_PLUGIN_LIST")
5endif()
6if(NOT DEFINED MIM_PLUGIN_LAYOUT)
7 set(MIM_PLUGIN_LAYOUT "" CACHE INTERNAL "MIM_PLUGIN_LAYOUT")
8endif()
9
10if(NOT MIM_TARGET_NAMESPACE)
11 set(MIM_TARGET_NAMESPACE "")
12endif()
13
14option(MIM_BUILD_LL_RUNTIME "Compile the ll backend's C runtime wrappers to LLVM IR (requires clang)." ON)
15find_program(MIM_CLANG NAMES clang)
16if(MIM_BUILD_LL_RUNTIME AND NOT MIM_CLANG)
17 message(STATUS
18 "MimIR: clang not found; ll backend C runtime wrappers will not be built. "
19 "Set MIM_CLANG or disable MIM_BUILD_LL_RUNTIME to silence this."
20 )
21endif()
22
23## \page add_mim_plugin_cmake add_mim_plugin
24## \brief Registers a new MimIR plugin.
25##
26## Relative to the plugin's `CMakeLists.txt`, there should be a file
27## `<plugin-name>.mim` containing the plugin's annexes.
28##
29## \code{.cmake}
30## add_mim_plugin(<plugin-name>
31## [SOURCES <source>...]
32## [PRIVATE <private-item>...]
33## [INSTALL])
34## \endcode
35##
36## `<plugin-name>` may only contain letters, digits, and underscores, and must
37## satisfy MimIR's plugin naming constraints.
38##
39## The command creates two targets:
40## - `mim_internal_<plugin-name>` bootstraps the plugin, generates
41## `<plugin-name>/autogen.h` for the C++ interface used to identify annexes,
42## `<plugin-name>.md` for the documentation, and copies `<plugin-name>.mim`
43## into the build tree.
44## - `mim_<plugin-name>` builds the actual `MODULE` library that contains
45## normalizers, passes, and backends. One of the listed source files must
46## export `mim_get_plugin`.
47##
48## `SOURCES` lists the source files that are compiled into the loadable plugin.
49## `PRIVATE` lists additional private link dependencies.
50##
51## `INSTALL` installs the plugin module, its `.mim` file, and the generated
52## `autogen.h`. The export name `mim-targets` must be exported accordingly; see
53## CMake's `install(EXPORT ...)` documentation.
54##
55## Additional target properties can be set afterwards, for example:
56## \code{.cmake}
57## target_include_directories(mim_<plugin-name> <path>...)
58## \endcode
59function(add_mim_plugin)
60 set(PLUGIN ${ARGV0})
61
62 if(NOT PLUGIN MATCHES "^[A-Za-z0-9_]+$")
63 message(FATAL_ERROR "Mim plugin names may only contain letters, digits, and underscores")
64 endif()
65
66 string(LENGTH "${PLUGIN}" PLUGIN_LENGTH)
67 if(PLUGIN_LENGTH GREATER 8)
68 message(FATAL_ERROR "Mim plugin '${PLUGIN}' exceeds the maximum supported length of 8 characters")
69 endif()
70
71 cmake_parse_arguments(
72 PARSE_ARGV 1 # skip first arg
73 PARSED # prefix of output variables
74 "INSTALL" # options
75 "" # one-value keywords (none)
76 "SOURCES;PRIVATE" # multi-value keywords
77 )
78
79 set(PLUGIN_MIM ${CMAKE_CURRENT_LIST_DIR}/${PLUGIN}.mim)
80 set(OUT_PLUGIN_MIM ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/${PLUGIN}.mim)
81 set(PLUGIN_MD ${CMAKE_BINARY_DIR}/docs/plug/${PLUGIN}.md)
82 set(AUTOGEN_H ${CMAKE_BINARY_DIR}/include/mim/plug/${PLUGIN}/autogen.h)
83 set(AUTOGEN_PY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/${PLUGIN}.py)
84
85 file(READ "${PLUGIN_MIM}" plugin_file_contents)
86
87 # Strip block comments (/* ... */) — greedy, so repeat if needed
88 string(REGEX REPLACE "/\\*[^*]*\\*+([^/*][^*]*\\*+)*/" "" plugin_file_contents "${plugin_file_contents}")
89
90 # Replace all newlines with semicolons to help with list processing
91 string(REPLACE "\n" ";" plugin_lines "${plugin_file_contents}")
92
93 file(
94 MAKE_DIRECTORY
95 ${CMAKE_BINARY_DIR}/docs/plug/
96 ${CMAKE_BINARY_DIR}/include/mim/plug/${PLUGIN}
97 ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/
98 )
99
100 add_custom_command(
101 OUTPUT
102 ${AUTOGEN_H}
103 ${AUTOGEN_PY}
104 ${PLUGIN_MD}
105 COMMAND $<TARGET_FILE:${MIM_TARGET_NAMESPACE}mim> ${PLUGIN_MIM} -P "${CMAKE_SOURCE_DIR}/src/mim/plug" -P "${CMAKE_CURRENT_LIST_DIR}/.." --bootstrap
106 --output-h ${AUTOGEN_H}
107 --output-md ${PLUGIN_MD}
108 --output-py ${AUTOGEN_PY}
109 MAIN_DEPENDENCY ${PLUGIN_MIM}
110 DEPENDS ${MIM_TARGET_NAMESPACE}mim
111 COMMENT "Bootstrapping MimIR plugin '${PLUGIN_MIM}'"
112 VERBATIM
113 )
114 add_custom_command(
115 OUTPUT ${OUT_PLUGIN_MIM}
116 COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PLUGIN_MIM} ${OUT_PLUGIN_MIM}
117 DEPENDS ${PLUGIN_MIM}
118 COMMENT "Copy '${PLUGIN_MIM}' to '${OUT_PLUGIN_MIM}'"
119 )
120
121 add_custom_target(mim_internal_${PLUGIN}
122 DEPENDS
123 ${AUTOGEN_H}
124 ${AUTOGEN_PY}
125 ${PLUGIN_MD}
126 ${OUT_PLUGIN_MIM}
127 )
128
129 if(PLUGIN IN_LIST MIM_PLUGIN_LIST)
130 message(FATAL_ERROR "Mim plugin '${PLUGIN}' is already registered")
131 endif()
132
133 list(APPEND MIM_PLUGIN_LIST "${PLUGIN}")
134 string(APPEND MIM_PLUGIN_LAYOUT "<tab type=\"user\" url=\"@ref ${PLUGIN}\" title=\"${PLUGIN}\"/>")
135
136 # populate to globals
137 set(MIM_PLUGIN_LIST "${MIM_PLUGIN_LIST}" CACHE INTERNAL "MIM_PLUGIN_LIST")
138 set(MIM_PLUGIN_LAYOUT "${MIM_PLUGIN_LAYOUT}" CACHE INTERNAL "MIM_PLUGIN_LAYOUT")
139
140 #
141 # mim_plugin
142 #
143 add_library(mim_${PLUGIN} MODULE)
144 add_dependencies(mim_${PLUGIN}
145 mim_internal_${PLUGIN}
146 ${PLUGIN_SOFT_DEPS}
147 ${PLUGIN_HARD_DEPS}
148 )
149 target_sources(mim_${PLUGIN}
150 PRIVATE
151 ${PARSED_SOURCES}
152 )
153 target_include_directories(mim_${PLUGIN}
154 PUBLIC
155 $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include> # for autogen.h
156 )
157 if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include")
158 target_include_directories(mim_${PLUGIN}
159 PRIVATE
160 "${CMAKE_CURRENT_LIST_DIR}/include"
161 )
162 endif()
163 target_link_libraries(mim_${PLUGIN}
164 PRIVATE
165 ${PARSED_PRIVATE}
166 ${MIM_TARGET_NAMESPACE}libmim
167 )
168 set_target_properties(mim_${PLUGIN}
169 PROPERTIES
170 CXX_VISIBILITY_PRESET hidden
171 VISIBILITY_INLINES_HIDDEN 1
172 WINDOWS_EXPORT_ALL_SYMBOLS OFF
173 PREFIX "lib" # always use "lib" as prefix regardless of OS/compiler
174 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim
175 )
176
177 #
178 # install
179 #
180 if(${PARSED_INSTALL})
181 install(
182 TARGETS
183 mim_${PLUGIN}
184 EXPORT mim-targets
185 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
186 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
187 RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
188 INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mim
189 )
190 install(
191 FILES ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/${PLUGIN}.mim
192 DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
193 )
194 install(
195 FILES ${AUTOGEN_H}
196 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mim/plug/${PLUGIN}
197 )
198 if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include")
199 install(
200 DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/"
201 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
202 )
203 endif()
204 endif()
205endfunction()
206
207## \page add_mim_runtime_cmake add_mim_runtime
208## \brief Compiles a plugin's C runtime wrappers into a single LLVM IR module.
209##
210## \code{.cmake}
211## add_mim_runtime(<plugin-name>
212## SOURCES <source>...
213## [INSTALL])
214## \endcode
215##
216## All `SOURCES` are compiled with `clang` and merged into a single textual module
217## `<libdir>/mim/rt/<plugin-name>_rt.ll`, next to the plugins. A backend locates
218## that one file via the driver's search paths (see
219## \ref mim::plug::ll::Emitter::load_rt_module) and either embeds it into or links
220## it with its emitted module (see `-X <plugin-name>:rt=embed|extern`). Producing a
221## single module keeps the runtime addressable by one well-known name regardless of
222## how many source files it is split into.
223##
224## A single source is compiled directly to the module; multiple sources are compiled
225## to bitcode and merged with `llvm-link` (which must then be on `PATH`).
226##
227## If `clang` is unavailable (see `MIM_CLANG`) or `MIM_BUILD_LL_RUNTIME` is off,
228## the command is a no-op; the runtime is simply not built.
229function(add_mim_runtime)
230 set(PLUGIN ${ARGV0})
231
232 cmake_parse_arguments(
233 PARSE_ARGV 1 # skip first arg
234 PARSED # prefix of output variables
235 "INSTALL" # options
236 "" # one-value keywords (none)
237 "SOURCES" # multi-value keywords
238 )
239
240 if(NOT MIM_BUILD_LL_RUNTIME OR NOT MIM_CLANG)
241 return()
242 endif()
243
244 set(RT_DIR ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/rt)
245 file(MAKE_DIRECTORY ${RT_DIR})
246 set(RT_LL ${RT_DIR}/${PLUGIN}_rt.ll)
247
248 set(ABS_SOURCES "")
249 foreach(src ${PARSED_SOURCES})
250 list(APPEND ABS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/${src})
251 endforeach()
252
253 list(LENGTH ABS_SOURCES N_SOURCES)
254 if(N_SOURCES EQUAL 1)
255 add_custom_command(
256 OUTPUT ${RT_LL}
257 COMMAND ${MIM_CLANG} -S -emit-llvm -O2 ${ABS_SOURCES} -o ${RT_LL}
258 DEPENDS ${ABS_SOURCES}
259 COMMENT "Compiling MimIR runtime '${PLUGIN}' to LLVM IR"
260 VERBATIM
261 )
262 else()
263 find_program(MIM_LLVM_LINK NAMES llvm-link)
264 if(NOT MIM_LLVM_LINK)
265 message(FATAL_ERROR
266 "add_mim_runtime(${PLUGIN}) with multiple SOURCES needs llvm-link to merge them"
267 )
268 endif()
269 set(RT_BCS "")
270 foreach(src ${PARSED_SOURCES})
271 get_filename_component(stem ${src} NAME_WE)
272 set(bc ${RT_DIR}/${PLUGIN}_${stem}.bc)
273 add_custom_command(
274 OUTPUT ${bc}
275 COMMAND ${MIM_CLANG} -emit-llvm -O2 -c ${CMAKE_CURRENT_LIST_DIR}/${src} -o ${bc}
276 DEPENDS ${CMAKE_CURRENT_LIST_DIR}/${src}
277 COMMENT "Compiling MimIR runtime source '${src}' to LLVM bitcode"
278 VERBATIM
279 )
280 list(APPEND RT_BCS ${bc})
281 endforeach()
282 add_custom_command(
283 OUTPUT ${RT_LL}
284 COMMAND ${MIM_LLVM_LINK} -S ${RT_BCS} -o ${RT_LL}
285 DEPENDS ${RT_BCS}
286 COMMENT "Linking MimIR runtime '${PLUGIN}' into a single LLVM IR module"
287 VERBATIM
288 )
289 endif()
290
291 add_custom_target(mim_runtime_${PLUGIN} ALL DEPENDS ${RT_LL})
292 if(TARGET mim_${PLUGIN})
293 add_dependencies(mim_${PLUGIN} mim_runtime_${PLUGIN})
294 endif()
295 if(${PARSED_INSTALL})
296 install(
297 FILES ${RT_LL}
298 DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim/rt
299 )
300 endif()
301endfunction()