MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
Mim.cmake
Go to the documentation of this file.
1include(GNUInstallDirs)
2
3option(MIM_BUILD_LL_RUNTIME "Compile the ll backend's C runtime wrappers to LLVM IR (requires clang)." ON)
4find_program(MIM_CLANG NAMES clang)
5if(MIM_BUILD_LL_RUNTIME AND NOT MIM_CLANG)
6 message(STATUS
7 "MimIR: clang not found; ll backend C runtime wrappers will not be built. "
8 "Set MIM_CLANG or disable MIM_BUILD_LL_RUNTIME to silence this."
9 )
10endif()
11
12# Collects the .mim files that bootstrapping mim_file reads, transitively.
13# A plugin/import directive resolves to <root>/<name>/<name>.mim, mirroring add_mim_plugin's -P arguments.
14# A newly added directive is only picked up on the next configure; editing an existing one is tracked.
15function(mim_transitive_imports mim_file roots out)
16 set(pending "${mim_file}")
17 set(files "")
18
19 while(pending)
20 list(POP_FRONT pending current)
21 file(READ "${current}" contents)
22 string(REGEX REPLACE "/\\*[^*]*\\*+([^/*][^*]*\\*+)*/" "" contents "${contents}")
23 string(REGEX REPLACE "//[^\n]*" "" contents "${contents}")
24 string(REGEX MATCHALL "(plugin|import)[ \t\r\n]+[A-Za-z0-9_]+" directives "${contents}")
25
26 foreach(directive IN LISTS directives)
27 string(REGEX REPLACE "^(plugin|import)[ \t\r\n]+" "" name "${directive}")
28 foreach(root IN LISTS roots)
29 set(candidate "${root}/${name}/${name}.mim")
30 if(EXISTS "${candidate}")
31 if(NOT candidate IN_LIST files)
32 list(APPEND files "${candidate}")
33 list(APPEND pending "${candidate}")
34 endif()
35 break()
36 endif()
37 endforeach()
38 endforeach()
39 endwhile()
40
41 set(${out} "${files}" PARENT_SCOPE)
42endfunction()
43
44## \page add_mim_plugin_cmake add_mim_plugin
45## \brief Registers a new MimIR plugin.
46##
47## Relative to the plugin's `CMakeLists.txt`, there should be a file
48## `<plugin-name>.mim` containing the plugin's annexes.
49##
50## \code{.cmake}
51## add_mim_plugin(<plugin-name>
52## [SOURCES <source>...]
53## [PRIVATE <private-item>...])
54## \endcode
55##
56## `<plugin-name>` may only contain letters, digits, and underscores, and must
57## satisfy MimIR's plugin naming constraints.
58##
59## The command creates two targets:
60## - `mim_internal_<plugin-name>` bootstraps the plugin, generates
61## `<plugin-name>/autogen.h` for the C++ interface used to identify annexes,
62## `<plugin-name>.md` for the documentation, and copies `<plugin-name>.mim`
63## into the build tree.
64## - `mim_<plugin-name>` builds the actual `MODULE` library that contains
65## normalizers, passes, and backends. One of the listed source files must
66## export `mim_get_plugin`.
67##
68## `SOURCES` lists the source files that are compiled into the loadable plugin.
69## `PRIVATE` lists additional private link dependencies.
70##
71## The plugin module, its `.mim` file, and the generated `autogen.h` are installed.
72## The export name `mim-targets` must be exported accordingly; see CMake's
73## `install(EXPORT ...)` documentation.
74##
75## Additional target properties can be set afterwards, for example:
76## \code{.cmake}
77## target_include_directories(mim_<plugin-name> <path>...)
78## \endcode
79function(add_mim_plugin)
80 set(PLUGIN ${ARGV0})
81
82 string(LENGTH "${PLUGIN}" PLUGIN_LENGTH)
83 if(NOT PLUGIN MATCHES "^[A-Za-z0-9_]+$" OR PLUGIN_LENGTH GREATER 8)
84 message(FATAL_ERROR
85 "Mim plugin name '${PLUGIN}' must be 1 to 8 letters, digits, or underscores"
86 )
87 endif()
88
89 cmake_parse_arguments(
90 PARSE_ARGV 1 # skip first arg
91 PARSED # prefix of output variables
92 "" # options (none)
93 "" # one-value keywords (none)
94 "SOURCES;PRIVATE" # multi-value keywords
95 )
96
97 set(PLUGIN_MIM ${CMAKE_CURRENT_LIST_DIR}/${PLUGIN}.mim)
98 set(OUT_PLUGIN_MIM ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/${PLUGIN}.mim)
99 set(PLUGIN_MD ${CMAKE_BINARY_DIR}/docs/plug/${PLUGIN}.md)
100 set(AUTOGEN_H ${CMAKE_BINARY_DIR}/include/mim/plug/${PLUGIN}/autogen.h)
101 set(AUTOGEN_PY ${CMAKE_BINARY_DIR}/py/mim/_plugins/${PLUGIN}.py)
102
103 file(
104 MAKE_DIRECTORY
105 ${CMAKE_BINARY_DIR}/docs/plug/
106 ${CMAKE_BINARY_DIR}/include/mim/plug/${PLUGIN}
107 ${CMAKE_BINARY_DIR}/py/mim/_plugins/
108 ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim/
109 )
110
111 set(PLUGIN_ROOTS "${CMAKE_SOURCE_DIR}/src/mim/plug" "${CMAKE_CURRENT_LIST_DIR}/..")
112 mim_transitive_imports("${PLUGIN_MIM}" "${PLUGIN_ROOTS}" PLUGIN_IMPORTS)
113
114 add_custom_command(
115 OUTPUT
116 ${AUTOGEN_H}
117 ${AUTOGEN_PY}
118 ${PLUGIN_MD}
119 COMMAND $<TARGET_FILE:${MIM_TARGET_NAMESPACE}mim> ${PLUGIN_MIM} -I "${CMAKE_SOURCE_DIR}/src/mim/plug" -I "${CMAKE_CURRENT_LIST_DIR}/.." --bootstrap
120 --output-h ${AUTOGEN_H}
121 --output-md ${PLUGIN_MD}
122 --output-py ${AUTOGEN_PY}
123 MAIN_DEPENDENCY ${PLUGIN_MIM}
124 DEPENDS ${MIM_TARGET_NAMESPACE}mim ${PLUGIN_IMPORTS}
125 COMMENT "Bootstrapping MimIR plugin '${PLUGIN_MIM}'"
126 VERBATIM
127 )
128 add_custom_command(
129 OUTPUT ${OUT_PLUGIN_MIM}
130 COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PLUGIN_MIM} ${OUT_PLUGIN_MIM}
131 DEPENDS ${PLUGIN_MIM}
132 COMMENT "Copy '${PLUGIN_MIM}' to '${OUT_PLUGIN_MIM}'"
133 )
134
135 add_custom_target(mim_internal_${PLUGIN}
136 DEPENDS
137 ${AUTOGEN_H}
138 ${AUTOGEN_PY}
139 ${PLUGIN_MD}
140 ${OUT_PLUGIN_MIM}
141 )
142
143 if(PLUGIN IN_LIST MIM_PLUGIN_LIST)
144 message(FATAL_ERROR "Mim plugin '${PLUGIN}' is already registered")
145 endif()
146
147 list(APPEND MIM_PLUGIN_LIST "${PLUGIN}")
148 string(APPEND MIM_PLUGIN_LAYOUT "<tab type=\"user\" url=\"@ref ${PLUGIN}\" title=\"${PLUGIN}\"/>")
149
150 set(MIM_PLUGIN_LIST "${MIM_PLUGIN_LIST}" CACHE INTERNAL "MIM_PLUGIN_LIST")
151 set(MIM_PLUGIN_LAYOUT "${MIM_PLUGIN_LAYOUT}" CACHE INTERNAL "MIM_PLUGIN_LAYOUT")
152
153 add_library(mim_${PLUGIN} MODULE)
154 add_dependencies(mim_${PLUGIN} mim_internal_${PLUGIN})
155 target_sources(mim_${PLUGIN}
156 PRIVATE
157 ${PARSED_SOURCES}
158 )
159 target_include_directories(mim_${PLUGIN}
160 PUBLIC
161 $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include> # for autogen.h
162 )
163 if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include")
164 target_include_directories(mim_${PLUGIN}
165 PRIVATE
166 "${CMAKE_CURRENT_LIST_DIR}/include"
167 )
168 endif()
169 target_link_libraries(mim_${PLUGIN}
170 PRIVATE
171 ${PARSED_PRIVATE}
172 ${MIM_TARGET_NAMESPACE}libmim
173 )
174 set_target_properties(mim_${PLUGIN}
175 PROPERTIES
176 CXX_VISIBILITY_PRESET hidden
177 VISIBILITY_INLINES_HIDDEN 1
178 WINDOWS_EXPORT_ALL_SYMBOLS OFF
179 PREFIX "lib" # always use "lib" as prefix regardless of OS/compiler
180 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/mim
181 )
182
183 install(
184 TARGETS
185 mim_${PLUGIN}
186 EXPORT mim-targets
187 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
188 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
189 RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
190 INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mim
191 )
192 install(
193 FILES ${OUT_PLUGIN_MIM}
194 DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim
195 )
196 install(
197 FILES ${AUTOGEN_H}
198 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mim/plug/${PLUGIN}
199 )
200 if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include")
201 install(
202 DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/"
203 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
204 )
205 endif()
206endfunction()
207
208## \page add_mim_runtime_cmake add_mim_runtime
209## \brief Compiles a plugin's C runtime wrappers into a single LLVM IR module.
210##
211## \code{.cmake}
212## add_mim_runtime(<plugin-name>
213## SOURCES <source>...)
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 "" # options (none)
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(RT_SOURCES ${PARSED_SOURCES})
249 list(TRANSFORM RT_SOURCES PREPEND ${CMAKE_CURRENT_LIST_DIR}/)
250
251 list(LENGTH RT_SOURCES N_SOURCES)
252 if(N_SOURCES EQUAL 1)
253 add_custom_command(
254 OUTPUT ${RT_LL}
255 COMMAND ${MIM_CLANG} -S -emit-llvm -O2 ${RT_SOURCES} -o ${RT_LL}
256 DEPENDS ${RT_SOURCES}
257 COMMENT "Compiling MimIR runtime '${PLUGIN}' to LLVM IR"
258 VERBATIM
259 )
260 else()
261 find_program(MIM_LLVM_LINK NAMES llvm-link)
262 if(NOT MIM_LLVM_LINK)
263 message(FATAL_ERROR
264 "add_mim_runtime(${PLUGIN}) with multiple SOURCES needs llvm-link to merge them"
265 )
266 endif()
267 set(RT_BCS "")
268 foreach(src IN LISTS RT_SOURCES)
269 cmake_path(GET src STEM stem)
270 set(bc ${RT_DIR}/${PLUGIN}_${stem}.bc)
271 add_custom_command(
272 OUTPUT ${bc}
273 COMMAND ${MIM_CLANG} -emit-llvm -O2 -c ${src} -o ${bc}
274 DEPENDS ${src}
275 COMMENT "Compiling MimIR runtime source '${stem}' to LLVM bitcode"
276 VERBATIM
277 )
278 list(APPEND RT_BCS ${bc})
279 endforeach()
280 add_custom_command(
281 OUTPUT ${RT_LL}
282 COMMAND ${MIM_LLVM_LINK} -S ${RT_BCS} -o ${RT_LL}
283 DEPENDS ${RT_BCS}
284 COMMENT "Linking MimIR runtime '${PLUGIN}' into a single LLVM IR module"
285 VERBATIM
286 )
287 endif()
288
289 add_custom_target(mim_runtime_${PLUGIN} ALL DEPENDS ${RT_LL})
290 if(TARGET mim_${PLUGIN})
291 add_dependencies(mim_${PLUGIN} mim_runtime_${PLUGIN})
292 endif()
293 install(
294 FILES ${RT_LL}
295 DESTINATION ${CMAKE_INSTALL_LIBDIR}/mim/rt
296 )
297endfunction()