* Uses option arguments instead of positional ones.
* Allows linking to dlmalloc.
* Accepts both .bc and .ll files and takes care of annotations.
* Allows running the LLVM optimization pass automatically.
* Updated test runner to use the new emscripten.py interface.

Refactoring:
* Moved settings.py to root folder. It no longer applies just to tests.
* Updated references to settings.py.
* Added an __init__.py to tools, so we don't have to hack around imports.
This commit is contained in:
max99x 2011-07-07 10:38:35 +03:00
Родитель d94b2423ae
Коммит 7dcdb044d3
6 изменённых файлов: 5881 добавлений и 44 удалений

Просмотреть файл

@ -1,43 +1,182 @@
#!/usr/bin/python
#!/usr/bin/python2
import os, sys, subprocess
import argparse
import json
import os
import subprocess
import sys
import tempfile
import tools.shared as shared
abspath = os.path.abspath(os.path.dirname(__file__))
def path_from_root(*pathelems):
return os.path.join(os.path.sep, *(abspath.split(os.sep) + list(pathelems)))
exec(open(path_from_root('tools', 'shared.py'), 'r').read())
COMPILER = path_from_root('src', 'compiler.js')
# TODO: Clean up temporary files.
def path_from_root(*target):
"""Returns the absolute path to the target from the emscripten root."""
abspath = os.path.abspath(os.path.dirname(__file__))
return os.path.join(os.path.sep, *(abspath.split(os.sep) + list(target)))
def get_temp_file(suffix):
"""Returns a named temp file with the given prefix."""
return tempfile.NamedTemporaryFile(
dir=shared.TEMP_DIR, suffix=suffix, delete=False)
def assemble(filepath):
"""Converts human-readable LLVM assembly to binary LLVM bitcode.
Args:
filepath: The path to the file to assemble. If the name ends with ".bc", the
file is assumed to be in bitcode format already.
Returns:
The path to the assembled file.
"""
if not filepath.endswith('.bc'):
out = get_temp_file('.bc')
ret = subprocess.call([shared.LLVM_AS, '-o=-', filepath], stdout=out)
out.close()
if ret != 0: raise RuntimeError('Could not assemble %s.' % filepath)
filepath = out.name
return filepath
def disassemble(filepath):
"""Converts binary LLVM bitcode to human-readable LLVM assembly.
Args:
filepath: The path to the file to disassemble. If the name ends with ".ll",
the file is assumed to be in human-readable assembly format already.
Returns:
The path to the disassembled file.
"""
if not filepath.endswith('.ll'):
out = get_temp_file('.ll')
command = [shared.LLVM_DIS, '-o=-', filepath] + shared.LLVM_DIS_OPTS
ret = subprocess.call(command, stdout=out)
out.close()
if ret != 0: raise RuntimeError('Could not disassemble %s.' % filepath)
filepath = out.name
return filepath
def optimize(filepath):
"""Runs LLVM's optimization passes on a given bitcode file.
Args:
filepath: The path to the bitcode file to optimize.
Returns:
The path to the optimized file.
"""
out = get_temp_file('.bc')
ret = subprocess.call([shared.LLVM_OPT, '-O3', '-o=-', filepath], stdout=out)
out.close()
if ret != 0: raise RuntimeError('Could not optimize %s.' % filepath)
return out.name
def link(*objects):
"""Links multiple LLVM bitcode files into a single file.
Args:
objects: The bitcode files to link.
Returns:
The path to the linked file.
"""
out = get_temp_file('.bc')
ret = subprocess.call([shared.LLVM_LINK] + list(objects), stdout=out)
out.close()
if ret != 0: raise RuntimeError('Could not link %s.' % objects)
return out.name
def compile_malloc():
"""Compiles dlmalloc to LLVM bitcode and returns the path to the .bc file."""
src = path_from_root('src', 'dlmalloc.c')
out = get_temp_file('.bc')
clang = shared.to_cc(shared.CLANG)
include_dir = '-I' + path_from_root('src', 'include')
command = [clang, '-c', '-g', '-emit-llvm', '-m32', '-o-', include_dir, src]
ret = subprocess.call(command, stdout=out)
out.close()
if ret != 0: raise RuntimeError('Could not compile dlmalloc.')
return out.name
def emscript(infile, settings, outfile):
"""Runs the emscripten LLVM-to-JS compiler.
Args:
infile: The path to the input LLVM assembly file.
settings: JSON-formatted string of settings that overrides the values
defined in src/settings.js.
outfile: The file where the output is written.
"""
data = open(infile, 'r').read()
compiler = path_from_root('src', 'compiler.js')
subprocess.Popen(shared.COMPILER_ENGINE + [compiler],
stdin=subprocess.PIPE,
stdout=outfile,
cwd=path_from_root('src'),
stderr=subprocess.STDOUT).communicate(settings + '\n' + data)
outfile.close()
def main(args):
# Construct a final linked and disassembled file.
args.infile = assemble(args.infile)
if args.dlmalloc: args.infile = link(args.infile, compile_malloc())
if args.optimize: args.infile = optimize(args.infile)
args.infile = disassemble(args.infile)
# Prepare settings for serialization to JSON.
settings = {}
for setting in args.settings:
name, value = setting.split('=', 1)
settings[name] = json.loads(value)
# Adjust sign correction for dlmalloc.
if args.dlmalloc:
CORRECT_SIGNS = int(settings.get('CORRECT_SIGNS', 0))
if CORRECT_SIGNS in (0, 2):
path = path_from_root('src', 'dlmalloc.c')
old_lines = json.loads(settings.get('CORRECT_SIGNS_LINES', '[]'))
line_nums = [4816, 4191, 4246, 4199, 4205, 4235, 4227]
lines = old_lines + [path + ':' + str(i) for i in line_nums]
settings['CORRECT_SIGNS'] = 2
settings['CORRECT_SIGNS_LINES'] = lines
# Compile the assembly to Javascript.
emscript(args.infile, json.dumps(settings), args.outfile)
def emscripten(filename, settings, outfile):
data = open(filename, 'r').read()
try:
cwd = os.getcwd()
except:
cwd = None
os.chdir(os.path.dirname(COMPILER))
subprocess.Popen(COMPILER_ENGINE + [COMPILER], stdin=subprocess.PIPE, stdout=outfile, stderr=subprocess.STDOUT).communicate(settings+'\n'+data)
if outfile: outfile.close()
if cwd is not None:
os.chdir(cwd)
if __name__ == '__main__':
if sys.argv.__len__() not in range(2,6):
print '''
Emscripten usage: emscripten.py INFILE [SETTINGS] [OUTPUT_FILE]
INFILE must be in human-readable LLVM disassembly form (i.e., as text,
not binary).
SETTINGS is an optional set of compiler settings, overriding the defaults,
in JSON format. See src/settings.js.
OUTPUT_FILE is the file to create with the output. If not given, we write
to stdout.
You should have an ~/.emscripten file set up, see tests/settings.py, which
in particular includes COMPILER_ENGINE.
'''
else:
settings = sys.argv[2] if len(sys.argv) >= 3 else "{}"
outfile = open(sys.argv[3], 'w') if len(sys.argv) >= 4 else None
emscripten(sys.argv[1], settings, outfile)
parser = argparse.ArgumentParser(
description='Compile LLVM assembly to Javascript.',
epilog='You should have an ~/.emscripten file set up; see settings.py.')
parser.add_argument('infile',
help='The LLVM assembly file to compile, either in '
'human-readable (*.ll) or in bitcode (*.bc) format.')
parser.add_argument('-O', '--optimize',
default=False,
action='store_true',
help='Run LLVM -O3 optimizations on the input.')
parser.add_argument('-m', '--dlmalloc',
default=False,
action='store_true',
help='Use dlmalloc. Without, uses a dummy allocator.')
parser.add_argument('-o', '--outfile',
default=sys.stdout,
type=argparse.FileType('w'),
help='Where to write the output; defaults to stdout.')
parser.add_argument('-s', '--settings',
default=[],
nargs=argparse.ZERO_OR_MORE,
metavar='FOO=BAR',
help='Overrides for settings defined in settings.js.')
main(parser.parse_args())

Просмотреть файл

5699
src/dlmalloc.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -5,7 +5,7 @@ See settings.py file for options&params. Edit as needed.
'''
from subprocess import Popen, PIPE, STDOUT
import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re
import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re, json
# Setup
@ -19,7 +19,7 @@ exec(open(path_from_root('tools', 'shared.py'), 'r').read())
try:
assert COMPILER_OPTS != None
except:
raise Exception('Cannot find "COMPILER_OPTS" definition. Is ~/.emscripten set up properly? You may need to copy the template at ~/tests/settings.py into it.')
raise Exception('Cannot find "COMPILER_OPTS" definition. Is ~/.emscripten set up properly? You may need to copy the template from settings.py into it.')
# Paths
@ -246,7 +246,8 @@ class RunnerCore(unittest.TestCase):
exported_settings[setting] = value
except:
pass
compiler_output = timeout_run(Popen([EMSCRIPTEN, filename + '.o.ll', str(exported_settings).replace("'", '"'), filename + '.o.js'], stdout=PIPE, stderr=STDOUT), TIMEOUT, 'Compiling')
settings = ['%s=%s' % (k, json.dumps(v)) for k, v in exported_settings.items()]
compiler_output = timeout_run(Popen([EMSCRIPTEN, filename + '.o.ll', '-o', filename + '.o.js', '-s'] + settings, stdout=PIPE, stderr=STDOUT), TIMEOUT, 'Compiling')
# Detect compilation crashes and errors
if compiler_output is not None and 'Traceback' in compiler_output and 'in test_' in compiler_output: print compiler_output; assert 0

0
tools/__init__.py Normal file
Просмотреть файл

Просмотреть файл

@ -1,10 +1,9 @@
import shutil, time
import shutil, time, os
from subprocess import Popen, PIPE, STDOUT
CONFIG_FILE = os.path.expanduser('~/.emscripten')
if not os.path.exists(CONFIG_FILE):
shutil.copy(path_from_root('tests', 'settings.py'), CONFIG_FILE)
shutil.copy(path_from_root('settings.py'), CONFIG_FILE)
exec(open(CONFIG_FILE, 'r').read())
# Tools
@ -67,4 +66,3 @@ def line_splitter(data):
def limit_size(string, MAX=80*20):
if len(string) < MAX: return string
return string[0:MAX] + '...'