1#!/usr/bin/env python3 2 3# Copyright 2018 The SwiftShader Authors. All Rights Reserved. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from subprocess import run 18 19import argparse 20import multiprocessing 21import os 22import re 23import shutil 24 25 26LLVM_DIR = os.path.abspath(os.path.join('..', 'llvm')) 27 28LLVM_CONFIGS = os.path.abspath(os.path.join('..', 'configs')) 29 30LLVM_OBJS = os.path.join(os.getcwd(), 'llvm_objs') 31 32LLVM_TARGETS = [ 33 ('AArch64', ('__aarch64__',)), 34 ('ARM', ('__arm__',)), 35 ('X86', ('__i386__', '__x86_64__')), 36 ('Mips', ('__mips__',)), 37] 38 39LLVM_TRIPLES = { 40 'android': [ 41 ('__x86_64__', 'x86_64-linux-android'), 42 ('__i386__', 'i686-linux-android'), 43 ('__arm__', 'armv7-linux-androideabi'), 44 ('__aarch64__', 'aarch64-linux-android'), 45 ], 46 'linux': [ 47 ('__x86_64__', 'x86_64-unknown-linux-gnu'), 48 ('__i386__', 'i686-pc-linux-gnu'), 49 ('__arm__', 'armv7-linux-gnueabihf'), 50 ('__aarch64__', 'aarch64-linux-gnu'), 51 ('__mips__', 'mipsel-linux-gnu'), 52 ('__mips64', 'mips64el-linux-gnuabi64'), 53 ], 54 'darwin': [ 55 ('__x86_64__', 'x86_64-apple-darwin'), 56 ], 57} 58 59LLVM_OPTIONS = [ 60 '-DCMAKE_BUILD_TYPE=Release', 61 '-DLLVM_TARGETS_TO_BUILD=' + ';'.join(t[0] for t in LLVM_TARGETS), 62 '-DLLVM_ENABLE_TERMINFO=OFF', 63 '-DLLVM_ENABLE_LIBXML2=OFF', 64 '-DLLVM_ENABLE_LIBEDIT=OFF', 65 '-DLLVM_ENABLE_LIBPFM=OFF', 66 '-DLLVM_ENABLE_ZLIB=OFF', 67] 68 69 70def _parse_args(): 71 parser = argparse.ArgumentParser() 72 parser.add_argument('name', help='destination name', 73 choices=['android', 'linux', 'darwin']) 74 parser.add_argument('-j', '--jobs', help='parallel compilation', type=int) 75 return parser.parse_args() 76 77 78def build_llvm(num_jobs): 79 """Build LLVM and get all generated files.""" 80 if num_jobs is None: 81 num_jobs = multiprocessing.cpu_count() 82 83 os.makedirs(LLVM_OBJS, exist_ok=True) 84 run(['cmake', LLVM_DIR] + LLVM_OPTIONS, cwd=LLVM_OBJS) 85 run(['make', '-j' + str(num_jobs)], cwd=LLVM_OBJS) 86 87 88def list_files(src_base, src, dst_base, suffixes): 89 """Enumerate the files that are under `src` and end with one of the 90 `suffixes` and yield the source path and the destination path.""" 91 src_base = os.path.abspath(src_base) 92 src = os.path.join(src_base, src) 93 for base_dir, dirnames, filenames in os.walk(src): 94 for filename in filenames: 95 if os.path.splitext(filename)[1] in suffixes: 96 relative = os.path.relpath(base_dir, src_base) 97 yield (os.path.join(base_dir, filename), 98 os.path.join(dst_base, relative, filename)) 99 100 101def copy_common_generated_files(dst_base): 102 """Copy platform-independent generated files.""" 103 suffixes = {'.inc', '.h', '.def'} 104 subdirs = [ 105 os.path.join('include', 'llvm', 'IR'), 106 os.path.join('include', 'llvm', 'Support'), 107 os.path.join('lib', 'IR'), 108 os.path.join('lib', 'Target', 'AArch64'), 109 os.path.join('lib', 'Target', 'ARM'), 110 os.path.join('lib', 'Target', 'X86'), 111 os.path.join('lib', 'Target', 'Mips'), 112 os.path.join('lib', 'Transforms', 'InstCombine'), 113 ] 114 for subdir in subdirs: 115 for src, dst in list_files(LLVM_OBJS, subdir, dst_base, suffixes): 116 os.makedirs(os.path.dirname(dst), exist_ok=True) 117 shutil.copyfile(src, dst) 118 119 120def copy_platform_file(platform, src, dst): 121 """Copy platform-dependent generated files and add platform-specific 122 modifications.""" 123 124 # LLVM configuration patterns to be post-processed. 125 llvm_target_pattern = re.compile('^LLVM_[A-Z_]+\\(([A-Za-z0-9_]+)\\)$') 126 llvm_native_pattern = re.compile( 127 '^#define LLVM_NATIVE_([A-Z]+) (LLVMInitialize)?(.*)$') 128 llvm_triple_pattern = re.compile('^#define (LLVM_[A-Z_]+_TRIPLE) "(.*)"$') 129 llvm_define_pattern = re.compile('^#define ([A-Za-z0-9_]+) (.*)$') 130 131 # LLVM configurations to be undefined. 132 undef_names = [ 133 'BACKTRACE_HEADER', 134 'ENABLE_BACKTRACES', 135 'ENABLE_CRASH_OVERRIDES', 136 'HAVE_BACKTRACE', 137 'HAVE_POSIX_SPAWN', 138 'HAVE_PTHREAD_GETNAME_NP', 139 'HAVE_PTHREAD_SETNAME_NP', 140 'HAVE_TERMIOS_H', 141 'HAVE_ZLIB_H', 142 'HAVE__UNWIND_BACKTRACE', 143 ] 144 145 # Build architecture-specific conditions. 146 conds = {} 147 for arch, defs in LLVM_TARGETS: 148 conds[arch] = ' || '.join('defined(' + v + ')' for v in defs) 149 150 # Get a set of platform-specific triples. 151 triples = LLVM_TRIPLES[platform] 152 153 with open(src, 'r') as src_file: 154 os.makedirs(os.path.dirname(dst), exist_ok=True) 155 with open(dst, 'w') as dst_file: 156 for line in src_file: 157 match = llvm_target_pattern.match(line) 158 if match: 159 arch = match.group(1) 160 print('#if ' + conds[arch], file=dst_file) 161 print(line, file=dst_file, end='') 162 print('#endif', file=dst_file) 163 continue 164 165 match = llvm_native_pattern.match(line) 166 if match: 167 name = match.group(1) 168 init = match.group(2) or '' 169 arch = match.group(3) 170 end = '' 171 if arch.lower().endswith(name.lower()): 172 end = arch[-len(name):] 173 directive = '#if ' 174 for arch, defs in LLVM_TARGETS: 175 print(directive + conds[arch], file=dst_file) 176 print('#define LLVM_NATIVE_' + name + ' ' + 177 init + arch + end, file=dst_file) 178 directive = '#elif ' 179 print('#else', file=dst_file) 180 print('#error "unknown architecture"', file=dst_file) 181 print('#endif', file=dst_file) 182 continue 183 184 match = llvm_triple_pattern.match(line) 185 if match: 186 name = match.group(1) 187 directive = '#if' 188 for defs, triple in triples: 189 print(directive + ' defined(' + defs + ')', 190 file=dst_file) 191 print('#define ' + name + ' "' + triple + '"', 192 file=dst_file) 193 directive = '#elif' 194 print('#else', file=dst_file) 195 print('#error "unknown architecture"', file=dst_file) 196 print('#endif', file=dst_file) 197 continue 198 199 match = llvm_define_pattern.match(line) 200 if match and match.group(1) in undef_names: 201 print('/* #undef ' + match.group(1) + ' */', file=dst_file) 202 continue 203 204 print(line, file=dst_file, end='') 205 206 207def copy_platform_generated_files(platform, dst_base): 208 """Copy platform-specific generated files.""" 209 suffixes = {'.inc', '.h', '.def'} 210 src_dir = os.path.join('include', 'llvm', 'Config') 211 for src, dst in list_files(LLVM_OBJS, src_dir, dst_base, suffixes): 212 copy_platform_file(platform, src, dst) 213 214 215def main(): 216 args = _parse_args() 217 build_llvm(args.jobs) 218 copy_common_generated_files(os.path.join(LLVM_CONFIGS, 'common')) 219 copy_platform_generated_files( 220 args.name, os.path.join(LLVM_CONFIGS, args.name)) 221 222 223if __name__ == '__main__': 224 main() 225