Enable split-dwarf and thin archives when possible. (#11292)

Split-dwarf separates debug info to per-object .dwo files, which reduces
IO throughout the build. It works best with the gdb-index linker feature
(of gold and lld), which links to this debug info (vs embedding) and
also adds an index to speed debugger launch.

Thin archives, which is a feature of GNU AR and llvm-ar on Linux
produces static archives that do not embed object files, instead just
referencing them by path.

While spit-dwarf is aimed at debug configurations, thin archives can
help all builds.

Results:

```
Before:
  Clean build:
    real    7m24.609s
    user    392m31.930s
    sys     18m59.113s
  build dir: 11GiB
  libIREECompiler.so: 1.4GiB
  Trivial relink the compiler:
    real    0m5.336s
    user    0m12.862s
    sys     0m31.818s

After:
  Clean build:
    real    7m38.461s
    user    402m52.180s
    sys     18m8.314s
  build dir: 5.2GiB
  libIREECompiler.so: 490MiB
  Trivial relink the compiler:
    real    0m4.350s
    user    0m8.233s
    sys     0m8.104s
  gdb of iree-compile starts instantly and sets a breakpoint on ireeCompilerRunMain with no delay (a few seconds to step in)
```

For a RelWithDebInfo build as documented on our website, wall clock time
to do a clean build is in the noise on my machine, but with the new
flags:

* Build directory size is reduced by >50%
* Size of the main compiler shared library is reduced by ~65%
* Time to incremental relink of the compiler after a trivial change to a
C file is reduced by ~18%.

Ideally there would be a less invasive way to enable these things, but
that isn't coming any time soon. I think the complexity is worth it. The
trivial relink case shows that this should make the cycle time better
disproportionately on lower end machines as well.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3754730..6181fba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -266,6 +266,8 @@
 option(IREE_ENABLE_TSAN "Enable thread sanitizer" OFF)
 option(IREE_BYTECODE_MODULE_ENABLE_TSAN "Enable thread sanitizer in IREE modules in tests" OFF)
 option(IREE_ENABLE_UBSAN "Enable undefined behavior sanitizer" OFF)
+option(IREE_ENABLE_SPLIT_DWARF "Enable gsplit-dwarf for debug information if the platform supports it" OFF)
+option(IREE_ENABLE_THIN_ARCHIVES "Enables thin ar archives (elf systems only). Disable for released static archives" OFF)
 
 # STREQUAL feels wrong here - we don't care about the exact true-value used,
 # ON or TRUE or something else. But we haven't been able to think of a less bad
@@ -364,7 +366,6 @@
 
 include(iree_macros)
 include(iree_copts)
-include(sanitizers)
 include(iree_cc_binary)
 include(iree_cc_library)
 include(iree_cc_test)
diff --git a/build_tools/cmake/iree_setup_toolchain.cmake b/build_tools/cmake/iree_setup_toolchain.cmake
index b1dbfa9..bdb54d9 100644
--- a/build_tools/cmake/iree_setup_toolchain.cmake
+++ b/build_tools/cmake/iree_setup_toolchain.cmake
@@ -4,6 +4,9 @@
 # See https://llvm.org/LICENSE.txt for license information.
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+include(CheckCXXCompilerFlag)
+include(CheckLinkerFlag)
+
 # Appends ${VALUE} to each argument.
 function(iree_append_to_lists VALUE)
   foreach(_VARIABLE ${ARGN})
@@ -11,6 +14,10 @@
   endforeach(_VARIABLE)
 endfunction()
 
+#-------------------------------------------------------------------------------
+# Linker setup
+#-------------------------------------------------------------------------------
+
 if(IREE_ENABLE_LLD)
   if(IREE_USE_LINKER)
     message(FATAL_ERROR "IREE_ENABLE_LLD and IREE_USE_LINKER can't be set at the same time")
@@ -57,3 +64,84 @@
     message(FATAL_ERROR "Compiler '${CMAKE_C_COMPILER}' does not support '${IREE_LINKER_FLAG}'")
   endif()
 endif()
+
+#-------------------------------------------------------------------------------
+# Sanitizer configurations
+#-------------------------------------------------------------------------------
+
+# Note: we add these flags to the global CMake flags, not to IREE-specific
+# variables such as IREE_DEFAULT_COPTS so that all symbols are consistently
+# defined with the same sanitizer flags, including e.g. standard library
+# symbols that might be used by both IREE and non-IREE (e.g. LLVM) code.
+
+if(IREE_ENABLE_ASAN)
+  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=address")
+  string(APPEND CMAKE_C_FLAGS " -fsanitize=address")
+endif()
+if(IREE_ENABLE_MSAN)
+  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=memory")
+  string(APPEND CMAKE_C_FLAGS " -fsanitize=memory")
+endif()
+if(IREE_ENABLE_TSAN)
+  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=thread")
+  string(APPEND CMAKE_C_FLAGS " -fsanitize=thread")
+endif()
+if(IREE_ENABLE_UBSAN)
+  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=undefined")
+  string(APPEND CMAKE_C_FLAGS " -fsanitize=undefined")
+endif()
+
+#-------------------------------------------------------------------------------
+# Build performance optimizations
+#-------------------------------------------------------------------------------
+
+# Split DWARF breaks debug information out of object files and stores them in
+# separate .dwo files. This reduces a lot of needless I/O during normal build
+# activities. It consists of the -gsplit-dwarf compiler flag and (for maximum
+# effect) the --gdb-index linker flag, which just emits an index to binaries
+# instead of full debug contents. gdb-index is supported by gold and partially
+# supported by LLD (LLD supports it if split-dwarf objects were compiled with
+# ggnu-pubnames).
+# If https://gitlab.kitware.com/cmake/cmake/-/issues/21179 is ever implemented,
+# use that.
+if(IREE_ENABLE_SPLIT_DWARF)
+  check_cxx_compiler_flag(-gsplit-dwarf IREE_SUPPORTS_SPLIT_DWARF)
+  if(IREE_SUPPORTS_SPLIT_DWARF)
+    # Also add -ggnu-pubnames for compilation because it links faster and lld
+    # doesn't do the slow path without it.
+    iree_append_to_lists(" -gsplit-dwarf -ggnu-pubnames"
+      CMAKE_C_FLAGS_DEBUG
+      CMAKE_CXX_FLAGS_DEBUG
+      CMAKE_C_FLAGS_RELWITHDEBINFO
+      CMAKE_CXX_FLAGS_RELWITHDEBINFO
+    )
+  endif()
+  check_linker_flag(CXX "-Wl,--gdb-index" IREE_SUPPORTS_GDB_INDEX)
+  if(IREE_SUPPORTS_GDB_INDEX)
+    message(STATUS "Enabling gdb-index (binaries with debug info are not relocatable)")
+    iree_append_to_lists(" -Wl,--gdb-index"
+      CMAKE_EXE_LINKER_FLAGS_DEBUG
+      CMAKE_MODULE_LINKER_FLAGS_DEBUG
+      CMAKE_SHARED_LINKER_FLAGS_DEBUG
+      CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
+      CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
+      CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
+    )
+  endif()
+endif()
+
+# Thin archives makes static archives that only link to backing object files
+# instead of embedding them. This makes them non-relocatable but is almost
+# always the right thing outside of certain deployment/packaging scenarios.
+if(IREE_ENABLE_THIN_ARCHIVES)
+  execute_process(COMMAND ${CMAKE_AR} -V OUTPUT_VARIABLE IREE_AR_VERSION)
+  if ("${IREE_AR_VERSION}" MATCHES "^GNU ar|LLVM")
+    message(STATUS "Enabling thin archives (static libraries will not be relocatable)")
+    set(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> qT <TARGET> <LINK_FLAGS> <OBJECTS>")
+    set(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> qT <TARGET> <LINK_FLAGS> <OBJECTS>")
+    set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> crT <TARGET> <LINK_FLAGS> <OBJECTS>")
+    set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> crT <TARGET> <LINK_FLAGS> <OBJECTS>")
+  else()
+    message(WARNING "Thin archives requested but not supported by ar")
+  endif()
+endif()
diff --git a/build_tools/cmake/sanitizers.cmake b/build_tools/cmake/sanitizers.cmake
deleted file mode 100644
index 4df913f..0000000
--- a/build_tools/cmake/sanitizers.cmake
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2020 The IREE Authors
-#
-# Licensed under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#-------------------------------------------------------------------------------
-# Sanitizer configurations
-#-------------------------------------------------------------------------------
-
-# Note: we add these flags to the global CMake flags, not to IREE-specific
-# variables such as IREE_DEFAULT_COPTS so that all symbols are consistently
-# defined with the same sanitizer flags, including e.g. standard library
-# symbols that might be used by both IREE and non-IREE (e.g. LLVM) code.
-
-if(IREE_ENABLE_ASAN)
-  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=address")
-  string(APPEND CMAKE_C_FLAGS " -fsanitize=address")
-endif()
-if(IREE_ENABLE_MSAN)
-  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=memory")
-  string(APPEND CMAKE_C_FLAGS " -fsanitize=memory")
-endif()
-if(IREE_ENABLE_TSAN)
-  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=thread")
-  string(APPEND CMAKE_C_FLAGS " -fsanitize=thread")
-endif()
-if(IREE_ENABLE_UBSAN)
-  string(APPEND CMAKE_CXX_FLAGS " -fsanitize=undefined")
-  string(APPEND CMAKE_C_FLAGS " -fsanitize=undefined")
-endif()