diff --git a/CMakeLists.txt b/CMakeLists.txt index eaf4fd23..f0a31de7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,11 @@ option( the tools every time you update llvm-project." ON ) +option( + APPLY_LLVM_PERFORMANCE_PATCHES + "During checkout, apply optional downstream patches to + llvm-project to improve performance." +) set( FVP_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/fvp/install" CACHE STRING @@ -280,24 +285,17 @@ if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo? read_repo_version(${LLVM_TOOLCHAIN_C_LIBRARY} ${LLVM_TOOLCHAIN_C_LIBRARY}) endif() -# The patches are generated from custom branch, with followin command: -# git format-patch -k origin/main -# The patches apply cleanly against llvm-project commit -# 327124ece7d59de56ca0f9faa2cd82af68c011b9. -set( - llvm_project_patches - ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project/0001-libc-tests-with-picolibc-xfail-one-remaining-test.patch - ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project/0002-libc-tests-with-picolibc-disable-large-tests.patch - ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project/0003-Disable-failing-compiler-rt-test.patch - ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project/0004-libc-tests-with-picolibc-XFAIL-uses-of-atomics.patch - ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project/0005-libc-tests-with-picolibc-mark-two-more-large-tests.patch -) +set(LLVM_PATCH_COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patch_llvm.py ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project) +if(APPLY_LLVM_PERFORMANCE_PATCHES) + set(LLVM_PATCH_COMMAND ${LLVM_PATCH_COMMAND} && ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patch_llvm.py ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project-perf) +endif() + FetchContent_Declare(llvmproject GIT_REPOSITORY https://github.com/llvm/llvm-project.git GIT_TAG "${llvmproject_TAG}" GIT_SHALLOW "${llvmproject_SHALLOW}" GIT_PROGRESS TRUE - PATCH_COMMAND git reset --quiet --hard && git clean --quiet --force -dx && git am -k --ignore-whitespace --3way ${llvm_project_patches} + PATCH_COMMAND ${LLVM_PATCH_COMMAND} # Add the llvm subdirectory later to ensure that # LLVMEmbeddedToolchainForArm is the first project declared. # Otherwise CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT diff --git a/cmake/patch_llvm.py b/cmake/patch_llvm.py new file mode 100644 index 00000000..85bf535b --- /dev/null +++ b/cmake/patch_llvm.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 + +""" +Script to apply a set of patches to llvm-project sources. +""" + +import argparse +import os +import pathlib +import subprocess +import sys + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "patchdir", + help="Set of patches to apply. This should be a directory containing one or more ordered *.patch files.", + ) + parser.add_argument( + "--llvm_dir", + help="Directory of the llvm-project git checkout, if not the current directory.", + ) + parser.add_argument( + "--method", + choices=["am", "apply"], + default="apply", + help="Git command to use. git am will add each patch as a commit, whereas git apply will leave patched changes staged.", + ) + parser.add_argument( + "--reset", + help="Clean and hard reset the repo to a specified commit before patching.", + ) + parser.add_argument( + "--restore_on_fail", + action="store_true", + help="If a patch in a series cannot be applied, restore the original state instead of leaving patches missing. Return code will be 2 instead of 1.", + ) + args = parser.parse_args() + + if args.llvm_dir: + git_cmd = ["git", "-C", args.llvm_dir] + else: + git_cmd = ["git"] + + if args.reset: + reset_args = git_cmd + ["reset", "--quiet", "--hard", args.reset] + subprocess.check_output(reset_args) + clean_args = git_cmd + ["clean", "--quiet", "--force", "-dx", args.reset] + subprocess.check_output(clean_args) + + abs_patch_dir = os.path.abspath(args.patchdir) + patch_list = list(pathlib.Path(abs_patch_dir).glob("*.patch")) + patch_list.sort() + + print(f"Found {len(patch_list)} patches to apply:") + print("\n".join(p.name for p in patch_list)) + + if args.method == "am": + merge_args = git_cmd + ["am", "-k", "--ignore-whitespace", "--3way"] + for patch in patch_list: + merge_args.append(str(patch)) + p = subprocess.run(merge_args, capture_output=True, text=True) + print(p.stdout) + print(p.stderr) + + if p.returncode == 0: + print(f"All patches applied.") + sys.exit(0) + if args.restore_on_fail: + # Check that the operation can be aborted. + # git am doesn't give any specific return codes, + # so check for unresolved working files. + rebase_apply_path = os.path.join(".git", "rebase-apply") + if args.llvm_dir: + rebase_apply_path = os.path.join(args.llvm_dir, rebase_apply_path) + if os.path.isdir(rebase_apply_path): + print("Aborting git am...") + subprocess.run(git_cmd + ["am", "--abort"], check=True) + print(f"Abort successful.") + sys.exit(2) + else: + print("Unable to abort.") + sys.exit(1) + else: + applied_patches = [] + for current_patch in patch_list: + print(f"Checking {current_patch.name}...") + # Check that the patch applies before trying to apply it. + apply_check_args = git_cmd + [ + "apply", + "--ignore-whitespace", + "--3way", + "--check", + str(current_patch), + ] + p_check = subprocess.run(apply_check_args) + + if p_check.returncode == 0: + # Patch will apply. + print(f"Applying {current_patch.name}...") + apply_args = git_cmd + [ + "apply", + "--ignore-whitespace", + "--3way", + str(current_patch), + ] + apply_args = subprocess.run(apply_args, check=True) + applied_patches.append(current_patch) + else: + # Patch won't apply. + print(f"Unable to apply {current_patch.name}") + if args.restore_on_fail: + # Remove any patches that have already been applied. + while len(applied_patches) > 0: + previous_patch = applied_patches.pop() + print(f"Reversing {previous_patch.name}...") + reverse_args = git_cmd + [ + "apply", + "--ignore-whitespace", + "--3way", + "--reverse", + str(previous_patch), + ] + p_check = subprocess.run(reverse_args, check=True) + print( + f"Rollback successful, failure occured on {current_patch.name}" + ) + sys.exit(2) + sys.exit(1) + print(f"All patches applied.") + + +main() diff --git a/docs/building-from-source.md b/docs/building-from-source.md index f62fac50..823d2ab7 100644 --- a/docs/building-from-source.md +++ b/docs/building-from-source.md @@ -148,3 +148,12 @@ The same build directory can be used for both native and MinGW toolchains. See [patches](https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/tree/main/patches) directory for the current set of differences from upstream. + +The patches for llvm-project are split between two folders, llvm-project and +llvm-project-perf. The former are generally required for building and +successfully running all tests. The patches in llvm-project-perf are optional, +and designed to improve performance in certain circumstances. + +To reduce divergence from upstream and potential patch conflicts, the +performance patches are not applied by default, but can be enabled for an +automatic checkout with the APPLY_LLVM_PERFORMANCE_PATCHES option. diff --git a/patches/llvm-project-perf/0000-Placeholder-commit.patch b/patches/llvm-project-perf/0000-Placeholder-commit.patch new file mode 100644 index 00000000..ff8059cb --- /dev/null +++ b/patches/llvm-project-perf/0000-Placeholder-commit.patch @@ -0,0 +1,22 @@ +From e79697a54cba9bfc1a755ed048e42054d679de61 Mon Sep 17 00:00:00 2001 +From: David Candler +Date: Wed, 2 Oct 2024 14:13:31 +0100 +Subject: [PATCH] Placeholder commit + +--- + .gitignore | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.gitignore b/.gitignore +index 0e7c6c790013..dfa0b8da0ccd 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,4 +1,4 @@ +-#==============================================================================# ++#==============================================================================# + # This file specifies intentionally untracked files that git should ignore. + # See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html + # +-- +2.34.1 +