Skip to content

Commit 72f9881

Browse files
authored
[libclc] Refactor build system to allow in-tree builds (#87622)
The previous build system was adding custom "OpenCL" and "LLVM IR" languages in CMake to build the builtin libraries. This was making it harder to build in-tree because the tool binaries needed to be present at configure time. This commit refactors the build system to use custom commands to build the bytecode files one by one, and link them all together into the final bytecode library. It also enables in-tree builds by aliasing the clang/llvm-link/etc. tool targets to internal targets, which are imported from the LLVM installation directory when building out of tree. Diffing (with llvm-diff) all of the final bytecode libraries in an out-of-tree configuration against those built using the current tip system shows no changes. Note that there are textual changes to metadata IDs which confuse regular diff, and that llvm-diff 14 and below may show false-positives. This commit also removes a file listed in one of the SOURCEs which didn't exist and which was preventing the use of ENABLE_RUNTIME_SUBNORMAL when configuring CMake.
1 parent b63fe0d commit 72f9881

12 files changed

+322
-312
lines changed

libclc/CMakeLists.txt

Lines changed: 163 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ project( libclc VERSION 0.2.0 LANGUAGES CXX C)
44

55
set(CMAKE_CXX_STANDARD 17)
66

7+
# Add path for custom modules
8+
list( INSERT CMAKE_MODULE_PATH 0 "${PROJECT_SOURCE_DIR}/cmake/modules" )
9+
10+
set( LIBCLC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
11+
set( LIBCLC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} )
12+
set( LIBCLC_OBJFILE_DIR ${LIBCLC_BINARY_DIR}/obj.libclc.dir )
13+
14+
include( AddLibclc )
15+
716
include( GNUInstallDirs )
817
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
918
amdgcn-amdhsa/lib/SOURCES;
@@ -27,31 +36,51 @@ set( LIBCLC_TARGETS_TO_BUILD "all"
2736

2837
option( ENABLE_RUNTIME_SUBNORMAL "Enable runtime linking of subnormal support." OFF )
2938

30-
find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_DIR}")
31-
include(AddLLVM)
39+
if( LIBCLC_STANDALONE_BUILD OR CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
40+
# Out-of-tree configuration
41+
set( LIBCLC_STANDALONE_BUILD TRUE )
3242

33-
message( STATUS "libclc LLVM version: ${LLVM_PACKAGE_VERSION}" )
43+
find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_DIR}")
44+
include(AddLLVM)
3445

35-
if( LLVM_PACKAGE_VERSION VERSION_LESS LIBCLC_MIN_LLVM )
36-
message( FATAL_ERROR "libclc needs at least LLVM ${LIBCLC_MIN_LLVM}" )
37-
endif()
46+
message( STATUS "libclc LLVM version: ${LLVM_PACKAGE_VERSION}" )
3847

39-
find_program( LLVM_CLANG clang PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
40-
find_program( LLVM_AS llvm-as PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
41-
find_program( LLVM_LINK llvm-link PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
42-
find_program( LLVM_OPT opt PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
43-
find_program( LLVM_SPIRV llvm-spirv PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
48+
if( LLVM_PACKAGE_VERSION VERSION_LESS LIBCLC_MIN_LLVM )
49+
message( FATAL_ERROR "libclc needs at least LLVM ${LIBCLC_MIN_LLVM}" )
50+
endif()
51+
52+
# Import required tools as targets
53+
foreach( tool clang llvm-as llvm-link opt )
54+
find_program( LLVM_TOOL_${tool} ${tool} PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
55+
add_executable( libclc::${tool} IMPORTED GLOBAL )
56+
set_target_properties( libclc::${tool} PROPERTIES IMPORTED_LOCATION ${LLVM_TOOL_${tool}} )
57+
endforeach()
58+
else()
59+
# In-tree configuration
60+
set( LIBCLC_STANDALONE_BUILD FALSE )
61+
62+
set( LLVM_PACKAGE_VERSION ${LLVM_VERSION} )
4463

45-
# Print toolchain
46-
message( STATUS "libclc toolchain - clang: ${LLVM_CLANG}" )
47-
message( STATUS "libclc toolchain - llvm-as: ${LLVM_AS}" )
48-
message( STATUS "libclc toolchain - llvm-link: ${LLVM_LINK}" )
49-
message( STATUS "libclc toolchain - opt: ${LLVM_OPT}" )
50-
message( STATUS "libclc toolchain - llvm-spirv: ${LLVM_SPIRV}" )
51-
if( NOT LLVM_CLANG OR NOT LLVM_OPT OR NOT LLVM_AS OR NOT LLVM_LINK )
64+
# Note that we check this later (for both build types) but we can provide a
65+
# more useful error message when built in-tree. We assume that LLVM tools are
66+
# always available so don't warn here.
67+
if( NOT clang IN_LIST LLVM_ENABLE_PROJECTS )
68+
message(FATAL_ERROR "Clang is not enabled, but is required to build libclc in-tree")
69+
endif()
70+
71+
foreach( tool clang llvm-as llvm-link opt )
72+
add_executable(libclc::${tool} ALIAS ${tool})
73+
endforeach()
74+
endif()
75+
76+
if( NOT TARGET libclc::clang OR NOT TARGET libclc::opt
77+
OR NOT TARGET libclc::llvm-as OR NOT TARGET libclc::llvm-link )
5278
message( FATAL_ERROR "libclc toolchain incomplete!" )
5379
endif()
5480

81+
# llvm-spirv is an optional dependency, used to build spirv-* targets.
82+
find_program( LLVM_SPIRV llvm-spirv PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
83+
5584
# List of all targets. Note that some are added dynamically below.
5685
set( LIBCLC_TARGETS_ALL
5786
amdgcn--
@@ -90,24 +119,9 @@ if( "spirv-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD OR "spirv64-mesa3d-" IN_LIST
90119
endif()
91120
endif()
92121

93-
set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake )
94-
set( CMAKE_CLC_COMPILER ${LLVM_CLANG} )
95-
set( CMAKE_CLC_ARCHIVE ${LLVM_LINK} )
96-
set( CMAKE_LLAsm_PREPROCESSOR ${LLVM_CLANG} )
97-
set( CMAKE_LLAsm_COMPILER ${LLVM_AS} )
98-
set( CMAKE_LLAsm_ARCHIVE ${LLVM_LINK} )
99-
100122
# Construct LLVM version define
101123
set( LLVM_VERSION_DEFINE "-DHAVE_LLVM=0x${LLVM_VERSION_MAJOR}0${LLVM_VERSION_MINOR}" )
102124

103-
104-
# LLVM 13 enables standard includes by default
105-
if( LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 13.0.0 )
106-
set( CMAKE_LLAsm_FLAGS "${CMAKE_LLAsm_FLAGS} -cl-no-stdinc" )
107-
set( CMAKE_CLC_FLAGS "${CMAKE_CLC_FLAGS} -cl-no-stdinc" )
108-
endif()
109-
110-
enable_language( CLC LLAsm )
111125
# This needs to be set before any target that needs it
112126
# We need to use LLVM_INCLUDE_DIRS here, because if we are linking to an
113127
# llvm build directory, this includes $src/llvm/include which is where all the
@@ -122,7 +136,7 @@ set(LLVM_LINK_COMPONENTS
122136
IRReader
123137
Support
124138
)
125-
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
139+
if( LIBCLC_STANDALONE_BUILD )
126140
add_llvm_executable( prepare_builtins utils/prepare-builtins.cpp )
127141
else()
128142
add_llvm_utility( prepare_builtins utils/prepare-builtins.cpp )
@@ -167,12 +181,14 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libclc.pc DESTINATION "${CMAKE_INSTAL
167181
install( DIRECTORY generic/include/clc DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" )
168182

169183
if( ENABLE_RUNTIME_SUBNORMAL )
170-
add_library( subnormal_use_default STATIC
171-
generic/lib/subnormal_use_default.ll )
172-
add_library( subnormal_disable STATIC
173-
generic/lib/subnormal_disable.ll )
174-
install( TARGETS subnormal_use_default subnormal_disable ARCHIVE
175-
DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
184+
foreach( file subnormal_use_default subnormal_disable )
185+
link_bc(
186+
TARGET ${file}
187+
INPUTS ${PROJECT_SOURCE_DIR}/generic/lib/${file}.ll
188+
)
189+
install( FILES $<TARGET_PROPERTY:${file},TARGET_FILE> ARCHIVE
190+
DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
191+
endforeach()
176192
endif()
177193

178194
find_package( Python3 REQUIRED COMPONENTS Interpreter )
@@ -232,131 +248,164 @@ foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
232248

233249
# Add the generated convert.cl here to prevent adding the one listed in
234250
# SOURCES
251+
set( objects ) # A "set" of already-added input files
252+
set( rel_files ) # Source directory input files, relative to the root dir
253+
set( gen_files ) # Generated binary input files, relative to the binary dir
235254
if( NOT ${ARCH} STREQUAL "spirv" AND NOT ${ARCH} STREQUAL "spirv64" )
236255
if( NOT ENABLE_RUNTIME_SUBNORMAL AND NOT ${ARCH} STREQUAL "clspv" AND
237256
NOT ${ARCH} STREQUAL "clspv64" )
238-
set( rel_files convert.cl )
239-
set( objects convert.cl )
257+
list( APPEND gen_files convert.cl )
258+
list( APPEND objects convert.cl )
240259
list( APPEND rel_files generic/lib/subnormal_use_default.ll )
241260
elseif(${ARCH} STREQUAL "clspv" OR ${ARCH} STREQUAL "clspv64")
242-
set( rel_files clspv-convert.cl )
243-
set( objects clspv-convert.cl )
261+
list( APPEND gen_files clspv-convert.cl )
262+
list( APPEND objects clspv-convert.cl )
244263
endif()
245-
else()
246-
set( rel_files )
247-
set( objects )
248264
endif()
249265

250266
foreach( l ${source_list} )
251267
file( READ ${l} file_list )
252268
string( REPLACE "\n" ";" file_list ${file_list} )
253269
get_filename_component( dir ${l} DIRECTORY )
254270
foreach( f ${file_list} )
255-
list( FIND objects ${f} found )
256-
if( found EQUAL -1 )
271+
# Only add each file once, so that targets can 'specialize' builtins
272+
if( NOT ${f} IN_LIST objects )
257273
list( APPEND objects ${f} )
258274
list( APPEND rel_files ${dir}/${f} )
259-
# FIXME: This should really go away
260-
file( TO_CMAKE_PATH ${PROJECT_SOURCE_DIR}/${dir}/${f} src_loc )
261-
get_filename_component( fdir ${src_loc} DIRECTORY )
262-
263-
set_source_files_properties( ${dir}/${f}
264-
PROPERTIES COMPILE_FLAGS "-I ${fdir}" )
265275
endif()
266276
endforeach()
267277
endforeach()
268278

269279
foreach( d ${${t}_devices} )
270-
# Some targets don't have a specific GPU to target
271-
if( ${d} STREQUAL "none" OR ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
272-
set( mcpu )
273-
set( arch_suffix "${t}" )
274-
else()
275-
set( mcpu "-mcpu=${d}" )
276-
set( arch_suffix "${d}-${t}" )
280+
get_libclc_device_info(
281+
TRIPLE ${t}
282+
DEVICE ${d}
283+
CPU cpu
284+
ARCH_SUFFIX arch_suffix
285+
CLANG_TRIPLE clang_triple
286+
)
287+
288+
set( mcpu )
289+
if( NOT "${cpu}" STREQUAL "" )
290+
set( mcpu "-mcpu=${cpu}" )
277291
endif()
292+
278293
message( STATUS " device: ${d} ( ${${d}_aliases} )" )
279294

280-
if ( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
281-
if( ${ARCH} STREQUAL "spirv" )
282-
set( t "spir--" )
283-
else()
284-
set( t "spir64--" )
285-
endif()
295+
if ( ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 )
286296
set( build_flags -O0 -finline-hint-functions )
287297
set( opt_flags )
288298
set( spvflags --spirv-max-version=1.1 )
289-
elseif( ${ARCH} STREQUAL "clspv" )
290-
set( t "spir--" )
291-
set( build_flags "-Wno-unknown-assumption")
292-
set( opt_flags -O3 )
293-
elseif( ${ARCH} STREQUAL "clspv64" )
294-
set( t "spir64--" )
299+
elseif( ARCH STREQUAL clspv OR ARCH STREQUAL clspv64 )
295300
set( build_flags "-Wno-unknown-assumption")
296301
set( opt_flags -O3 )
297302
else()
298303
set( build_flags )
299304
set( opt_flags -O3 )
300305
endif()
301306

307+
set( LIBCLC_ARCH_OBJFILE_DIR "${LIBCLC_OBJFILE_DIR}/${arch_suffix}" )
308+
file( MAKE_DIRECTORY ${LIBCLC_ARCH_OBJFILE_DIR} )
309+
310+
string( TOUPPER "CLC_${ARCH}" CLC_TARGET_DEFINE )
311+
312+
list( APPEND build_flags
313+
-D__CLC_INTERNAL
314+
-D${CLC_TARGET_DEFINE}
315+
-I${PROJECT_SOURCE_DIR}/generic/include
316+
# FIXME: Fix libclc to not require disabling this noisy warning
317+
-Wno-bitwise-conditional-parentheses
318+
)
319+
320+
set( bytecode_files "" )
321+
foreach( file IN LISTS gen_files rel_files )
322+
# We need to take each file and produce an absolute input file, as well
323+
# as a unique architecture-specific output file. We deal with a mix of
324+
# different input files, which makes this trickier.
325+
if( ${file} IN_LIST gen_files )
326+
# Generated files are given just as file names, which we must make
327+
# absolute to the binary directory.
328+
set( input_file ${CMAKE_CURRENT_BINARY_DIR}/${file} )
329+
set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${file}.o" )
330+
else()
331+
# Other files are originally relative to each SOURCE file, which are
332+
# then make relative to the libclc root directory. We must normalize
333+
# the path (e.g., ironing out any ".."), then make it relative to the
334+
# root directory again, and use that relative path component for the
335+
# binary path.
336+
get_filename_component( abs_path ${file} ABSOLUTE BASE_DIR ${PROJECT_SOURCE_DIR} )
337+
file( RELATIVE_PATH root_rel_path ${PROJECT_SOURCE_DIR} ${abs_path} )
338+
set( input_file ${PROJECT_SOURCE_DIR}/${file} )
339+
set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${root_rel_path}.o" )
340+
endif()
341+
342+
get_filename_component( file_dir ${file} DIRECTORY )
343+
344+
compile_to_bc(
345+
TRIPLE ${clang_triple}
346+
INPUT ${input_file}
347+
OUTPUT ${output_file}
348+
EXTRA_OPTS "${mcpu}" -fno-builtin -nostdlib
349+
"${build_flags}" -I${PROJECT_SOURCE_DIR}/${file_dir}
350+
)
351+
list( APPEND bytecode_files ${output_file} )
352+
endforeach()
353+
302354
set( builtins_link_lib_tgt builtins.link.${arch_suffix} )
303-
add_library( ${builtins_link_lib_tgt} STATIC ${rel_files} )
304-
# Make sure we depend on the pseudo target to prevent
305-
# multiple invocations
306-
add_dependencies( ${builtins_link_lib_tgt} generate_convert.cl )
307-
add_dependencies( ${builtins_link_lib_tgt} clspv-generate_convert.cl )
308-
# CMake will turn this include into absolute path
309-
target_include_directories( ${builtins_link_lib_tgt} PRIVATE
310-
"generic/include" )
311-
target_compile_definitions( ${builtins_link_lib_tgt} PRIVATE
312-
"__CLC_INTERNAL" )
313-
string( TOUPPER "-DCLC_${ARCH}" CLC_TARGET_DEFINE )
314-
target_compile_definitions( ${builtins_link_lib_tgt} PRIVATE
315-
${CLC_TARGET_DEFINE} )
316-
target_compile_options( ${builtins_link_lib_tgt} PRIVATE -target
317-
${t} ${mcpu} -fno-builtin -nostdlib ${build_flags} )
318-
set_target_properties( ${builtins_link_lib_tgt} PROPERTIES
319-
LINKER_LANGUAGE CLC )
320-
321-
set( obj_suffix ${arch_suffix}.bc )
322-
set( builtins_opt_lib_tgt builtins.opt.${obj_suffix} )
323-
324-
# Add opt target
325-
add_custom_command( OUTPUT ${builtins_opt_lib_tgt}
326-
COMMAND ${LLVM_OPT} ${opt_flags} -o ${builtins_opt_lib_tgt}
327-
$<TARGET_FILE:${builtins_link_lib_tgt}>
328-
DEPENDS ${builtins_link_lib_tgt} )
329-
add_custom_target( "opt.${obj_suffix}" ALL
330-
DEPENDS ${builtins_opt_lib_tgt} )
331-
332-
if( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
355+
356+
link_bc(
357+
TARGET ${builtins_link_lib_tgt}
358+
INPUTS ${bytecode_files}
359+
)
360+
361+
set( builtins_link_lib $<TARGET_PROPERTY:${builtins_link_lib_tgt},TARGET_FILE> )
362+
363+
if( ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 )
333364
set( spv_suffix ${arch_suffix}.spv )
334-
add_custom_command( OUTPUT "${spv_suffix}"
335-
COMMAND ${LLVM_SPIRV} ${spvflags} -o "${spv_suffix}" $<TARGET_FILE:${builtins_link_lib_tgt}>
336-
DEPENDS ${builtins_link_lib_tgt} )
365+
add_custom_command( OUTPUT ${spv_suffix}
366+
COMMAND ${LLVM_SPIRV} ${spvflags} -o ${spv_suffix} ${builtins_link_lib}
367+
DEPENDS ${builtins_link_lib_tgt}
368+
)
337369
add_custom_target( "prepare-${spv_suffix}" ALL DEPENDS "${spv_suffix}" )
338370
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${spv_suffix}
339371
DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
340372
else()
373+
set( builtins_opt_lib_tgt builtins.opt.${arch_suffix} )
374+
375+
# Add opt target
376+
add_custom_command( OUTPUT ${builtins_opt_lib_tgt}.bc
377+
COMMAND libclc::opt ${opt_flags} -o ${builtins_opt_lib_tgt}.bc
378+
${builtins_link_lib}
379+
DEPENDS libclc::opt ${builtins_link_lib_tgt}
380+
)
381+
add_custom_target( ${builtins_opt_lib_tgt}
382+
ALL DEPENDS ${builtins_opt_lib_tgt}.bc
383+
)
384+
set_target_properties( ${builtins_opt_lib_tgt}
385+
PROPERTIES TARGET_FILE ${builtins_opt_lib_tgt}.bc
386+
)
387+
341388
# Add prepare target
342-
add_custom_command( OUTPUT "${obj_suffix}"
343-
COMMAND prepare_builtins -o "${obj_suffix}" ${builtins_opt_lib_tgt}
344-
DEPENDS "opt.${obj_suffix}" ${builtins_opt_lib_tgt} prepare_builtins )
345-
add_custom_target( "prepare-${obj_suffix}" ALL DEPENDS "${obj_suffix}" )
389+
set( obj_suffix ${arch_suffix}.bc )
390+
add_custom_command( OUTPUT ${obj_suffix}
391+
COMMAND prepare_builtins -o ${obj_suffix}
392+
$<TARGET_PROPERTY:${builtins_opt_lib_tgt},TARGET_FILE>
393+
DEPENDS ${builtins_opt_lib_tgt} prepare_builtins )
394+
add_custom_target( prepare-${obj_suffix} ALL DEPENDS ${obj_suffix} )
346395

347396
# nvptx-- targets don't include workitem builtins
348-
if( NOT ${t} MATCHES ".*ptx.*--$" )
397+
if( NOT clang_triple MATCHES ".*ptx.*--$" )
349398
add_test( NAME external-calls-${obj_suffix}
350399
COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} ${LLVM_TOOLS_BINARY_DIR}
351400
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} )
352401
endif()
353402

354403
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
355404
foreach( a ${${d}_aliases} )
356-
set( alias_suffix "${a}-${t}.bc" )
405+
set( alias_suffix "${a}-${clang_triple}.bc" )
357406
add_custom_target( ${alias_suffix} ALL
358407
COMMAND ${CMAKE_COMMAND} -E create_symlink ${obj_suffix} ${alias_suffix}
359-
DEPENDS "prepare-${obj_suffix}" )
408+
DEPENDS prepare-${obj_suffix} )
360409
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${alias_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
361410
endforeach( a )
362411
endif()

libclc/cmake/CMakeCLCCompiler.cmake.in

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)