Skip to content

Commit 2e78b03

Browse files
committed
[libclc] Refactor build system to allow in-tree builds
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 338ecfb commit 2e78b03

12 files changed

+296
-293
lines changed

libclc/CMakeLists.txt

Lines changed: 140 additions & 95 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(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 )
4461

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 )
62+
set( LLVM_PACKAGE_VERSION ${LLVM_VERSION} )
63+
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
@@ -163,12 +177,15 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libclc.pc DESTINATION "${CMAKE_INSTAL
163177
install( DIRECTORY generic/include/clc DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" )
164178

165179
if( ENABLE_RUNTIME_SUBNORMAL )
166-
add_library( subnormal_use_default STATIC
167-
generic/lib/subnormal_use_default.ll )
168-
add_library( subnormal_disable STATIC
169-
generic/lib/subnormal_disable.ll )
170-
install( TARGETS subnormal_use_default subnormal_disable ARCHIVE
171-
DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
180+
foreach( file subnormal_use_default subnormal_disable )
181+
link_bc(
182+
OUTPUT ${LIBCLC_OBJFILE_DIR}/${file}.bc
183+
INPUTS ${PROJECT_SOURCE_DIR}/generic/lib/${file}.ll
184+
)
185+
add_custom_target( ${file}.bc ALL DEPENDS ${LIBCLC_OBJFILE_DIR}/${file}.bc )
186+
install( FILES ${LIBCLC_OBJFILE_DIR}/${file}.bc ARCHIVE
187+
DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
188+
endforeach()
172189
endif()
173190

174191
find_package( Python3 REQUIRED COMPONENTS Interpreter )
@@ -228,99 +245,123 @@ foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
228245

229246
# Add the generated convert.cl here to prevent adding the one listed in
230247
# SOURCES
248+
set( objects ) # A "set" of already-added input files
249+
set( rel_files ) # Source directory input files, relative to the root dir
250+
set( gen_files ) # Generated binary input files, relative to the binary dir
231251
if( NOT ${ARCH} STREQUAL "spirv" AND NOT ${ARCH} STREQUAL "spirv64" )
232252
if( NOT ENABLE_RUNTIME_SUBNORMAL AND NOT ${ARCH} STREQUAL "clspv" AND
233253
NOT ${ARCH} STREQUAL "clspv64" )
234-
set( rel_files convert.cl )
235-
set( objects convert.cl )
254+
list( APPEND gen_files convert.cl )
255+
list( APPEND objects convert.cl )
236256
list( APPEND rel_files generic/lib/subnormal_use_default.ll )
237257
elseif(${ARCH} STREQUAL "clspv" OR ${ARCH} STREQUAL "clspv64")
238-
set( rel_files clspv-convert.cl )
239-
set( objects clspv-convert.cl )
258+
list( APPEND gen_files clspv-convert.cl )
259+
list( APPEND objects clspv-convert.cl )
240260
endif()
241-
else()
242-
set( rel_files )
243-
set( objects )
244261
endif()
245262

246263
foreach( l ${source_list} )
247264
file( READ ${l} file_list )
248265
string( REPLACE "\n" ";" file_list ${file_list} )
249266
get_filename_component( dir ${l} DIRECTORY )
250267
foreach( f ${file_list} )
251-
list( FIND objects ${f} found )
252-
if( found EQUAL -1 )
268+
# Only add each file once, so that targets can 'specialize' builtins
269+
if( NOT ${f} IN_LIST objects )
253270
list( APPEND objects ${f} )
254271
list( APPEND rel_files ${dir}/${f} )
255-
# FIXME: This should really go away
256-
file( TO_CMAKE_PATH ${PROJECT_SOURCE_DIR}/${dir}/${f} src_loc )
257-
get_filename_component( fdir ${src_loc} DIRECTORY )
258-
259-
set_source_files_properties( ${dir}/${f}
260-
PROPERTIES COMPILE_FLAGS "-I ${fdir}" )
261272
endif()
262273
endforeach()
263274
endforeach()
264275

265276
foreach( d ${${t}_devices} )
266-
# Some targets don't have a specific GPU to target
267-
if( ${d} STREQUAL "none" OR ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
268-
set( mcpu )
269-
set( arch_suffix "${t}" )
270-
else()
271-
set( mcpu "-mcpu=${d}" )
272-
set( arch_suffix "${d}-${t}" )
277+
get_libclc_device_info(
278+
TRIPLE ${t}
279+
DEVICE ${d}
280+
CPU cpu
281+
ARCH_SUFFIX arch_suffix
282+
CLANG_TRIPLE clang_triple
283+
)
284+
285+
set( mcpu )
286+
if( NOT "${cpu}" STREQUAL "" )
287+
set( mcpu "-mcpu=${cpu}" )
273288
endif()
289+
274290
message( STATUS " device: ${d} ( ${${d}_aliases} )" )
275291

276-
if ( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
277-
if( ${ARCH} STREQUAL "spirv" )
278-
set( t "spir--" )
279-
else()
280-
set( t "spir64--" )
281-
endif()
292+
if ( ARCH STREQUAL spirv OR ARCH STREQUAL spirv64 )
282293
set( build_flags -O0 -finline-hint-functions )
283294
set( opt_flags )
284295
set( spvflags --spirv-max-version=1.1 )
285-
elseif( ${ARCH} STREQUAL "clspv" )
286-
set( t "spir--" )
287-
set( build_flags "-Wno-unknown-assumption")
288-
set( opt_flags -O3 )
289-
elseif( ${ARCH} STREQUAL "clspv64" )
290-
set( t "spir64--" )
296+
elseif( ARCH STREQUAL clspv OR ARCH STREQUAL clspv64 )
291297
set( build_flags "-Wno-unknown-assumption")
292298
set( opt_flags -O3 )
293299
else()
294300
set( build_flags )
295301
set( opt_flags -O3 )
296302
endif()
297303

298-
set( builtins_link_lib_tgt builtins.link.${arch_suffix} )
299-
add_library( ${builtins_link_lib_tgt} STATIC ${rel_files} )
300-
# Make sure we depend on the pseudo target to prevent
301-
# multiple invocations
302-
add_dependencies( ${builtins_link_lib_tgt} generate_convert.cl )
303-
add_dependencies( ${builtins_link_lib_tgt} clspv-generate_convert.cl )
304-
# CMake will turn this include into absolute path
305-
target_include_directories( ${builtins_link_lib_tgt} PRIVATE
306-
"generic/include" )
307-
target_compile_definitions( ${builtins_link_lib_tgt} PRIVATE
308-
"__CLC_INTERNAL" )
309-
string( TOUPPER "-DCLC_${ARCH}" CLC_TARGET_DEFINE )
310-
target_compile_definitions( ${builtins_link_lib_tgt} PRIVATE
311-
${CLC_TARGET_DEFINE} )
312-
target_compile_options( ${builtins_link_lib_tgt} PRIVATE -target
313-
${t} ${mcpu} -fno-builtin -nostdlib ${build_flags} )
314-
set_target_properties( ${builtins_link_lib_tgt} PROPERTIES
315-
LINKER_LANGUAGE CLC )
304+
set( LIBCLC_ARCH_OBJFILE_DIR "${LIBCLC_OBJFILE_DIR}/${arch_suffix}" )
305+
file( MAKE_DIRECTORY ${LIBCLC_ARCH_OBJFILE_DIR} )
306+
307+
string( TOUPPER "CLC_${ARCH}" CLC_TARGET_DEFINE )
308+
309+
list( APPEND build_flags
310+
-D__CLC_INTERNAL
311+
-D${CLC_TARGET_DEFINE}
312+
-I${PROJECT_SOURCE_DIR}/generic/include
313+
# FIXME: Fix libclc to not require disabling this noisy warnings
314+
-Wno-bitwise-conditional-parentheses
315+
)
316+
317+
set( bytecode_files "" )
318+
foreach( file IN LISTS gen_files rel_files )
319+
# We need to take each file and produce an absolute input file, as well
320+
# as a unique architecture-specific output file. We deal with a mix of
321+
# different input files, which makes this trickier.
322+
if( ${file} IN_LIST gen_files )
323+
# Generated files are given just as file names, which we must make
324+
# absolute to the binary directory.
325+
set( input_file ${CMAKE_CURRENT_BINARY_DIR}/${file} )
326+
set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${file}.o" )
327+
else()
328+
# Other files are originally relative to each SOURCE file, which are
329+
# then make relative to the libclc root directory. We must normalize
330+
# the path (e.g., ironing out any ".."), then make it relative to the
331+
# root directory again, and use that relative path component for the
332+
# binary path.
333+
get_filename_component( abs_path ${file} ABSOLUTE BASE_DIR ${PROJECT_SOURCE_DIR} )
334+
file( RELATIVE_PATH root_rel_path ${PROJECT_SOURCE_DIR} ${abs_path} )
335+
set( input_file ${PROJECT_SOURCE_DIR}/${file} )
336+
set( output_file "${LIBCLC_ARCH_OBJFILE_DIR}/${root_rel_path}.o" )
337+
endif()
338+
339+
get_filename_component( file_dir ${file} DIRECTORY )
340+
341+
compile_to_bc(
342+
TRIPLE ${clang_triple}
343+
INPUT ${input_file}
344+
OUTPUT ${output_file}
345+
EXTRA_OPTS "${mcpu}" -fno-builtin -nostdlib
346+
"${build_flags}" -I${PROJECT_SOURCE_DIR}/${file_dir}
347+
)
348+
list(APPEND bytecode_files ${output_file})
349+
endforeach()
350+
351+
set( builtins_link_lib_tgt builtins.link.${arch_suffix}.bc )
352+
353+
link_bc(
354+
OUTPUT ${builtins_link_lib_tgt}
355+
INPUTS ${bytecode_files}
356+
)
316357

317358
set( obj_suffix ${arch_suffix}.bc )
318359
set( builtins_opt_lib_tgt builtins.opt.${obj_suffix} )
319360

320361
# Add opt target
321362
add_custom_command( OUTPUT ${builtins_opt_lib_tgt}
322-
COMMAND ${LLVM_OPT} ${opt_flags} -o ${builtins_opt_lib_tgt}
323-
$<TARGET_FILE:${builtins_link_lib_tgt}>
363+
COMMAND libclc::opt ${opt_flags} -o ${builtins_opt_lib_tgt}
364+
${builtins_link_lib_tgt}
324365
DEPENDS ${builtins_link_lib_tgt} )
325366
add_custom_target( "opt.${obj_suffix}" ALL
326367
DEPENDS ${builtins_opt_lib_tgt} )
@@ -341,15 +382,15 @@ foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
341382
add_custom_target( "prepare-${obj_suffix}" ALL DEPENDS "${obj_suffix}" )
342383

343384
# nvptx-- targets don't include workitem builtins
344-
if( NOT ${t} MATCHES ".*ptx.*--$" )
385+
if( NOT ${clang_triple} MATCHES ".*ptx.*--$" )
345386
add_test( NAME external-calls-${obj_suffix}
346387
COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} ${LLVM_TOOLS_BINARY_DIR}
347388
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} )
348389
endif()
349390

350391
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
351392
foreach( a ${${d}_aliases} )
352-
set( alias_suffix "${a}-${t}.bc" )
393+
set( alias_suffix "${a}-${clang_triple}.bc" )
353394
add_custom_target( ${alias_suffix} ALL
354395
COMMAND ${CMAKE_COMMAND} -E create_symlink ${obj_suffix} ${alias_suffix}
355396
DEPENDS "prepare-${obj_suffix}" )
@@ -358,3 +399,7 @@ foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
358399
endif()
359400
endforeach( d )
360401
endforeach( t )
402+
403+
if( NOT LIBCLC_STANDALONE_BUILD )
404+
add_subdirectory( test )
405+
endif()

libclc/cmake/CMakeCLCCompiler.cmake.in

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

libclc/cmake/CMakeCLCInformation.cmake

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

libclc/cmake/CMakeDetermineCLCCompiler.cmake

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

0 commit comments

Comments
 (0)