From fe5a282ae8cdc7d5ad91cb4c30448332eab20b0a Mon Sep 17 00:00:00 2001 From: Andrea Shepard <andrea@persephoneslair.org> Date: Sun, 20 Nov 2016 22:07:07 +0000 Subject: [PATCH] Add --disable-llvm-backend/--enable-llvm-backend build-time options, and move llvm.c to src/backends/llvm/ subdir --- SConstruct | 249 ++++++++++++++++++--------------- src/SConscript | 87 +++++++----- src/backends/{ => llvm}/llvm.c | 4 +- 3 files changed, 188 insertions(+), 152 deletions(-) rename src/backends/{ => llvm}/llvm.c (99%) diff --git a/SConstruct b/SConstruct index 0eb3605e..e768a019 100644 --- a/SConstruct +++ b/SConstruct @@ -56,7 +56,6 @@ if 'DESTDIR' in env: print('--!!-- you want; files will be installed in', file=sys.stderr) print('--!!-- %s' % (calcInstallPath('$prefix'),), file=sys.stderr) -env['LLVM_CONFIG'] = "llvm-config" if 'includedir' in env: env['incpath'] = calcInstallPath("$includedir", "hammer") else: @@ -98,9 +97,20 @@ AddOption('--tests', action='store_true', help='Build tests') +AddOption("--disable-llvm-backend", + dest="use_llvm", + default=False, + action="store_false", + help="Disable the LLVM backend (and don't require LLVM library dependencies)") + +AddOption("--enable-llvm-backend", + dest="use_llvm", + default=False, + action="store_true", + help="Enable the LLVM backend (and require LLVM library dependencies)") + env['CC'] = os.getenv('CC') or env['CC'] env['CXX'] = os.getenv('CXX') or env['CXX'] -env["LLVM_CONFIG"] = os.getenv("LLVM_CONFIG") or env["LLVM_CONFIG"] if os.getenv('CC') == 'clang' or env['PLATFORM'] == 'darwin': env.Replace(CC='clang', @@ -133,6 +143,11 @@ elif env['PLATFORM'] == 'win32': else: env.MergeFlags('-lrt') +if GetOption("use_llvm"): + # Overridable default path to llvm-config + env['LLVM_CONFIG'] = "llvm-config" + env["LLVM_CONFIG"] = os.getenv("LLVM_CONFIG") or env["LLVM_CONFIG"] + if GetOption('coverage'): env.Append(CFLAGS=['--coverage'], CXXFLAGS=['--coverage'], @@ -165,113 +180,122 @@ env['ENV'].update(x for x in os.environ.items() if x[0].startswith('CCC_')) #rootpath = env['ROOTPATH'] = os.path.abspath('.') #env.Append(CPPPATH=os.path.join('#', 'hammer')) -# Set up LLVM config stuff to export +if GetOption("use_llvm"): + # Set up LLVM config stuff to export -# some llvm versions are old and will not work; some require --system-libs -# with llvm-config, and some will break if given it -llvm_config_version = subprocess.Popen('%s --version' % env["LLVM_CONFIG"], \ - shell=True, \ - stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() -if LooseVersion(llvm_config_version[0]) < LooseVersion("3.6"): - print "This LLVM version %s is too old" % llvm_config_version[0].strip() - Exit(1) + # some llvm versions are old and will not work; some require --system-libs + # with llvm-config, and some will break if given it + llvm_config_version = subprocess.Popen('%s --version' % env["LLVM_CONFIG"], \ + shell=True, \ + stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() + if LooseVersion(llvm_config_version[0]) < LooseVersion("3.6"): + print "This LLVM version %s is too old" % llvm_config_version[0].strip() + Exit(1) -if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9") and \ - LooseVersion(llvm_config_version[0]) >= LooseVersion("3.5"): - llvm_system_libs_flag = "--system-libs" -else: - llvm_system_libs_flag = "" - -# Only keep one copy of this -llvm_required_components = "core executionengine mcjit analysis x86codegen x86info" -# Stubbing this out so we can implement static-only mode if needed later -llvm_use_shared = True -# Can we ask for shared/static from llvm-config? -if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9"): - # Nope - llvm_linkage_type_flag = "" - llvm_use_computed_shared_lib_name = True -else: - # Woo, they finally fixed the dumb - llvm_use_computed_shared_lib_name = False - if llvm_use_shared: - llvm_linkage_type_flag = "--link-shared" + if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9") and \ + LooseVersion(llvm_config_version[0]) >= LooseVersion("3.5"): + llvm_system_libs_flag = "--system-libs" else: - llvm_linkage_type_flag = "--link-static" - -if llvm_use_computed_shared_lib_name: - # Okay, pull out the major and minor version numbers (barf barf) - p = re.compile("^(\d+)\.(\d+).*$") - m = p.match(llvm_config_version[0]) - if m: - llvm_computed_shared_lib_name = "LLVM-%d.%d" % ((int)(m.group(1)), (int)(m.group(2))) + llvm_system_libs_flag = "" + + # Only keep one copy of this + llvm_required_components = "core executionengine mcjit analysis x86codegen x86info" + # Stubbing this out so we can implement static-only mode if needed later + llvm_use_shared = True + # Can we ask for shared/static from llvm-config? + if LooseVersion(llvm_config_version[0]) < LooseVersion("3.9"): + # Nope + llvm_linkage_type_flag = "" + llvm_use_computed_shared_lib_name = True else: - print "Couldn't compute shared library name from LLVM version '%s', but needed to" % \ - llvm_config_version[0] - Exit(1) -else: - # We won't be needing it - llvm_computed_shared_lib_name = None - -# llvm-config 'helpfully' supplies -g and -O flags; educate it with this -# custom ParseConfig function arg; make it a class with a method so we can -# pass it around with scons export/import - -class LLVMConfigSanitizer: - def sanitize(self, env, cmd, unique=1): - # cmd is output from llvm-config - flags = cmd.split() - # match -g or -O flags - p = re.compile("^-[gO].*$") - filtered_flags = [flag for flag in flags if not p.match(flag)] - filtered_cmd = ' '.join(filtered_flags) - # print "llvm_config_sanitize: \"%s\" => \"%s\"" % (cmd, filtered_cmd) - env.MergeFlags(filtered_cmd, unique) -llvm_config_sanitizer = LLVMConfigSanitizer() - -# LLVM defines, which the python bindings need -try: - llvm_config_cflags = subprocess.Popen('%s --cflags' % env["LLVM_CONFIG"], \ - shell=True, \ - stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() - flags = llvm_config_cflags[0].split() - # get just the -D ones - p = re.compile("^-D(.*)$") - llvm_defines = [p.match(flag).group(1) for flag in flags if p.match(flag)] -except: - print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"] - Exit(1) - -# Get the llvm includedir, which the python bindings need -try: - llvm_config_includes = subprocess.Popen('%s --includedir' % env["LLVM_CONFIG"], \ - shell=True, \ - stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() - llvm_includes = llvm_config_includes[0].splitlines() -except: - print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"] - Exit(1) - -# This goes here so we already know all the LLVM crap -# Make a fresh environment to parse the config into, to read out just LLVM stuff -llvm_dummy_env = Environment() -# Get LLVM stuff into LIBS/LDFLAGS -llvm_dummy_env.ParseConfig('%s --ldflags %s %s %s' % \ - (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, \ - llvm_required_components), \ - function=llvm_config_sanitizer.sanitize) -# Get the right -l lines in -if llvm_use_shared: + # Woo, they finally fixed the dumb + llvm_use_computed_shared_lib_name = False + if llvm_use_shared: + llvm_linkage_type_flag = "--link-shared" + else: + llvm_linkage_type_flag = "--link-static" + if llvm_use_computed_shared_lib_name: - llvm_dummy_env.Append(LIBS=[llvm_computed_shared_lib_name, ]) + # Okay, pull out the major and minor version numbers (barf barf) + p = re.compile("^(\d+)\.(\d+).*$") + m = p.match(llvm_config_version[0]) + if m: + llvm_computed_shared_lib_name = "LLVM-%d.%d" % ((int)(m.group(1)), (int)(m.group(2))) + else: + print "Couldn't compute shared library name from LLVM version '%s', but needed to" % \ + llvm_config_version[0] + Exit(1) else: - llvm_dummy_env.ParseConfig('%s %s --libs %s' % \ - (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ - function=llvm_config_sanitizer.sanitize) -llvm_dummy_env.Append(LIBS=['stdc++', ], ) + # We won't be needing it + llvm_computed_shared_lib_name = None + + # llvm-config 'helpfully' supplies -g and -O flags; educate it with this + # custom ParseConfig function arg; make it a class with a method so we can + # pass it around with scons export/import + + class LLVMConfigSanitizer: + def sanitize(self, env, cmd, unique=1): + # cmd is output from llvm-config + flags = cmd.split() + # match -g or -O flags + p = re.compile("^-[gO].*$") + filtered_flags = [flag for flag in flags if not p.match(flag)] + filtered_cmd = ' '.join(filtered_flags) + # print "llvm_config_sanitize: \"%s\" => \"%s\"" % (cmd, filtered_cmd) + env.MergeFlags(filtered_cmd, unique) + llvm_config_sanitizer = LLVMConfigSanitizer() + + # LLVM defines, which the python bindings need + try: + llvm_config_cflags = subprocess.Popen('%s --cflags' % env["LLVM_CONFIG"], \ + shell=True, \ + stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() + flags = llvm_config_cflags[0].split() + # get just the -D ones + p = re.compile("^-D(.*)$") + llvm_defines = [p.match(flag).group(1) for flag in flags if p.match(flag)] + except: + print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"] + Exit(1) + + # Get the llvm includedir, which the python bindings need + try: + llvm_config_includes = subprocess.Popen('%s --includedir' % env["LLVM_CONFIG"], \ + shell=True, \ + stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() + llvm_includes = llvm_config_includes[0].splitlines() + except: + print "%s failed. Make sure you have LLVM and clang installed." % env["LLVM_CONFIG"] + Exit(1) + + # This goes here so we already know all the LLVM crap + # Make a fresh environment to parse the config into, to read out just LLVM stuff + llvm_dummy_env = Environment() + # Get LLVM stuff into LIBS/LDFLAGS + llvm_dummy_env.ParseConfig('%s --ldflags %s %s %s' % \ + (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, \ + llvm_required_components), \ + function=llvm_config_sanitizer.sanitize) + # Get the right -l lines in + if llvm_use_shared: + if llvm_use_computed_shared_lib_name: + llvm_dummy_env.Append(LIBS=[llvm_computed_shared_lib_name, ]) + else: + llvm_dummy_env.ParseConfig('%s %s --libs %s' % \ + (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ + function=llvm_config_sanitizer.sanitize) + llvm_dummy_env.Append(LIBS=['stdc++', ], ) +#endif GetOption("use_llvm") + +# The .pc.in file has substs for llvm_lib_flags and llvm_libdir_flags, so if +# we aren't using LLVM, set them to the empty string +if GetOption("use_llvm"): + env['llvm_libdir_flags'] = llvm_dummy_env.subst('$_LIBDIRFLAGS') + env['llvm_lib_flags'] = llvm_dummy_env.subst('$_LIBFLAGS') +else: + env['llvm_libdir_flags'] = "" + env['llvm_lib_flags'] = "" -env['llvm_libdir_flags'] = llvm_dummy_env.subst('$_LIBDIRFLAGS') -env['llvm_lib_flags'] = llvm_dummy_env.subst('$_LIBFLAGS') pkgconfig = env.ScanReplace('libhammer.pc.in') Default(pkgconfig) env.Install("$pkgconfigpath", pkgconfig) @@ -288,16 +312,17 @@ Export('env') Export('testruns') Export('targets') # LLVM-related flags -Export('llvm_computed_shared_lib_name') -Export('llvm_config_sanitizer') -Export('llvm_config_version') -Export('llvm_defines') -Export('llvm_includes') -Export('llvm_linkage_type_flag') -Export('llvm_required_components') -Export('llvm_system_libs_flag') -Export('llvm_use_computed_shared_lib_name') -Export('llvm_use_shared') +if GetOption("use_llvm"): + Export('llvm_computed_shared_lib_name') + Export('llvm_config_sanitizer') + Export('llvm_config_version') + Export('llvm_defines') + Export('llvm_includes') + Export('llvm_linkage_type_flag') + Export('llvm_required_components') + Export('llvm_system_libs_flag') + Export('llvm_use_computed_shared_lib_name') + Export('llvm_use_shared') if not GetOption('in_place'): env['BUILD_BASE'] = 'build/$VARIANT' diff --git a/src/SConscript b/src/SConscript index ef8fdb05..8913224f 100644 --- a/src/SConscript +++ b/src/SConscript @@ -9,14 +9,15 @@ import subprocess Import('env testruns') # LLVM-related flags -Import('llvm_computed_shared_lib_name') -Import('llvm_config_sanitizer') -Import('llvm_config_version') -Import('llvm_linkage_type_flag') -Import('llvm_required_components') -Import('llvm_system_libs_flag') -Import('llvm_use_computed_shared_lib_name') -Import('llvm_use_shared') +if GetOption("use_llvm"): + Import('llvm_computed_shared_lib_name') + Import('llvm_config_sanitizer') + Import('llvm_config_version') + Import('llvm_linkage_type_flag') + Import('llvm_required_components') + Import('llvm_system_libs_flag') + Import('llvm_use_computed_shared_lib_name') + Import('llvm_use_shared') dist_headers = [ 'hammer.h', @@ -67,7 +68,12 @@ parsers = ['parsers/%s.c'%s for s in 'value']] backends = ['backends/%s.c' % s for s in - ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0', 'llvm']] + ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0']] + +# Add LLVM backend if enabled +if GetOption("use_llvm"): + llvm_backend_files = ['llvm.c'] + backends = backends + ['backends/llvm/%s' % s for s in llvm_backend_files] misc_hammer_parts = [ 'allocator.c', @@ -109,29 +115,33 @@ if env['PLATFORM'] == 'win32': # prevent collision between .lib from dll and .lib for static lib static_library_name = 'hammer_s' -env.ParseConfig('%s --cflags --ldflags' % env["LLVM_CONFIG"], function=llvm_config_sanitizer.sanitize) +if GetOption("use_llvm"): + env.ParseConfig('%s --cflags --ldflags' % env["LLVM_CONFIG"], function=llvm_config_sanitizer.sanitize) libhammer_static = env.StaticLibrary(static_library_name, parsers + backends + misc_hammer_parts) # Use a cloned env for the shared library so we can have library dependencies shared_env = env.Clone() -# Get LLVM stuff into LIBS/LDFLAGS -shared_env.ParseConfig('%s --ldflags %s %s %s' % \ - (env["LLVM_CONFIG"], llvm_system_libs_flag, llvm_linkage_type_flag, llvm_required_components), \ - function=llvm_config_sanitizer.sanitize) -# Get the right -l lines in -if llvm_use_shared: - if llvm_use_computed_shared_lib_name: - shared_env.Append(LIBS=[llvm_computed_shared_lib_name, ]) +if GetOption("use_llvm"): + # Get LLVM stuff into LIBS/LDFLAGS + shared_env.ParseConfig('%s --ldflags %s %s %s' % \ + (env["LLVM_CONFIG"], llvm_system_libs_flag, \ + llvm_linkage_type_flag, llvm_required_components), \ + function=llvm_config_sanitizer.sanitize) + # Get the right -l lines in + if llvm_use_shared: + if llvm_use_computed_shared_lib_name: + shared_env.Append(LIBS=[llvm_computed_shared_lib_name, ]) + else: + shared_env.ParseConfig('%s %s --libs %s' % \ + (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ + function=llvm_config_sanitizer.sanitize) else: + # Just grab the statics regardless of version shared_env.ParseConfig('%s %s --libs %s' % \ (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ function=llvm_config_sanitizer.sanitize) -else: - # Just grab the statics regardless of version - shared_env.ParseConfig('%s %s --libs %s' % \ - (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ - function=llvm_config_sanitizer.sanitize) -shared_env.Append(LIBS=['stdc++', ], LIBPATH=['.']) + shared_env.Append(LIBS=['stdc++', ], LIBPATH=['.']) + libhammer_shared = shared_env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts) if build_shared_library: @@ -150,24 +160,25 @@ if GetOption('with_tests'): testenv.Append(LIBS=['hammer']) testenv.Prepend(LIBPATH=['.']) testenv.ParseConfig('pkg-config --cflags --libs glib-2.0') - # Get LLVM stuff into LIBS/LDFLAGS - testenv.ParseConfig('%s --ldflags %s %s %s' % \ - (env["LLVM_CONFIG"], llvm_system_libs_flag, \ - llvm_linkage_type_flag, llvm_required_components), \ - function=llvm_config_sanitizer.sanitize) - # Get the right -l lines in - if llvm_use_shared: - if llvm_use_computed_shared_lib_name: - testenv.Append(LIBS=[llvm_computed_shared_lib_name, ]) + if GetOption("use_llvm"): + # Get LLVM stuff into LIBS/LDFLAGS + testenv.ParseConfig('%s --ldflags %s %s %s' % \ + (env["LLVM_CONFIG"], llvm_system_libs_flag, \ + llvm_linkage_type_flag, llvm_required_components), \ + function=llvm_config_sanitizer.sanitize) + # Get the right -l lines in + if llvm_use_shared: + if llvm_use_computed_shared_lib_name: + testenv.Append(LIBS=[llvm_computed_shared_lib_name, ]) + else: + testenv.ParseConfig('%s %s --libs %s' % \ + (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ + function=llvm_config_sanitizer.sanitize) else: + # Just grab the statics regardless of version testenv.ParseConfig('%s %s --libs %s' % \ (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ function=llvm_config_sanitizer.sanitize) - else: - # Just grab the statics regardless of version - testenv.ParseConfig('%s %s --libs %s' % \ - (env["LLVM_CONFIG"], llvm_linkage_type_flag, llvm_required_components), \ - function=llvm_config_sanitizer.sanitize) ctestexec = testenv.Program('test_suite', ctests + ['test_suite.c'], LINKFLAGS='--coverage' if testenv.GetOption('coverage') else None) ctest = Alias('testc', [ctestexec], ''.join(['env LD_LIBRARY_PATH=', os.path.dirname(ctestexec[0].path), ' ', ctestexec[0].path])) AlwaysBuild(ctest) diff --git a/src/backends/llvm.c b/src/backends/llvm/llvm.c similarity index 99% rename from src/backends/llvm.c rename to src/backends/llvm/llvm.c index 8f53a932..d46d1d96 100644 --- a/src/backends/llvm.c +++ b/src/backends/llvm/llvm.c @@ -4,8 +4,8 @@ #include <llvm-c/Core.h> #pragma GCC diagnostic pop #include <llvm-c/ExecutionEngine.h> -#include "../internal.h" -#include "../llvm.h" +#include "../../internal.h" +#include "../../llvm.h" typedef struct HLLVMParser_ { LLVMModuleRef mod; -- GitLab