Copy and modify update.py for LLVM 10

Modifications to update.py:
* The script now clones and builds llvm in a temporary location based on
  the LLVM_BRANCH and LLVM_COMMIT variables in the script.
* Added logging, including for execution of commands
* Now setting LLVM_ENABLE_THREADS=ON as we recently manually enabled
  multithreading for the LLVM backend.

Change-Id: I53bed46d890c0da234c4245d456c83280bf7b370
Bug: b/152339534
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/46188
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/third_party/llvm-10.0/scripts/update.py b/third_party/llvm-10.0/scripts/update.py
new file mode 100644
index 0000000..5783667
--- /dev/null
+++ b/third_party/llvm-10.0/scripts/update.py
@@ -0,0 +1,316 @@
+#!/usr/bin/env python3
+
+# Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import argparse
+import contextlib
+import multiprocessing
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+from os import path
+
+# LLVM_BRANCH must match the value of the same variable in third_party/update-llvm-10.sh
+LLVM_BRANCH = "release/10.x"
+
+# LLVM_COMMIT must be set to the commit hash that we last updated to when running third_party/update-llvm-10.sh.
+# Run 'git show -s origin/llvm10-clean' and look for 'llvm-10-update: <hash>' to retrieve it.
+LLVM_COMMIT = "d32170dbd5b0d54436537b6b75beaf44324e0c28"
+
+SCRIPT_DIR = path.dirname(path.realpath(sys.argv[0]))
+LLVM_STAGING_DIR = path.abspath(path.join(tempfile.gettempdir(), "llvm-10"))
+LLVM_DIR = path.abspath(path.join(LLVM_STAGING_DIR, "llvm"))
+LLVM_OBJS = path.join(LLVM_STAGING_DIR, "build")
+LLVM_CONFIGS = path.abspath(path.join(SCRIPT_DIR, '..', 'configs'))
+
+LLVM_TARGETS = [
+    ('AArch64', ('__aarch64__',)),
+    ('ARM', ('__arm__',)),
+    ('X86', ('__i386__', '__x86_64__')),
+    ('Mips', ('__mips__',)),
+    ('PowerPC', ('__powerpc64__',)),
+]
+
+LLVM_TRIPLES = {
+    'android': [
+        ('__x86_64__', 'x86_64-linux-android'),
+        ('__i386__', 'i686-linux-android'),
+        ('__arm__', 'armv7-linux-androideabi'),
+        ('__aarch64__', 'aarch64-linux-android'),
+    ],
+    'linux': [
+        ('__x86_64__', 'x86_64-unknown-linux-gnu'),
+        ('__i386__', 'i686-pc-linux-gnu'),
+        ('__arm__', 'armv7-linux-gnueabihf'),
+        ('__aarch64__', 'aarch64-linux-gnu'),
+        ('__mips__', 'mipsel-linux-gnu'),
+        ('__mips64', 'mips64el-linux-gnuabi64'),
+        ('__powerpc64__', 'powerpc64le-unknown-linux-gnu'),
+    ],
+    'darwin': [
+        ('__x86_64__', 'x86_64-apple-darwin'),
+    ],
+    'windows': [
+        ('__x86_64__', 'x86_64-pc-win32'),
+        ('__i386__', 'i686-pc-win32'),
+        ('__arm__', 'armv7-pc-win32'),
+        ('__aarch64__', 'aarch64-pc-win32'),
+        ('__mips__', 'mipsel-pc-win32'),
+        ('__mips64', 'mips64el-pc-win32'),
+    ],
+}
+
+LLVM_OPTIONS = [
+    '-DCMAKE_BUILD_TYPE=Release',
+    '-DLLVM_TARGETS_TO_BUILD=' + ';'.join(t[0] for t in LLVM_TARGETS),
+    '-DLLVM_ENABLE_THREADS=ON',
+    '-DLLVM_ENABLE_TERMINFO=OFF',
+    '-DLLVM_ENABLE_LIBXML2=OFF',
+    '-DLLVM_ENABLE_LIBEDIT=OFF',
+    '-DLLVM_ENABLE_LIBPFM=OFF',
+    '-DLLVM_ENABLE_ZLIB=OFF',
+    '-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON'
+]
+
+
+@contextlib.contextmanager
+def pushd(new_dir):
+    previous_dir = os.getcwd()
+    os.chdir(new_dir)
+    try:
+        yield
+    finally:
+        os.chdir(previous_dir)
+
+
+def log(message, level=1):
+    print(' ' * level + '> ' + message)
+
+
+def run_command(command, log_level=1):
+    log(command, log_level)
+    os.system(command)
+
+
+def run_subprocess(*popenargs, log_level=1, cwd=None):
+    log([' '.join(t) for t in popenargs][0], log_level)
+    return subprocess.run(*popenargs, cwd=cwd)
+
+
+def _parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('name', help='destination name',
+                        choices=['android', 'linux', 'darwin', 'windows'])
+    parser.add_argument('-j', '--jobs', help='parallel compilation', type=int)
+    return parser.parse_args()
+
+
+def clone_llvm():
+    log("Cloning/Updating LLVM", 1)
+    # Clone or update staging directory
+    if not path.exists(LLVM_STAGING_DIR):
+        os.mkdir(LLVM_STAGING_DIR)
+        with pushd(LLVM_STAGING_DIR):
+            run_command('git init', 2)
+            run_command(
+                'git remote add origin https://github.com/llvm/llvm-project.git', 2)
+            run_command('git config core.sparsecheckout true', 2)
+            run_command('echo /llvm > .git/info/sparse-checkout', 2)
+
+    with pushd(LLVM_STAGING_DIR):
+        run_command('echo /llvm > .git/info/sparse-checkout', 2)
+        run_command('git fetch origin {}'.format(LLVM_BRANCH), 2)
+        run_command('git checkout {}'.format(LLVM_COMMIT), 2)
+    return
+
+
+def build_llvm(name, num_jobs):
+    """Build LLVM and get all generated files."""
+    log("Building LLVM", 1)
+    if num_jobs is None:
+        num_jobs = multiprocessing.cpu_count()
+
+    """On Windows we need to have CMake generate build files for the 64-bit
+    Visual Studio host toolchain."""
+    host = '-Thost=x64' if name == 'windows' else ''
+
+    os.makedirs(LLVM_OBJS, exist_ok=True)
+    run_subprocess(['cmake', host, LLVM_DIR] +
+                   LLVM_OPTIONS, log_level=2, cwd=LLVM_OBJS)
+    run_subprocess(['cmake', '--build', '.', '-j',
+                    str(num_jobs)], log_level=2, cwd=LLVM_OBJS)
+
+
+def list_files(src_base, src, dst_base, suffixes):
+    """Enumerate the files that are under `src` and end with one of the
+    `suffixes` and yield the source path and the destination path."""
+    src_base = path.abspath(src_base)
+    src = path.join(src_base, src)
+    for base_dir, dirnames, filenames in os.walk(src):
+        for filename in filenames:
+            if path.splitext(filename)[1] in suffixes:
+                relative = path.relpath(base_dir, src_base)
+                yield (path.join(base_dir, filename),
+                       path.join(dst_base, relative, filename))
+
+
+def copy_common_generated_files(dst_base):
+    """Copy platform-independent generated files."""
+    log("Copying platform-independent generated files", 1)
+    suffixes = {'.inc', '.h', '.def'}
+    subdirs = [
+        path.join('include', 'llvm', 'IR'),
+        path.join('include', 'llvm', 'Support'),
+        path.join('lib', 'IR'),
+        path.join('lib', 'Target', 'AArch64'),
+        path.join('lib', 'Target', 'ARM'),
+        path.join('lib', 'Target', 'X86'),
+        path.join('lib', 'Target', 'Mips'),
+        path.join('lib', 'Target', 'PowerPC'),
+        path.join('lib', 'Transforms', 'InstCombine'),
+    ]
+    for subdir in subdirs:
+        for src, dst in list_files(LLVM_OBJS, subdir, dst_base, suffixes):
+            log('{} -> {}'.format(src, dst), 2)
+            os.makedirs(path.dirname(dst), exist_ok=True)
+            shutil.copyfile(src, dst)
+
+
+def copy_platform_file(platform, src, dst):
+    """Copy platform-dependent generated files and add platform-specific
+    modifications."""
+
+    # LLVM configuration patterns to be post-processed.
+    llvm_target_pattern = re.compile('^LLVM_[A-Z_]+\\(([A-Za-z0-9_]+)\\)$')
+    llvm_native_pattern = re.compile(
+        '^#define LLVM_NATIVE_([A-Z]+) (LLVMInitialize)?(.*)$')
+    llvm_triple_pattern = re.compile('^#define (LLVM_[A-Z_]+_TRIPLE) "(.*)"$')
+    llvm_define_pattern = re.compile('^#define ([A-Za-z0-9_]+) (.*)$')
+
+    # LLVM configurations to be undefined.
+    undef_names = [
+        'BACKTRACE_HEADER',
+        'ENABLE_BACKTRACES',
+        'ENABLE_CRASH_OVERRIDES',
+        'HAVE_BACKTRACE',
+        'HAVE_POSIX_SPAWN',
+        'HAVE_PTHREAD_GETNAME_NP',
+        'HAVE_PTHREAD_SETNAME_NP',
+        'HAVE_TERMIOS_H',
+        'HAVE_ZLIB_H',
+        'HAVE__UNWIND_BACKTRACE',
+    ]
+
+    # Build architecture-specific conditions.
+    conds = {}
+    for arch, defs in LLVM_TARGETS:
+        conds[arch] = ' || '.join('defined(' + v + ')' for v in defs)
+
+    # Get a set of platform-specific triples.
+    triples = LLVM_TRIPLES[platform]
+
+    with open(src, 'r') as src_file:
+        os.makedirs(path.dirname(dst), exist_ok=True)
+        with open(dst, 'w') as dst_file:
+            for line in src_file:
+                if line == '#define LLVM_CONFIG_H\n':
+                    print(line, file=dst_file, end='')
+                    print('', file=dst_file)
+                    print('#if !defined(__i386__) && defined(_M_IX86)',
+                          file=dst_file)
+                    print('#define __i386__ 1', file=dst_file)
+                    print('#endif', file=dst_file)
+                    print('', file=dst_file)
+                    print(
+                        '#if !defined(__x86_64__) && (defined(_M_AMD64) || defined (_M_X64))', file=dst_file)
+                    print('#define __x86_64__ 1', file=dst_file)
+                    print('#endif', file=dst_file)
+                    print('', file=dst_file)
+
+                match = llvm_target_pattern.match(line)
+                if match:
+                    arch = match.group(1)
+                    print('#if ' + conds[arch], file=dst_file)
+                    print(line, file=dst_file, end='')
+                    print('#endif', file=dst_file)
+                    continue
+
+                match = llvm_native_pattern.match(line)
+                if match:
+                    name = match.group(1)
+                    init = match.group(2) or ''
+                    arch = match.group(3)
+                    end = ''
+                    if arch.lower().endswith(name.lower()):
+                        end = arch[-len(name):]
+                    directive = '#if '
+                    for arch, defs in LLVM_TARGETS:
+                        print(directive + conds[arch], file=dst_file)
+                        print('#define LLVM_NATIVE_' + name + ' ' +
+                              init + arch + end, file=dst_file)
+                        directive = '#elif '
+                    print('#else', file=dst_file)
+                    print('#error "unknown architecture"', file=dst_file)
+                    print('#endif', file=dst_file)
+                    continue
+
+                match = llvm_triple_pattern.match(line)
+                if match:
+                    name = match.group(1)
+                    directive = '#if'
+                    for defs, triple in triples:
+                        print(directive + ' defined(' + defs + ')',
+                              file=dst_file)
+                        print('#define ' + name + ' "' + triple + '"',
+                              file=dst_file)
+                        directive = '#elif'
+                    print('#else', file=dst_file)
+                    print('#error "unknown architecture"', file=dst_file)
+                    print('#endif', file=dst_file)
+                    continue
+
+                match = llvm_define_pattern.match(line)
+                if match and match.group(1) in undef_names:
+                    print('/* #undef ' + match.group(1) + ' */', file=dst_file)
+                    continue
+
+                print(line, file=dst_file, end='')
+
+
+def copy_platform_generated_files(platform, dst_base):
+    """Copy platform-specific generated files."""
+    log("Copying platform-specific generated files", 1)
+    suffixes = {'.inc', '.h', '.def'}
+    src_dir = path.join('include', 'llvm', 'Config')
+    for src, dst in list_files(LLVM_OBJS, src_dir, dst_base, suffixes):
+        log('{}, {} -> {}'.format(platform, src, dst), 2)
+        copy_platform_file(platform, src, dst)
+
+
+def main():
+    args = _parse_args()
+    clone_llvm()
+    build_llvm(args.name, args.jobs)
+    copy_common_generated_files(path.join(LLVM_CONFIGS, 'common'))
+    copy_platform_generated_files(
+        args.name, path.join(LLVM_CONFIGS, args.name))
+
+
+if __name__ == '__main__':
+    main()