diff options
17 files changed, 231 insertions, 12 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index c95ee18..b002656 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -257,10 +257,14 @@ int dummy; # Languages that can mix with C or C++ but don't support unity builds yet # because the syntax we use for unity builds is specific to C/++/ObjC/++. + # Assembly files cannot be unitified and neither can LLVM IR files langs_cant_unity = ('d', 'fortran') def get_target_source_can_unity(self, target, source): if isinstance(source, File): source = source.fname + if self.environment.is_llvm_ir(source) or \ + self.environment.is_assembly(source): + return False suffix = os.path.splitext(source)[1][1:] for lang in self.langs_cant_unity: if not lang in target.compilers: @@ -374,6 +378,9 @@ int dummy; # because we need `header_deps` to be fully generated in the above loop. for src in generated_source_files: src_list.append(src) + if self.environment.is_llvm_ir(src): + obj_list.append(self.generate_llvm_ir_compile(target, outfile, src)) + continue obj_list.append(self.generate_single_compile(target, outfile, src, True, header_deps=header_deps)) @@ -409,7 +416,9 @@ int dummy; for f, src in target_sources.items(): if not self.environment.is_header(src): src_list.append(src) - if is_unity and self.get_target_source_can_unity(target, src): + if self.environment.is_llvm_ir(src): + obj_list.append(self.generate_llvm_ir_compile(target, outfile, src)) + elif is_unity and self.get_target_source_can_unity(target, src): abs_src = os.path.join(self.environment.get_build_dir(), src.rel_to_builddir(self.build_to_src)) unity_src.append(abs_src) @@ -1365,6 +1374,36 @@ rule FORTRAN_DEP_HACK ''' outfile.write(template % cmd) + def generate_llvm_ir_compile_rule(self, compiler, is_cross, outfile): + if getattr(self, 'created_llvm_ir_rule', False): + return + rule = 'rule llvm_ir{}_COMPILER\n'.format('_CROSS' if is_cross else '') + args = [' '.join([ninja_quote(i) for i in compiler.get_exelist()]), + ' '.join(self.get_cross_info_lang_args(compiler, is_cross)), + ' '.join(compiler.get_output_args('$out')), + ' '.join(compiler.get_compile_only_args())] + if mesonlib.is_windows(): + command_template = ' command = {} @$out.rsp\n' \ + ' rspfile = $out.rsp\n' \ + ' rspfile_content = {} $ARGS {} {} $in\n' + else: + command_template = ' command = {} {} $ARGS {} {} $in\n' + command = command_template.format(*args) + description = ' description = Compiling LLVM IR object $in.\n' + outfile.write(rule) + outfile.write(command) + outfile.write(description) + outfile.write('\n') + self.created_llvm_ir_rule = True + + def get_cross_info_lang_args(self, lang, is_cross): + if is_cross: + try: + return self.environment.cross_info.config['properties'][lang + '_args'] + except KeyError: + pass + return [] + def generate_compile_rule_for(self, langname, compiler, qstr, is_cross, outfile): if langname == 'java': if not is_cross: @@ -1399,12 +1438,7 @@ rule FORTRAN_DEP_HACK if d != '$out' and d != '$in': d = qstr % d quoted_depargs.append(d) - cross_args = [] - if is_cross: - try: - cross_args = self.environment.cross_info.config['properties'][langname + '_args'] - except KeyError: - pass + cross_args = self.get_cross_info_lang_args(langname, is_cross) if mesonlib.is_windows(): command_template = ''' command = %s @$out.rsp rspfile = $out.rsp @@ -1477,6 +1511,8 @@ rule FORTRAN_DEP_HACK qstr = quote_char + "%s" + quote_char for compiler in self.build.compilers: langname = compiler.get_language() + if compiler.get_id() == 'clang': + self.generate_llvm_ir_compile_rule(compiler, False, outfile) self.generate_compile_rule_for(langname, compiler, qstr, False, outfile) self.generate_pch_rule_for(langname, compiler, qstr, False, outfile) if self.environment.is_cross_build(): @@ -1488,6 +1524,8 @@ rule FORTRAN_DEP_HACK cclist = self.build.compilers for compiler in cclist: langname = compiler.get_language() + if compiler.get_id() == 'clang': + self.generate_llvm_ir_compile_rule(compiler, True, outfile) self.generate_compile_rule_for(langname, compiler, qstr, True, outfile) self.generate_pch_rule_for(langname, compiler, qstr, True, outfile) outfile.write('\n') @@ -1680,9 +1718,39 @@ rule FORTRAN_DEP_HACK def get_link_debugfile_args(self, linker, target, outname): return linker.get_link_debugfile_args(outname) + def generate_llvm_ir_compile(self, target, outfile, src): + compiler = get_compiler_for_source(target.compilers.values(), src) + commands = [] + # Compiler args for compiling this target + commands += compilers.get_base_compile_args(self.environment.coredata.base_options, + compiler) + if isinstance(src, (RawFilename, File)): + src_filename = src.fname + elif os.path.isabs(src): + src_filename = os.path.basename(src) + else: + src_filename = src + obj_basename = src_filename.replace('/', '_').replace('\\', '_') + rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename) + rel_obj += '.' + self.environment.get_object_suffix() + commands += self.get_compile_debugfile_args(compiler, target, rel_obj) + if isinstance(src, RawFilename): + rel_src = src.fname + elif isinstance(src, File): + rel_src = src.rel_to_builddir(self.build_to_src) + else: + raise InvalidArguments('Invalid source type: {!r}'.format(src)) + # Write the Ninja build command + compiler_name = 'llvm_ir{}_COMPILER'.format('_CROSS' if target.is_cross else '') + element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src) + commands = self.dedup_arguments(commands) + element.add_item('ARGS', commands) + element.write(outfile) + return rel_obj + def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]): """ - Compiles C/C++, ObjC/ObjC++, and D sources + Compiles C/C++, ObjC/ObjC++, Fortran, and D sources """ if isinstance(src, str) and src.endswith('.h'): raise AssertionError('BUG: sources should not contain headers') diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 2ee4aac..e474d2e 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -59,6 +59,16 @@ def is_source(fname): suffix = fname.split('.')[-1] return suffix in clike_suffixes +def is_assembly(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + return fname.split('.')[-1].lower() == 's' + +def is_llvm_ir(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + return fname.split('.')[-1] == 'll' + def is_object(fname): if hasattr(fname, 'fname'): fname = fname.fname @@ -2018,6 +2028,8 @@ class GnuCompiler: if self.gcc_type != GCC_OSX: self.base_options.append('b_lundef') self.base_options.append('b_asneeded') + # All GCC backends can do assembly + self.can_compile_suffixes.add('s') def get_colorout_args(self, colortype): if mesonlib.version_compare(self.version, '>=4.9.0'): @@ -2071,8 +2083,6 @@ class GnuCCompiler(GnuCompiler, CCompiler): def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) GnuCompiler.__init__(self, gcc_type, defines) - # Gcc can do asm, too. - self.can_compile_suffixes.add('s') self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} @@ -2185,6 +2195,8 @@ class ClangCompiler(): if self.clang_type != CLANG_OSX: self.base_options.append('b_lundef') self.base_options.append('b_asneeded') + # All Clang backends can do assembly and LLVM IR + self.can_compile_suffixes.update(['ll', 's']) def get_pic_args(self): if self.clang_type in (CLANG_WIN, CLANG_OSX): @@ -2240,8 +2252,6 @@ class ClangCCompiler(ClangCompiler, CCompiler): def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) ClangCompiler.__init__(self, clang_type) - # Clang can do asm, too. - self.can_compile_suffixes.add('s') self.warn_args = {'1': ['-Wall', '-Winvalid-pch'], '2': ['-Wall', '-Wextra', '-Winvalid-pch'], '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']} diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index e673d50..44c5965 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -271,6 +271,12 @@ class Environment(): def is_source(self, fname): return is_source(fname) + def is_assembly(self, fname): + return is_assembly(fname) + + def is_llvm_ir(self, fname): + return is_llvm_ir(fname) + def is_object(self, fname): return is_object(fname) diff --git a/test cases/common/126 llvm ir and assembly/main.c b/test cases/common/126 llvm ir and assembly/main.c new file mode 100644 index 0000000..edfb29e --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/main.c @@ -0,0 +1,9 @@ +unsigned square_unsigned (unsigned a); + +int +main (int argc, char * argv[]) +{ + if (square_unsigned (2) != 4) + return -1; + return 0; +} diff --git a/test cases/common/126 llvm ir and assembly/main.cpp b/test cases/common/126 llvm ir and assembly/main.cpp new file mode 100644 index 0000000..4f576e7 --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/main.cpp @@ -0,0 +1,12 @@ + +extern "C" { + unsigned square_unsigned (unsigned a); +} + +int +main (int argc, char * argv[]) +{ + if (square_unsigned (2) != 4) + return -1; + return 0; +} diff --git a/test cases/common/126 llvm ir and assembly/meson.build b/test cases/common/126 llvm ir and assembly/meson.build new file mode 100644 index 0000000..8b9be33 --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/meson.build @@ -0,0 +1,17 @@ +project('llvm-ir', 'c', 'cpp') + +foreach lang : ['c', 'cpp'] + cc_id = meson.get_compiler(lang).get_id() + if cc_id == 'clang' + e = executable('square_ir_' + lang, 'square.ll', 'main.' + lang) + test('test IR square' + lang, e) + endif + # FIXME: msvc does not support passing assembly to cl.exe, but you can pass + # it to ml.exe and get a compiled object. Meson should add support for + # transparently building assembly with ml.exe with MSVC. + if cc_id != 'msvc' + cpu = host_machine.cpu_family() + e = executable('square_asm_' + lang, 'square-' + cpu + '.S', 'main.' + lang) + test('test ASM square' + lang, e, args : [e.full_path()]) + endif +endforeach diff --git a/test cases/common/126 llvm ir and assembly/square-arm.S b/test cases/common/126 llvm ir and assembly/square-arm.S new file mode 100644 index 0000000..58a5b1b --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/square-arm.S @@ -0,0 +1,9 @@ +#include "symbol-underscore.h" + +.text +.globl SYMBOL_NAME(square_unsigned) + +SYMBOL_NAME(square_unsigned): + mul r1, r0, r0 + mov r0, r1 + mov pc, lr diff --git a/test cases/common/126 llvm ir and assembly/square-x86.S b/test cases/common/126 llvm ir and assembly/square-x86.S new file mode 100644 index 0000000..f3d67e7 --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/square-x86.S @@ -0,0 +1,9 @@ +#include "symbol-underscore.h" + +.text +.globl SYMBOL_NAME(square_unsigned) + +SYMBOL_NAME(square_unsigned): + movl 4(%esp), %eax + imull %eax, %eax + retl diff --git a/test cases/common/126 llvm ir and assembly/square-x86_64.S b/test cases/common/126 llvm ir and assembly/square-x86_64.S new file mode 100644 index 0000000..ea73a9d --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/square-x86_64.S @@ -0,0 +1,9 @@ +#include "symbol-underscore.h" + +.text +.globl SYMBOL_NAME(square_unsigned) + +SYMBOL_NAME(square_unsigned): + imull %edi, %edi + movl %edi, %eax + retq diff --git a/test cases/common/126 llvm ir and assembly/square.ll b/test cases/common/126 llvm ir and assembly/square.ll new file mode 100644 index 0000000..7c321aa --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/square.ll @@ -0,0 +1,4 @@ +define i32 @square_unsigned(i32 %a) { + %1 = mul i32 %a, %a + ret i32 %1 +} diff --git a/test cases/common/126 llvm ir and assembly/symbol-underscore.h b/test cases/common/126 llvm ir and assembly/symbol-underscore.h new file mode 100644 index 0000000..508cf50 --- /dev/null +++ b/test cases/common/126 llvm ir and assembly/symbol-underscore.h @@ -0,0 +1,5 @@ +#if defined(__WIN32__) || defined(__APPLE__) +# define SYMBOL_NAME(name) _##name +#else +# define SYMBOL_NAME(name) name +#endif diff --git a/test cases/common/127 cpp and asm/meson.build b/test cases/common/127 cpp and asm/meson.build new file mode 100644 index 0000000..55c3d3c --- /dev/null +++ b/test cases/common/127 cpp and asm/meson.build @@ -0,0 +1,16 @@ +project('c++ and assembly test', 'cpp') + +sources = ['trivial.cc'] +# If the compiler cannot compile assembly, don't use it +if meson.get_compiler('cpp').get_id() != 'msvc' + cpu = host_machine.cpu_family() + sources += ['retval-' + cpu + '.S'] + cpp_args = ['-DUSE_ASM'] + message('Using ASM') +else + cpp_args = ['-DNO_USE_ASM'] +endif + +exe = executable('trivialprog', sources, + cpp_args : cpp_args) +test('runtest', exe) diff --git a/test cases/common/127 cpp and asm/retval-arm.S b/test cases/common/127 cpp and asm/retval-arm.S new file mode 100644 index 0000000..8b37197 --- /dev/null +++ b/test cases/common/127 cpp and asm/retval-arm.S @@ -0,0 +1,8 @@ +#include "symbol-underscore.h" + +.text +.globl SYMBOL_NAME(get_retval) + +SYMBOL_NAME(get_retval): + mov r0, #0 + mov pc, lr diff --git a/test cases/common/127 cpp and asm/retval-x86.S b/test cases/common/127 cpp and asm/retval-x86.S new file mode 100644 index 0000000..06bd75c --- /dev/null +++ b/test cases/common/127 cpp and asm/retval-x86.S @@ -0,0 +1,8 @@ +#include "symbol-underscore.h" + +.text +.globl SYMBOL_NAME(get_retval) + +SYMBOL_NAME(get_retval): + xorl %eax, %eax + retl diff --git a/test cases/common/127 cpp and asm/retval-x86_64.S b/test cases/common/127 cpp and asm/retval-x86_64.S new file mode 100644 index 0000000..638921e --- /dev/null +++ b/test cases/common/127 cpp and asm/retval-x86_64.S @@ -0,0 +1,8 @@ +#include "symbol-underscore.h" + +.text +.globl SYMBOL_NAME(get_retval) + +SYMBOL_NAME(get_retval): + xorl %eax, %eax + retq diff --git a/test cases/common/127 cpp and asm/symbol-underscore.h b/test cases/common/127 cpp and asm/symbol-underscore.h new file mode 100644 index 0000000..508cf50 --- /dev/null +++ b/test cases/common/127 cpp and asm/symbol-underscore.h @@ -0,0 +1,5 @@ +#if defined(__WIN32__) || defined(__APPLE__) +# define SYMBOL_NAME(name) _##name +#else +# define SYMBOL_NAME(name) name +#endif diff --git a/test cases/common/127 cpp and asm/trivial.cc b/test cases/common/127 cpp and asm/trivial.cc new file mode 100644 index 0000000..d2928c0 --- /dev/null +++ b/test cases/common/127 cpp and asm/trivial.cc @@ -0,0 +1,16 @@ +#include<iostream> + +extern "C" { + int get_retval(void); +} + +int main(int argc, char **argv) { + std::cout << "C++ seems to be working." << std::endl; +#if defined(USE_ASM) + return get_retval(); +#elif defined(NO_USE_ASM) + return 0; +#else + #error "Forgot to pass asm define" +#endif +} |