1cmake_minimum_required(VERSION 3.13.4)
2
3project( libclc VERSION 0.2.0 LANGUAGES CXX )
4include( GNUInstallDirs )
5set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
6  amdgcn-amdhsa/lib/SOURCES;
7  amdgcn/lib/SOURCES;
8  amdgcn-mesa3d/lib/SOURCES;
9  amdgpu/lib/SOURCES;
10  generic/lib/SOURCES;
11  ptx/lib/SOURCES;
12  ptx-nvidiacl/lib/SOURCES;
13  r600/lib/SOURCES;
14  spirv/lib/SOURCES;
15  spirv64/lib/SOURCES
16)
17
18# List of all targets
19set( LIBCLC_TARGETS_ALL
20  amdgcn--
21  amdgcn--amdhsa
22  r600--
23  nvptx--
24  nvptx64--
25  nvptx--nvidiacl
26  nvptx64--nvidiacl
27  spirv-mesa3d-
28  spirv64-mesa3d-
29)
30
31set( LIBCLC_MIN_LLVM "3.9.0" )
32
33set( LIBCLC_TARGETS_TO_BUILD "all"
34    CACHE STRING "Semicolon-separated list of targets to build, or 'all'." )
35
36option( ENABLE_RUNTIME_SUBNORMAL "Enable runtime linking of subnormal support."
37OFF )
38
39if( NOT LLVM_CONFIG )
40	find_program( LLVM_CONFIG llvm-config )
41endif()
42execute_process( COMMAND ${LLVM_CONFIG} "--version"
43	         OUTPUT_VARIABLE LLVM_VERSION
44		 OUTPUT_STRIP_TRAILING_WHITESPACE )
45message( "LLVM version: ${LLVM_VERSION}" )
46
47if( ${LLVM_VERSION} VERSION_LESS ${LIBCLC_MIN_LLVM} )
48	message( FATAL_ERROR "libclc needs at least LLVM ${LIBCLC_MIN_LLVM}" )
49endif()
50
51# mesa3d environment is only available since LLVM 4.0
52if( ${LLVM_VERSION} VERSION_GREATER "3.9.0" )
53	set( LIBCLC_TARGETS_ALL ${LIBCLC_TARGETS_ALL} amdgcn-mesa-mesa3d )
54endif()
55
56if( LIBCLC_TARGETS_TO_BUILD STREQUAL "all" )
57	set( LIBCLC_TARGETS_TO_BUILD ${LIBCLC_TARGETS_ALL} )
58endif()
59
60execute_process( COMMAND ${LLVM_CONFIG} "--system-libs"
61	OUTPUT_VARIABLE LLVM_SYSTEM_LIBS
62	OUTPUT_STRIP_TRAILING_WHITESPACE )
63separate_arguments( LLVM_SYSTEM_LIBS )
64execute_process( COMMAND ${LLVM_CONFIG} "--libs" "core" "bitreader" "bitwriter"
65	OUTPUT_VARIABLE LLVM_LIBS
66	OUTPUT_STRIP_TRAILING_WHITESPACE )
67separate_arguments( LLVM_LIBS )
68execute_process( COMMAND ${LLVM_CONFIG} "--libdir"
69	OUTPUT_VARIABLE LLVM_LIBDIR
70	OUTPUT_STRIP_TRAILING_WHITESPACE )
71execute_process( COMMAND ${LLVM_CONFIG} "--ldflags"
72	OUTPUT_VARIABLE LLVM_LD_FLAGS
73	OUTPUT_STRIP_TRAILING_WHITESPACE )
74execute_process( COMMAND ${LLVM_CONFIG} "--cxxflags"
75	OUTPUT_VARIABLE LLVM_CXX_FLAGS
76	OUTPUT_STRIP_TRAILING_WHITESPACE )
77separate_arguments( LLVM_CXX_FLAGS )
78execute_process( COMMAND ${LLVM_CONFIG} "--bindir"
79	OUTPUT_VARIABLE LLVM_BINDIR
80	OUTPUT_STRIP_TRAILING_WHITESPACE )
81
82# These were not properly reported in early LLVM and we don't need them
83list( APPEND LLVM_CXX_FLAGS -fno-rtti -fno-exceptions )
84
85# Print LLVM variables
86message( "LLVM system libs: ${LLVM_SYSTEM_LIBS}" )
87message( "LLVM libs: ${LLVM_LIBS}" )
88message( "LLVM libdir: ${LLVM_LIBDIR}" )
89message( "LLVM bindir: ${LLVM_BINDIR}" )
90message( "LLVM ld flags: ${LLVM_LD_FLAGS}" )
91message( "LLVM cxx flags: ${LLVM_CXX_FLAGS}" )
92message( "" )
93
94find_program( LLVM_CLANG clang PATHS ${LLVM_BINDIR} NO_DEFAULT_PATH )
95find_program( LLVM_AS llvm-as PATHS ${LLVM_BINDIR} NO_DEFAULT_PATH )
96find_program( LLVM_LINK llvm-link PATHS ${LLVM_BINDIR} NO_DEFAULT_PATH )
97find_program( LLVM_OPT opt PATHS ${LLVM_BINDIR} NO_DEFAULT_PATH )
98find_program( LLVM_SPIRV llvm-spirv PATHS ${LLVM_BINDIR} NO_DEFAULT_PATH )
99
100# Print toolchain
101message( "clang: ${LLVM_CLANG}" )
102message( "llvm-as: ${LLVM_AS}" )
103message( "llvm-link: ${LLVM_LINK}" )
104message( "opt: ${LLVM_OPT}" )
105message( "llvm-spirv: ${LLVM_SPIRV}" )
106message( "" )
107if( NOT LLVM_CLANG OR NOT LLVM_OPT OR NOT LLVM_AS OR NOT LLVM_LINK )
108	message( FATAL_ERROR "toolchain incomplete!" )
109endif()
110
111list( SORT LIBCLC_TARGETS_TO_BUILD )
112
113if( "spirv-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD OR "spirv64-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD )
114	if( NOT LLVM_SPIRV )
115		message( FATAL_ERROR "SPIR-V targets requested, but spirv-tools is not installed" )
116	endif()
117endif()
118
119set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake )
120set( CMAKE_CLC_COMPILER ${LLVM_CLANG} )
121set( CMAKE_CLC_ARCHIVE ${LLVM_LINK} )
122set( CMAKE_LLAsm_PREPROCESSOR ${LLVM_CLANG} )
123set( CMAKE_LLAsm_COMPILER ${LLVM_AS} )
124set( CMAKE_LLAsm_ARCHIVE ${LLVM_LINK} )
125enable_language( CLC LLAsm )
126
127# Construct LLVM version define
128string( REPLACE "." ";" LLVM_VERSION_LIST ${LLVM_VERSION} )
129list( GET LLVM_VERSION_LIST 0 LLVM_MAJOR )
130list( GET LLVM_VERSION_LIST 1 LLVM_MINOR )
131set( LLVM_VERSION_DEFINE "-DHAVE_LLVM=0x${LLVM_MAJOR}0${LLVM_MINOR}" )
132
133# This needs to be set before any target that needs it
134link_directories( ${LLVM_LIBDIR} )
135
136# Setup prepare_builtins tools
137add_executable( prepare_builtins utils/prepare-builtins.cpp )
138target_compile_options( prepare_builtins PRIVATE ${LLVM_CXX_FLAGS} )
139target_compile_definitions( prepare_builtins PRIVATE ${LLVM_VERSION_DEFINE} )
140target_link_libraries( prepare_builtins PRIVATE ${LLVM_LIBS} )
141target_link_libraries( prepare_builtins PRIVATE ${LLVM_SYSTEM_LIBS} )
142
143# Setup arch devices
144set( r600--_devices cedar cypress barts cayman )
145set( amdgcn--_devices tahiti )
146set( amdgcn-mesa-mesa3d_devices ${amdgcn--_devices} )
147set( amdgcn--amdhsa_devices none )
148set( nvptx--_devices none )
149set( nvptx64--_devices none )
150set( nvptx--nvidiacl_devices none )
151set( nvptx64--nvidiacl_devices none )
152set( spirv-mesa3d-_devices none )
153set( spirv64-mesa3d-_devices none )
154
155# Setup aliases
156set( cedar_aliases palm sumo sumo2 redwood juniper )
157set( cypress_aliases hemlock )
158set( barts_aliases turks caicos )
159set( cayman_aliases aruba )
160set( tahiti_aliases pitcairn verde oland hainan bonaire kabini kaveri hawaii
161	mullins tonga iceland carrizo fiji stoney polaris10 polaris11 )
162
163# Support for gfx9 was added in LLVM 5.0 (r295554)
164if( ${LLVM_VERSION} VERSION_GREATER "4.99.99" )
165	set( tahiti_aliases ${tahiti_aliases} gfx900 gfx902 )
166endif()
167
168# Support for Vega12 and Vega20 was added in LLVM 7 (r331215)
169if( ${LLVM_VERSION} VERSION_GREATER "6.99.99" )
170	set( tahiti_aliases ${tahiti_aliases} gfx904 gfx906 )
171endif()
172
173# pkg-config file
174configure_file( libclc.pc.in libclc.pc @ONLY )
175install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libclc.pc DESTINATION ${CMAKE_INSTALL_DATADIR}/pkgconfig )
176install( DIRECTORY generic/include/clc DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} )
177
178if( ENABLE_RUNTIME_SUBNORMAL )
179	add_library( subnormal_use_default STATIC
180		generic/lib/subnormal_use_default.ll )
181	add_library( subnormal_disable STATIC
182		generic/lib/subnormal_disable.ll )
183	install( TARGETS subnormal_use_default subnormal_disable ARCHIVE
184		DESTINATION ${CMAKE_INSTALL_DATADIR}/clc )
185endif()
186
187find_package( Python3 REQUIRED COMPONENTS Interpreter )
188file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/generic/lib/gen_convert.py script_loc )
189add_custom_command(
190	OUTPUT convert.cl
191	COMMAND ${Python3_EXECUTABLE} ${script_loc} > convert.cl
192	DEPENDS ${script_loc} )
193add_custom_target( "generate_convert.cl" DEPENDS convert.cl )
194
195enable_testing()
196
197foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
198	message( "BUILDING ${t}" )
199	string( REPLACE "-" ";" TRIPLE  ${t} )
200	list( GET TRIPLE 0 ARCH )
201	list( GET TRIPLE 1 VENDOR )
202	list( GET TRIPLE 2 OS )
203
204	set( dirs )
205
206	if ( NOT ${ARCH} STREQUAL spirv AND NOT ${ARCH} STREQUAL spirv64 )
207		LIST( APPEND dirs generic )
208	endif()
209
210	if( ${ARCH} STREQUAL r600 OR ${ARCH} STREQUAL amdgcn )
211		list( APPEND dirs amdgpu )
212	endif()
213
214	#nvptx is special
215	if( ${ARCH} STREQUAL nvptx OR ${ARCH} STREQUAL nvptx64 )
216		set( DARCH ptx )
217	else()
218		set( DARCH ${ARCH} )
219	endif()
220
221	# Enumerate SOURCES* files
222	set( source_list )
223	foreach( l ${dirs} ${DARCH} ${DARCH}-${OS} ${DARCH}-${VENDOR}-${OS} )
224		foreach( s "SOURCES" "SOURCES_${LLVM_MAJOR}.${LLVM_MINOR}" )
225			file( TO_CMAKE_PATH ${l}/lib/${s} file_loc )
226			file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/${file_loc} loc )
227			# Prepend the location to give higher priority to
228			# specialized implementation
229			if( EXISTS ${loc} )
230				set( source_list ${file_loc} ${source_list} )
231			endif()
232		endforeach()
233	endforeach()
234
235	# Add the generated convert.cl here to prevent adding
236	# the one listed in SOURCES
237	if( NOT ${ARCH} STREQUAL "spirv" AND NOT ${ARCH} STREQUAL "spirv64" )
238		set( rel_files convert.cl )
239		set( objects convert.cl )
240		if( NOT ENABLE_RUNTIME_SUBNORMAL )
241			list( APPEND rel_files generic/lib/subnormal_use_default.ll )
242		endif()
243	else()
244		set( rel_files )
245		set( objects )
246	endif()
247
248	foreach( l ${source_list} )
249		file( READ ${l} file_list )
250		string( REPLACE "\n" ";" file_list ${file_list} )
251		get_filename_component( dir ${l} DIRECTORY )
252		foreach( f ${file_list} )
253			list( FIND objects ${f} found )
254			if( found EQUAL  -1 )
255				list( APPEND objects ${f} )
256				list( APPEND rel_files ${dir}/${f} )
257				# FIXME: This should really go away
258				file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/${dir}/${f} src_loc )
259				get_filename_component( fdir ${src_loc} DIRECTORY )
260
261				set_source_files_properties( ${dir}/${f}
262					PROPERTIES COMPILE_FLAGS "-I ${fdir}" )
263			endif()
264		endforeach()
265	endforeach()
266
267	foreach( d ${${t}_devices} )
268		# Some targets don't have a specific GPU to target
269		if( ${d} STREQUAL "none" OR ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
270			set( mcpu )
271			set( arch_suffix "${t}" )
272		else()
273			set( mcpu "-mcpu=${d}" )
274			set( arch_suffix "${d}-${t}" )
275		endif()
276		message( "	DEVICE: ${d} ( ${${d}_aliases} )" )
277
278		if ( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
279			if( ${ARCH} STREQUAL "spirv" )
280				set( t "spir--" )
281			else()
282				set( t "spir64--" )
283			endif()
284			set( build_flags -O0 -finline-hint-functions )
285			set( opt_flags )
286			set( spvflags --spirv-max-version=1.1 )
287		else()
288			set( build_flags )
289			set( opt_flags -O3 )
290		endif()
291
292		add_library( builtins.link.${arch_suffix} STATIC ${rel_files} )
293		# Make sure we depend on the pseudo target to prevent
294		# multiple invocations
295		add_dependencies( builtins.link.${arch_suffix}
296			generate_convert.cl )
297		# CMake will turn this include into absolute path
298		target_include_directories( builtins.link.${arch_suffix} PRIVATE
299			"generic/include" )
300		target_compile_definitions( builtins.link.${arch_suffix} PRIVATE
301			"__CLC_INTERNAL" )
302		string( TOUPPER "-DCLC_${ARCH}" CLC_TARGET_DEFINE )
303		target_compile_definitions( builtins.link.${arch_suffix} PRIVATE
304			${CLC_TARGET_DEFINE} )
305		target_compile_options( builtins.link.${arch_suffix} PRIVATE  -target
306			${t} ${mcpu} -fno-builtin -nostdlib ${build_flags} )
307		set_target_properties( builtins.link.${arch_suffix} PROPERTIES
308			LINKER_LANGUAGE CLC )
309
310		set( obj_suffix ${arch_suffix}.bc )
311
312		# Add opt target
313		add_custom_command( OUTPUT "builtins.opt.${obj_suffix}"
314				    COMMAND ${LLVM_OPT} ${opt_flags} -o
315				    "builtins.opt.${obj_suffix}"
316				    "builtins.link.${obj_suffix}"
317				    DEPENDS "builtins.link.${arch_suffix}" )
318		add_custom_target( "opt.${obj_suffix}" ALL
319		                   DEPENDS "builtins.opt.${obj_suffix}" )
320
321		if( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
322			set( spv_suffix ${arch_suffix}.spv )
323			add_custom_command( OUTPUT "${spv_suffix}"
324					    COMMAND ${LLVM_SPIRV} ${spvflags}
325					    -o "${spv_suffix}"
326					    "builtins.link.${obj_suffix}"
327					    DEPENDS "builtins.link.${arch_suffix}" )
328			add_custom_target( "prepare-${spv_suffix}" ALL
329			                   DEPENDS "${spv_suffix}" )
330			install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${spv_suffix}
331				 DESTINATION ${CMAKE_INSTALL_DATADIR}/clc )
332		else()
333
334			# Add prepare target
335			add_custom_command( OUTPUT "${obj_suffix}"
336				            COMMAND prepare_builtins -o
337					    "${obj_suffix}"
338					    "builtins.opt.${obj_suffix}"
339					    DEPENDS "opt.${obj_suffix}"
340					            "builtins.opt.${obj_suffix}"
341					            prepare_builtins )
342			add_custom_target( "prepare-${obj_suffix}" ALL
343					   DEPENDS "${obj_suffix}" )
344
345			# nvptx-- targets don't include workitem builtins
346			if( NOT ${t} MATCHES ".*ptx.*--$" )
347				add_test( NAME external-calls-${obj_suffix}
348					  COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix}
349					  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
350				set_tests_properties( external-calls-${obj_suffix}
351					PROPERTIES ENVIRONMENT "LLVM_CONFIG=${LLVM_CONFIG}" )
352			endif()
353
354			install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION ${CMAKE_INSTALL_DATADIR}/clc )
355			foreach( a ${${d}_aliases} )
356				set( alias_suffix "${a}-${t}.bc" )
357				add_custom_target( ${alias_suffix} ALL
358						   COMMAND ${CMAKE_COMMAND} -E
359						   create_symlink ${obj_suffix}
360						   ${alias_suffix}
361				                   DEPENDS "prepare-${obj_suffix}" )
362				install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${alias_suffix} DESTINATION ${CMAKE_INSTALL_DATADIR}/clc )
363			endforeach( a )
364		endif()
365	endforeach( d )
366endforeach( t )
367