gollvm: add support for crosscompiling

Add support for compiling gollvm for other arch by specifying GOLLVM_DRIVER_DIR to a prebuilt gollvm with two targets, and specify target, toolchain and sysroot in GOLLVM_EXTRA_GOCFLAGS.

Example usage:
RISCV=$HOME/toolchain
SOURCE=$HOME/llvm-project/llvm
TRIPLE=riscv64-unknown-linux-gnu

# host
cmake -G Ninja -S $SOURCE -B build-x86 \
    -DCMAKE_INSTALL_PREFIX=install-x86 \
    -DCMAKE_BUILD_TYPE=Debug \
    -DLLVM_USE_LINKER=bfd \
    -DGOLLVM_DEFAULT_LINKER=bfd \
    -DLLVM_TARGET_ARCH="X86-64,RISCV64" \
    -DLLVM_TARGETS_TO_BUILD="X86;RISCV"

# crosscompile
cmake -G Ninja -S $SOURCE -B build-riscv \
    -DCMAKE_INSTALL_PREFIX=$INSTALL \
    -DCMAKE_BUILD_TYPE=Debug \
    -DLLVM_USE_LINKER=bfd \
    -DGOLLVM_DEFAULT_LINKER=bfd \
    -DCMAKE_CROSSCOMPILING=True \
    -DLLVM_TARGET_ARCH=RISCV64 \
    -DLLVM_DEFAULT_TARGET_TRIPLE=$TRIPLE \
    -DLLVM_TARGETS_TO_BUILD=RISCV \
    -DCMAKE_C_COMPILER=$RISCV/bin/$TRIPLE-gcc \
    -DCMAKE_CXX_COMPILER=$RISCV/bin/$TRIPLE-g++ \
    -DLLVM_TABLEGEN=$PWD/build-x86/bin/llvm-tblgen \
    -DGOLLVM_DRIVER_DIR=$PWD/build-x86/bin \
    -DGOLLVM_EXTRA_GOCFLAGS="--target=$TRIPLE \
                             --gcc-toolchain=$RISCV/ \
                             --sysroot=$RISCV/sysroot" \
    -DGOLLVM_USE_SPLIT_STACK=OFF \
    -DCMAKE_C_FLAGS=-latomic \
    -DCMAKE_CXX_FLAGS=-latomic

 # build gollvm crosscompiler
 ninja -C build-x86 llvm-goc llvm-goc-token llvm-godumpspec

 # cross compile gollvm, go tools and install
 ninja -C build-riscv install-gollvm

Change-Id: Ie4997771fd21437f65d857b7aaae267b7a394f23
Reviewed-on: https://go-review.googlesource.com/c/gollvm/+/425199
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 40a9618..2561f8b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,13 +39,28 @@
 
 set(gollvm_binroot "${CMAKE_CURRENT_BINARY_DIR}")
 
+# Set MPN path according to the target processor
+string(REGEX REPLACE "-" " " lht_components ${LLVM_DEFAULT_TARGET_TRIPLE})
+separate_arguments(lht_components)
+list(GET lht_components 0 llarch)
+
+if( ${llarch} STREQUAL "x86_64" )
+  set(MPN_PATH "x86_64 generic")
+elseif( ${llarch} STREQUAL "aarch64" )
+  set(MPN_PATH "arm64 generic")
+elseif( ${llarch} STREQUAL "riscv64" )
+  set(MPN_PATH "riscv generic")
+else()
+  message(SEND_ERROR "Arch ${llarch} not yet supported")
+endif()
+
 externalproject_add(libgmp
   URL https://gmplib.org/download/gmp/gmp-6.2.0.tar.bz2 https://mirrors.kernel.org/gnu/gmp/gmp-6.2.0.tar.bz2
   URL_MD5 c24161e0dd44cae78cd5f67193492a21
   DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/external-downloads
   BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/gmp-build
   SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/gmp
-  CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC}
+  CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC} --build=${LLVM_HOST_TRIPLE} --host=${LLVM_DEFAULT_TARGET_TRIPLE} MPN_PATH=${MPN_PATH}
   BUILD_COMMAND make -j${PROCESSOR_COUNT} install
   LOG_CONFIGURE 1
   LOG_BUILD 1
@@ -59,7 +74,7 @@
   DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/external-downloads
   BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/mpfr
   SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/mpfr
-  CONFIGURE_COMMAND <SOURCE_DIR>/configure --with-gmp=${CMAKE_CURRENT_BINARY_DIR}/external/gmp --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC}
+  CONFIGURE_COMMAND <SOURCE_DIR>/configure --with-gmp=${CMAKE_CURRENT_BINARY_DIR}/external/gmp --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC} -build=${LLVM_HOST_TRIPLE} --host=${LLVM_DEFAULT_TARGET_TRIPLE}
   BUILD_COMMAND make -j${PROCESSOR_COUNT} install
   LOG_CONFIGURE 1
   LOG_BUILD 1
@@ -74,7 +89,7 @@
   BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/mpc
   SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/mpc
   PREFIX ${EXTINSTALLDIR}
-  CONFIGURE_COMMAND <SOURCE_DIR>/configure --with-gmp=${CMAKE_CURRENT_BINARY_DIR}/external/gmp --with-mpfr=${CMAKE_CURRENT_BINARY_DIR}/external/mpfr --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC}
+  CONFIGURE_COMMAND <SOURCE_DIR>/configure --with-gmp=${CMAKE_CURRENT_BINARY_DIR}/external/gmp --with-mpfr=${CMAKE_CURRENT_BINARY_DIR}/external/mpfr --prefix=${EXTINSTALLDIR} ${EXTCPPFLAGS} ${EXTLDFLAGS} ${EXTCC} -build=${LLVM_HOST_TRIPLE} --host=${LLVM_DEFAULT_TARGET_TRIPLE}
   BUILD_COMMAND make -j${PROCESSOR_COUNT} install
   LOG_CONFIGURE 1
   LOG_BUILD 1
diff --git a/cmake/modules/AutoGenGo.cmake b/cmake/modules/AutoGenGo.cmake
index 644075a..a0a3af6 100644
--- a/cmake/modules/AutoGenGo.cmake
+++ b/cmake/modules/AutoGenGo.cmake
@@ -375,7 +375,11 @@
   CMAKE_PARSE_ARGUMENTS(ARG "EXPORT" "" "" ${ARGN})
 
   # Construct default driver path
-  set(driverpath "${GOLLVM_INSTALL_DIR}/bin/llvm-goc")
+  if (GOLLVM_DRIVER_DIR)
+    set(driverpath "${GOLLVM_DRIVER_DIR}/bin/llvm-goc")
+  else()
+    set(driverpath "${GOLLVM_INSTALL_DIR}/bin/llvm-goc")
+  endif()
 
   file(REMOVE ${outfile})
   file(WRITE ${outfile} "package ${package}\n\n")
diff --git a/cmake/modules/GoPackage.cmake b/cmake/modules/GoPackage.cmake
index 7e9e065..0d26a16 100644
--- a/cmake/modules/GoPackage.cmake
+++ b/cmake/modules/GoPackage.cmake
@@ -80,9 +80,14 @@
   endif()
 
   # Command to build *.gox.tmp
+  set(objcopycommand "objcopy")
+  if (GOLLVM_DRIVER_DIR)
+    set(objcopycommand "${LLVM_DEFAULT_TARGET_TRIPLE}-objcopy")
+  endif()
+
   add_custom_command(
     OUTPUT "${package_goxtmp}"
-    COMMAND objcopy -j .go_export "${package_ofile}" "${package_goxtmp}"
+    COMMAND "${objcopycommand}" -j .go_export "${package_ofile}" "${package_goxtmp}"
     DEPENDS ${package_ofile} ${package_picofile}
     COMMENT "Building Go exports file for package '${pkgpath}'"
     VERBATIM)
diff --git a/driver/GollvmOptions.td b/driver/GollvmOptions.td
index 3c60287..b2b627f 100644
--- a/driver/GollvmOptions.td
+++ b/driver/GollvmOptions.td
@@ -506,7 +506,8 @@
            "frontend by not running any LLVM passes at all">;
 
 def gcc_toolchain_EQ : Joined<["--"], "gcc-toolchain=">, Flags<[DriverOption]>,
-  HelpText<"Use the gcc toolchain at the given directory">;
+  HelpText<"Use the gcc toolchain at the given directory, the directory typically contains "
+           "‘lib{,32,64}/gcc{,-cross}/$triple’ and ‘include’">;
 
 def enable_gc_EQ : Joined<["-"], "enable-gc=">,
   HelpText<"Enable stack map generation">;
diff --git a/driver/LinuxToolChain.cpp b/driver/LinuxToolChain.cpp
index 2a2fb10..bebad8c 100644
--- a/driver/LinuxToolChain.cpp
+++ b/driver/LinuxToolChain.cpp
@@ -34,9 +34,10 @@
 
 static llvm::StringRef getOSLibDir(const llvm::Triple &triple)
 {
-  // multilib is not supported on major aarch64/arm64 linux distributions
+  // multilib is not supported on major aarch64/arm64 and riscv64 linux distributions
   // subject to change when more scenarios to be taken into account
-  if (triple.getArch() == llvm::Triple::aarch64)
+  if (triple.getArch() == llvm::Triple::aarch64 ||
+      triple.getArch() == llvm::Triple::riscv64)
     return "lib";
   // x86 uses the lib32 variant, unlike other archs.
   if (triple.getArch() == llvm::Triple::x86)
@@ -67,8 +68,11 @@
   pathlist &fpaths = filePaths();
   addIfPathExists(fpaths, gccDetector_.getLibPath());
   std::string osLibDir = getOSLibDir(targetTriple).str();
-  if (!driver.sysRoot().empty())
+  if (!driver.sysRoot().empty()) {
+    addIfPathExists(fpaths, llvm::Twine(driver.sysRoot() + "/usr/"
+                                        + osLibDir).str());
     osLibDir = driver.sysRoot() + "/" + osLibDir;
+  }
   addIfPathExists(fpaths, llvm::Twine(gccDetector_.getParentLibPath() +
                                       "/../" + ftrip).str());
   addIfPathExists(fpaths, llvm::Twine(osLibDir).str());
@@ -136,6 +140,10 @@
       break;
     }
   }
+  if (auto *Arg = args.getLastArg(gollvm::options::OPT_sysroot_EQ)) {
+    std::string Sysroot = Arg->getValue();
+    return Sysroot + "/" + LibDir + "/" + Loader;
+  }
   return "/" + LibDir + "/" + Loader;
 }
 
diff --git a/gotools/CMakeLists.txt b/gotools/CMakeLists.txt
index cf70562..e8023f4 100644
--- a/gotools/CMakeLists.txt
+++ b/gotools/CMakeLists.txt
@@ -22,8 +22,12 @@
 endif()
 
 # Driver for compiling *.go files.
-get_target_property(driverdir llvm-goc RUNTIME_OUTPUT_DIRECTORY)
-set(gollvm_driver "${driverdir}/llvm-goc")
+if (GOLLVM_DRIVER_DIR)
+  set(gollvm_driver "${GOLLVM_DRIVER_DIR}/llvm-goc")
+else()
+  get_target_property(driverdir llvm-goc RUNTIME_OUTPUT_DIRECTORY)
+  set(gollvm_driver "${driverdir}/llvm-goc")
+endif()
 set(gocompiler ${gollvm_driver})
 
 set(cmd_srcroot "${GOLLVM_SOURCE_DIR}/gofrontend/libgo/go/cmd")
diff --git a/libgo/CMakeLists.txt b/libgo/CMakeLists.txt
index b0cf30d..8625699 100644
--- a/libgo/CMakeLists.txt
+++ b/libgo/CMakeLists.txt
@@ -72,8 +72,12 @@
 #......................................................................
 
 # Driver for compiling *.go files.
-get_target_property(driverdir llvm-goc RUNTIME_OUTPUT_DIRECTORY)
-set(gollvm_driver "${driverdir}/llvm-goc")
+if (GOLLVM_DRIVER_DIR)
+  set(gollvm_driver "${GOLLVM_DRIVER_DIR}/llvm-goc")
+else()
+  get_target_property(driverdir llvm-goc RUNTIME_OUTPUT_DIRECTORY)
+  set(gollvm_driver "${driverdir}/llvm-goc")
+endif()
 set(gocompiler ${gollvm_driver})
 
 # Pick up any extra Go compiler flags specified via
@@ -272,8 +276,12 @@
 set(gensysinfotmp "${libgo_binroot}/gen-sysinfo.go.tmp")
 set(gensysinfomacrotmp "${libgo_binroot}/sysinfo.macros.txt")
 set(gensysinfoobject "${libgo_binroot}/sysinfo.o")
-get_target_property(godumpspecdir llvm-godumpspec RUNTIME_OUTPUT_DIRECTORY)
-set(godumpspecexec "${godumpspecdir}/llvm-godumpspec")
+if (GOLLVM_DRIVER_DIR)
+  set(godumpspecexec "${GOLLVM_DRIVER_DIR}/llvm-godumpspec")
+else()
+  get_target_property(godumpspecdir llvm-godumpspec RUNTIME_OUTPUT_DIRECTORY)
+  set(godumpspecexec "${godumpspecdir}/llvm-godumpspec")
+endif()
 set(sysinfoc "${libgo_srcroot}/sysinfo.c")
 set(sysinfoflags ${basedefines})
 list(APPEND sysinfoflags "-I${libgo_binroot}" "-I${libgo_binroot}/runtime")