diff --git a/SConstruct b/SConstruct index 072e966b7a7acc5b3cc1981790e9aa41ad8b84d2..707690c8f109f76bffabed0b82e3adc9153bc25d 100644 --- a/SConstruct +++ b/SConstruct @@ -46,7 +46,6 @@ if 'DESTDIR' in env: print >>sys.stderr, "--!!-- you want; files will be installed in" print >>sys.stderr, "--!!-- %s" % (calcInstallPath("$prefix"),) -env['LLVM_CONFIG'] = "llvm-config" if 'includedir' in env: env['incpath'] = calcInstallPath("$includedir", "hammer") else: @@ -91,6 +90,16 @@ AddOption("--in-place", action="store_true", help="Build in-place, rather than in the build/<variant> tree") +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)") dbg = env.Clone(VARIANT='debug') dbg.MergeFlags("-g -O0") @@ -105,7 +114,11 @@ else: 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 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"], @@ -126,113 +139,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")) +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) - -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" + 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_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) @@ -249,16 +271,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 80d96bf45c85822f888c896dfa479417ae298edc..cdbe6c7372ccc638efbf81b8d5cd2db7ed83cc6b 100644 --- a/src/SConscript +++ b/src/SConscript @@ -6,14 +6,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", @@ -61,10 +62,15 @@ parsers = ['parsers/%s.c'%s for s in 'unimplemented', 'whitespace', 'xor', - 'value']] + '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', @@ -89,29 +95,33 @@ ctests = ['t_benchmark.c', 't_misc.c', 't_regression.c'] -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('hammer', 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) Default(libhammer_shared, libhammer_static) @@ -124,24 +134,27 @@ env.Install("$backendsincpath", backends_headers) testenv = env.Clone() testenv.Append(LIBS=['hammer'], 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) -testenv.Append(LIBS=['stdc++'], LIBPATH=['.']) + testenv.Append(LIBS=['stdc++'], LIBPATH=['.']) + 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 8f53a9321aaebfd3c9b698a18e2c794f2153704e..d46d1d96ae1bf18eb9e9b0a4cc651c063771c5e9 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;