-
Notifications
You must be signed in to change notification settings - Fork 129
/
Copy pathLTO.cmake
249 lines (229 loc) · 11.8 KB
/
LTO.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# Usage :
#
# Variable : ENABLE_LTO | Enable or disable LTO support for this build
#
# find_lto(lang)
# - lang is C or CXX (the language to test LTO for)
# - call it after project() so that the compiler is already detected
#
# This will check for LTO support and create a target_enable_lto(target [debug,optimized,general]) macro.
# The 2nd parameter has the same meaning as in target_link_libraries, and is used to enable LTO only for those build configurations
# 'debug' is by default the Debug configuration, and 'optimized' all the other configurations
#
# if ENABLE_LTO is set to false, an empty macro will be generated
#
# Then to enable LTO for your target use
#
# target_enable_lto(mytarget general)
#
# It is however recommended to use it only for non debug builds the following way :
#
# target_enable_lto(mytarget optimized)
#
# Note : For CMake versions < 3.9, target_link_library is used in it's non plain version.
# You will need to specify PUBLIC/PRIVATE/INTERFACE to all your other target_link_library calls for the target
#
# WARNING for cmake versions older than 3.9 :
# This module will override CMAKE_AR CMAKE_RANLIB and CMAKE_NM by the gcc versions if found when building with gcc
# License:
#
# Copyright (C) 2016 Lectem <lectem@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the 'Software') deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
macro(find_lto lang)
if(ENABLE_LTO AND NOT LTO_${lang}_CHECKED)
#LTO support was added for clang/gcc in 3.9
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 3.9)
cmake_policy(SET CMP0054 NEW)
message(STATUS "Checking for LTO Compatibility")
# Since GCC 4.9 we need to use gcc-ar / gcc-ranlib / gcc-nm
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_GCC_AR OR NOT CMAKE_GCC_RANLIB OR NOT CMAKE_GCC_NM)
find_program(CMAKE_GCC_AR NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${_version}"
DOC "gcc provided wrapper for ar which adds the --plugin option"
)
find_program(CMAKE_GCC_RANLIB NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${_version}"
DOC "gcc provided wrapper for ranlib which adds the --plugin option"
)
# Not needed, but at least stay coherent
find_program(CMAKE_GCC_NM NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-nm"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-nm-${_version}"
DOC "gcc provided wrapper for nm which adds the --plugin option"
)
mark_as_advanced(CMAKE_GCC_AR CMAKE_GCC_RANLIB CMAKE_GCC_NM)
set(CMAKE_LTO_AR ${CMAKE_GCC_AR})
set(CMAKE_LTO_RANLIB ${CMAKE_GCC_RANLIB})
set(CMAKE_LTO_NM ${CMAKE_GCC_NM})
endif()
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_LTO_AR ${CMAKE_AR})
set(CMAKE_LTO_RANLIB ${CMAKE_RANLIB})
set(CMAKE_LTO_NM ${CMAKE_NM})
endif()
if(CMAKE_LTO_AR AND CMAKE_LTO_RANLIB)
set(__lto_flags -flto)
if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.7)
list(APPEND __lto_flags -fno-fat-lto-objects)
endif()
if(NOT DEFINED CMAKE_${lang}_PASSED_LTO_TEST)
set(__output_dir "${CMAKE_PLATFORM_INFO_DIR}/LtoTest1${lang}")
file(MAKE_DIRECTORY "${__output_dir}")
set(__output_base "${__output_dir}/lto-test-${lang}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "void foo() {}"
COMMAND ${CMAKE_${lang}_COMPILER} ${__lto_flags} -c -xc -
-o "${__output_base}.o"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
if("${__result}" STREQUAL "0")
execute_process(
COMMAND ${CMAKE_LTO_AR} cr "${__output_base}.a" "${__output_base}.o"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
endif()
if("${__result}" STREQUAL "0")
execute_process(
COMMAND ${CMAKE_LTO_RANLIB} "${__output_base}.a"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
endif()
if("${__result}" STREQUAL "0")
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "void foo(); int main() {foo();}"
COMMAND ${CMAKE_${lang}_COMPILER} ${__lto_flags} -xc -
-x none "${__output_base}.a" -o "${__output_base}"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
endif()
if("${__result}" STREQUAL "0")
set(__lto_found TRUE)
endif()
set(CMAKE_${lang}_PASSED_LTO_TEST
${__lto_found} CACHE INTERNAL
"If the compiler passed a simple LTO test compile")
endif()
if(CMAKE_${lang}_PASSED_LTO_TEST)
message(STATUS "Checking for LTO Compatibility - works")
set(LTO_${lang}_SUPPORT TRUE CACHE BOOL "Do we have LTO support ?")
set(LTO_COMPILE_FLAGS -flto CACHE STRING "Link Time Optimization compile flags")
set(LTO_LINK_FLAGS -flto CACHE STRING "Link Time Optimization link flags")
else()
message(STATUS "Checking for LTO Compatibility - not working")
endif()
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(STATUS "Checking for LTO Compatibility - works (assumed for clang)")
set(LTO_${lang}_SUPPORT TRUE CACHE BOOL "Do we have LTO support ?")
set(LTO_COMPILE_FLAGS -flto CACHE STRING "Link Time Optimization compile flags")
set(LTO_LINK_FLAGS -flto CACHE STRING "Link Time Optimization link flags")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(STATUS "Checking for LTO Compatibility - works")
set(LTO_${lang}_SUPPORT TRUE CACHE BOOL "Do we have LTO support ?")
set(LTO_COMPILE_FLAGS /GL CACHE STRING "Link Time Optimization compile flags")
set(LTO_LINK_FLAGS -LTCG:INCREMENTAL CACHE STRING "Link Time Optimization link flags")
else()
message(STATUS "Checking for LTO Compatibility - compiler not handled by module")
endif()
mark_as_advanced(LTO_${lang}_SUPPORT LTO_COMPILE_FLAGS LTO_LINK_FLAGS)
set(LTO_${lang}_CHECKED TRUE CACHE INTERNAL "" )
if(CMAKE_GCC_AR AND CMAKE_GCC_RANLIB AND CMAKE_GCC_NM)
# THIS IS HACKY BUT THERE IS NO OTHER SOLUTION ATM
set(CMAKE_AR ${CMAKE_GCC_AR} CACHE FILEPATH "Forcing gcc-ar instead of ar" FORCE)
set(CMAKE_NM ${CMAKE_GCC_NM} CACHE FILEPATH "Forcing gcc-nm instead of nm" FORCE)
set(CMAKE_RANLIB ${CMAKE_GCC_RANLIB} CACHE FILEPATH "Forcing gcc-ranlib instead of ranlib" FORCE)
endif()
endif(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 3.9)
endif(ENABLE_LTO AND NOT LTO_${lang}_CHECKED)
if(ENABLE_LTO)
#Special case for cmake older than 3.9, using a library for gcc/clang, but could setup the flags directly.
#Taking advantage of the [debug,optimized] parameter of target_link_libraries
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 3.9)
if(LTO_${lang}_SUPPORT)
if(NOT TARGET __enable_lto_tgt)
add_library(__enable_lto_tgt INTERFACE)
endif()
target_compile_options(__enable_lto_tgt INTERFACE ${LTO_COMPILE_FLAGS})
#this might not work for all platforms... in which case we'll have to set the link flags on the target directly
target_link_libraries(__enable_lto_tgt INTERFACE ${LTO_LINK_FLAGS} )
macro(target_enable_lto _target _build_configuration)
if(${_build_configuration} STREQUAL "optimized" OR ${_build_configuration} STREQUAL "debug" )
target_link_libraries(${_target} PRIVATE ${_build_configuration} __enable_lto_tgt)
else()
target_link_libraries(${_target} PRIVATE __enable_lto_tgt)
endif()
endmacro()
else()
#In old cmake versions, we can set INTERPROCEDURAL_OPTIMIZATION even if not supported by the compiler
#So if we didn't detect it, let cmake give it a try
set(__IPO_SUPPORTED TRUE)
endif()
else()
cmake_policy(SET CMP0069 NEW)
include(CheckIPOSupported)
# Optional IPO. Do not use IPO if it's not supported by compiler.
check_ipo_supported(RESULT __IPO_SUPPORTED OUTPUT output)
if(NOT __IPO_SUPPORTED)
message(STATUS "IPO is not supported or broken.")
else()
message(STATUS "IPO is supported")
endif()
endif()
if(__IPO_SUPPORTED)
macro(target_enable_lto _target _build_configuration)
if(NOT ${_build_configuration} STREQUAL "debug" )
#enable for all configurations
set_target_properties(${_target} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
if(${_build_configuration} STREQUAL "optimized" )
#blacklist debug configurations
set(__enable_debug_lto FALSE)
else()
#enable only for debug configurations
set(__enable_debug_lto TRUE)
endif()
get_property(DEBUG_CONFIGURATIONS GLOBAL PROPERTY DEBUG_CONFIGURATIONS)
if(NOT DEBUG_CONFIGURATIONS)
set(DEBUG_CONFIGURATIONS DEBUG) # This is what is done by CMAKE internally... since DEBUG_CONFIGURATIONS is empty by default
endif()
foreach(config IN LISTS DEBUG_CONFIGURATIONS)
set_target_properties(${_target} PROPERTIES INTERPROCEDURAL_OPTIMIZATION_${config} ${__enable_debug_lto})
endforeach()
endmacro()
endif()
endif()
if(NOT COMMAND target_enable_lto)
macro(target_enable_lto _target _build_configuration)
endmacro()
endif()
endmacro()