Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify handling of ll file generations in test code. NFC #8378

Merged
merged 1 commit into from
May 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 43 additions & 54 deletions tests/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,8 @@ def hardcode_arguments(self, filename, args):
js = open(filename).read()
create_test_file(filename, js.replace('run();', 'run(%s + Module["arguments"]);' % str(args)))

def prep_ll_run(self, filename, ll_file, force_recompile=False, build_ll_hook=None):
# force_recompile = force_recompile or os.path.getsize(filename + '.o.ll') > 50000
def prep_ll_file(self, output_file, input_file, force_recompile=False, build_ll_hook=None):
# force_recompile = force_recompile or os.path.getsize(filename + '.ll') > 50000
# If the file is big, recompile just to get ll_opts
# Recompiling just for dfe in ll_opts is too costly

Expand All @@ -446,43 +446,44 @@ def fix_target(ll_filename):
with open(ll_filename, 'w') as f:
f.write(contents)

output_obj = output_file + '.o'
output_ll = output_file + '.ll'

if force_recompile or build_ll_hook:
if ll_file.endswith(('.bc', '.o')):
if ll_file != filename + '.o':
shutil.copy(ll_file, filename + '.o')
Building.llvm_dis(filename)
if input_file.endswith(('.bc', '.o')):
if input_file != output_obj:
shutil.copy(input_file, output_obj)
Building.llvm_dis(output_obj, output_ll)
else:
shutil.copy(ll_file, filename + '.o.ll')
fix_target(filename + '.o.ll')
shutil.copy(input_file, output_ll)
fix_target(output_ll)

if build_ll_hook:
need_post = build_ll_hook(filename)
Building.llvm_as(filename)
shutil.move(filename + '.o.ll', filename + '.o.ll.pre') # for comparisons later
Building.llvm_dis(filename)
need_post = build_ll_hook(output_file)
Building.llvm_as(output_ll, output_obj)
shutil.move(output_ll, output_ll + '.pre') # for comparisons later
Building.llvm_dis(output_obj, output_ll)
if build_ll_hook and need_post:
build_ll_hook(filename)
Building.llvm_as(filename)
shutil.move(filename + '.o.ll', filename + '.o.ll.post') # for comparisons later
Building.llvm_dis(filename)
build_ll_hook(output_file)
Building.llvm_as(output_ll, output_obj)
shutil.move(output_ll, output_ll + '.post') # for comparisons later
Building.llvm_dis(output_obj, output_ll)

Building.llvm_as(filename)
Building.llvm_as(output_ll, output_obj)
else:
if ll_file.endswith('.ll'):
safe_copy(ll_file, filename + '.o.ll')
fix_target(filename + '.o.ll')
Building.llvm_as(filename)
if input_file.endswith('.ll'):
safe_copy(input_file, output_ll)
fix_target(output_ll)
Building.llvm_as(output_ll, output_obj)
else:
safe_copy(ll_file, filename + '.o')
safe_copy(input_file, output_obj)

return output_obj

def get_emcc_args(self):
# TODO(sbc): We should probably unify Building.COMPILER_TEST_OPTS and self.emcc_args
return self.serialize_settings() + self.emcc_args + Building.COMPILER_TEST_OPTS

# Generate JS from ll
def ll_to_js(self, filename):
Building.emcc(filename + '.o', self.get_emcc_args(), filename + '.o.js')

# Build JavaScript code from source code
def build(self, src, dirname, filename, main_file=None,
additional_files=[], libraries=[], includes=[], build_ll_hook=None,
Expand All @@ -501,7 +502,7 @@ def build(self, src, dirname, filename, main_file=None,
# copy whole directory, and use a specific main .cpp file
# (rmtree() fails on Windows if the current working directory is inside the tree.)
if os.getcwd().startswith(os.path.abspath(dirname)):
os.chdir(os.path.join(dirname, '..'))
os.chdir(os.path.join(dirname, '..'))
shutil.rmtree(dirname)
shutil.copytree(src, dirname)
shutil.move(os.path.join(dirname, main_file), filename)
Expand Down Expand Up @@ -539,10 +540,10 @@ def build(self, src, dirname, filename, main_file=None,
self.fail("Linkage error")

# Finalize
self.prep_ll_run(filename, object_file, build_ll_hook=build_ll_hook)
self.prep_ll_file(filename, object_file, build_ll_hook=build_ll_hook)

# BC => JS
self.ll_to_js(filename)
Building.emcc(object_file, self.get_emcc_args(), object_file + '.js')
else:
# "fast", new path: just call emcc and go straight to JS
all_files = [filename] + additional_files + libraries
Expand Down Expand Up @@ -976,15 +977,23 @@ def do_run(self, src, expected_output, args=[], output_nicerizer=None,
basename = 'src.c'
Building.COMPILER = to_cc(Building.COMPILER)

dirname = self.get_dir()
filename = os.path.join(dirname, basename)
if not no_build:
self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes,
if no_build:
if src:
js_file = src
else:
js_file = basename + '.o.js'
else:
dirname = self.get_dir()
filename = os.path.join(dirname, basename)
self.build(src, dirname, filename, main_file=main_file,
additional_files=additional_files, libraries=libraries,
includes=includes,
build_ll_hook=build_ll_hook, post_build=post_build)
js_file = filename + '.o.js'
self.assertExists(js_file)

# Run in both JavaScript engines, if optimizing - significant differences there (typed arrays)
js_engines = self.filtered_js_engines(js_engines)
js_file = filename + '.o.js'
if len(js_engines) == 0:
self.skipTest('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG)
if len(js_engines) > 1 and not self.use_all_engines:
Expand All @@ -1007,32 +1016,12 @@ def do_run(self, src, expected_output, args=[], output_nicerizer=None,
print('(test did not pass in JS engine: %s)' % engine)
raise

# shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging

if self.save_JS:
global test_index
self.hardcode_arguments(js_file, args)
shutil.copyfile(js_file, os.path.join(TEMP_DIR, str(test_index) + '.js'))
test_index += 1

# No building - just process an existing .ll file (or .bc, which we turn into .ll)
def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None,
output_nicerizer=None, force_recompile=False,
build_ll_hook=None, assert_returncode=None):
filename = os.path.join(self.get_dir(), 'src.cpp')

self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook)

self.ll_to_js(filename)

self.do_run(None,
expected_output,
args,
no_build=True,
js_engines=js_engines,
output_nicerizer=output_nicerizer,
assert_returncode=assert_returncode)

def get_freetype_library(self):
self.set_setting('DEAD_FUNCTIONS', self.get_setting('DEAD_FUNCTIONS') + ['_inflateEnd', '_inflate', '_inflateReset', '_inflateInit2_'])

Expand Down
78 changes: 45 additions & 33 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ def test_cube2hash(self):
for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'),
('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'),
('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]:
self.do_run('', 'hash value: ' + output, [text], no_build=True)
self.do_run('src.cpp.o.js', 'hash value: ' + output, [text], no_build=True)

def test_unaligned(self):
self.skipTest('LLVM marks the reads of s as fully aligned, making this test invalid')
Expand Down Expand Up @@ -592,6 +592,17 @@ def test_getgep(self):
# Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP
self.do_run_in_out_file_test('tests', 'core', 'test_getgep')

# No compiling from C/C++ - just process an existing .o/.ll/.bc file.
def do_run_object(self, obj_file, expected_output=None, **kwargs):
js_file = os.path.basename(obj_file) + '.js'
Building.emcc(obj_file, self.get_emcc_args(), js_file)
self.do_run(js_file, expected_output, no_build=True, **kwargs)

def do_ll_run(self, filename, expected_output=None, **kwargs):
output_base = os.path.basename(filename)
objfile = self.prep_ll_file(output_base, filename)
self.do_run_object(objfile, expected_output, **kwargs)

def test_multiply_defined_symbols(self):
create_test_file('a1.c', 'int f() { return 1; }')
create_test_file('a2.c', 'void x() {}')
Expand All @@ -617,7 +628,7 @@ def test_multiply_defined_symbols(self):

Building.link_to_object(['main.c.o', 'liba.a', 'libb.a'], 'all.o')

self.do_ll_run('all.o', 'result: 1')
self.do_run_object('all.o', 'result: 1')

def test_if(self):
self.do_run_in_out_file_test('tests', 'core', 'test_if')
Expand Down Expand Up @@ -1065,9 +1076,9 @@ def test_exceptions_3(self):
print('0')
self.do_run(src, 'Caught C string: a c string\nDone.', ['0'])
print('1')
self.do_run(src, 'Caught exception: std::exception\nDone.', ['1'], no_build=True)
self.do_run(None, 'Caught exception: std::exception\nDone.', ['1'], no_build=True)
print('2')
self.do_run(src, 'Caught exception: Hello\nDone.', ['2'], no_build=True)
self.do_run(None, 'Caught exception: Hello\nDone.', ['2'], no_build=True)

def test_exceptions_white_list(self):
self.set_setting('DISABLE_EXCEPTION_CATCHING', 2)
Expand Down Expand Up @@ -5269,9 +5280,11 @@ def test_constglobalunion(self):

def test_fannkuch(self):
results = [(1, 0), (2, 1), (3, 2), (4, 4), (5, 7), (6, 10), (7, 16), (8, 22)]
src = open(path_from_root('tests', 'fannkuch.cpp')).read()
self.build(src, self.get_dir(), 'fannkuch.cpp')
for i, j in results:
src = open(path_from_root('tests', 'fannkuch.cpp')).read()
self.do_run(src, 'Pfannkuchen(%d) = %d.' % (i, j), [str(i)], no_build=i > 1)
print(i, j)
self.do_run('fannkuch.cpp.o.js', 'Pfannkuchen(%d) = %d.' % (i, j), [str(i)], no_build=True)

def test_raytrace(self):
# TODO: Should we remove this test?
Expand All @@ -5287,17 +5300,19 @@ def test_fasta(self):
(50, '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''')]

old = self.emcc_args
orig_src = open(path_from_root('tests', 'fasta.cpp')).read()

def test(extra_args):
self.emcc_args = old + extra_args
for precision in [0, 1, 2]:
self.set_setting('PRECISE_F32', precision)
for t in ['float', 'double']:
print(precision, t)
src = open(path_from_root('tests', 'fasta.cpp')).read().replace('double', t)
for i, j in results:
self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i > 1)
shutil.copyfile('src.cpp.o.js', '%d_%s.js' % (precision, t))
src = orig_src.replace('double', t)
self.build(src, self.get_dir(), 'fasta.cpp')
for arg, output in results:
self.do_run('fasta.cpp.o.js', output, [str(arg)], lambda x, err: x.replace('\n', '*'), no_build=True)
shutil.copyfile('fasta.cpp.o.js', '%d_%s.js' % (precision, t))

test([])

Expand All @@ -5317,12 +5332,12 @@ def test_dlmalloc(self):

src = open(path_from_root('system', 'lib', 'dlmalloc.c')).read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c')).read()
self.do_run(src, '*1,0*', ['200', '1'])
self.do_run(src, '*400,0*', ['400', '400'], no_build=True)
self.do_run(None, '*400,0*', ['400', '400'], no_build=True)

# Linked version
src = open(path_from_root('tests', 'dlmalloc_test.c')).read()
self.do_run(src, '*1,0*', ['200', '1'])
self.do_run(src, '*400,0*', ['400', '400'], no_build=True)
self.do_run(None, '*400,0*', ['400', '400'], no_build=True)

# TODO: do this in other passes too, passing their opts into emcc
if self.emcc_args == []:
Expand All @@ -5331,8 +5346,8 @@ def test_dlmalloc(self):
try_delete('src.cpp.o.js')
run_process([PYTHON, EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=128MB', '-o', 'src.cpp.o.js'], stdout=PIPE, stderr=self.stderr_redirect)

self.do_run('x', '*1,0*', ['200', '1'], no_build=True)
self.do_run('x', '*400,0*', ['400', '400'], no_build=True)
self.do_run(None, '*1,0*', ['200', '1'], no_build=True)
self.do_run(None, '*400,0*', ['400', '400'], no_build=True)

# The same for new and all its variants
src = open(path_from_root('tests', 'new.cpp')).read()
Expand Down Expand Up @@ -5589,7 +5604,7 @@ def test_freetype(self):
includes=[path_from_root('tests', 'freetype', 'include')])

print('[issue 324 case 3]')
self.do_run('',
self.do_run(None,
open(path_from_root('tests', 'freetype', 'ref_4.txt')).read(),
['font.ttf', 'ea', '40', '32', '0'],
no_build=True)
Expand Down Expand Up @@ -5821,12 +5836,12 @@ def test_python(self):
print('lto:', lto)
if lto == 1:
self.emcc_args += ['--llvm-lto', '1']
self.do_ll_run(bitcode, pyoutput, args=['-S', '-c', pyscript])
self.do_run_object(bitcode, pyoutput, args=['-S', '-c', pyscript])

def test_lifetime(self):
self.do_ll_run(path_from_root('tests', 'lifetime.ll'), 'hello, world!\n')
if '-O1' in self.emcc_args or '-O2' in self.emcc_args:
assert 'a18' not in open('src.cpp.o.js').read(), 'lifetime stuff and their vars must be culled'
assert 'a18' not in open('lifetime.ll.o.js').read(), 'lifetime stuff and their vars must be culled'

# Test cases in separate files. Note that these files may contain invalid .ll!
# They are only valid enough for us to read for test purposes, not for llvm-as
Expand Down Expand Up @@ -5980,12 +5995,12 @@ def test_autodebug_bitcode(self):

# Autodebug the code
def do_autodebug(filename):
Building.llvm_dis(filename)
output = run_process([PYTHON, AUTODEBUGGER, filename + '.o.ll', filename + '.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).stdout
Building.llvm_dis(filename + '.o', filename + '.ll')
output = run_process([PYTHON, AUTODEBUGGER, filename + '.ll', filename + '.auto.ll'], stdout=PIPE, stderr=self.stderr_redirect).stdout
assert 'Success.' in output, output
# rebuild .bc
# TODO: use code in do_autodebug_post for this
self.prep_ll_run(filename, filename + '.o.ll.ll', force_recompile=True)
self.prep_ll_file(filename, filename + '.auto.ll', force_recompile=True)

# Run a test that should work, generating some code
test_path = path_from_root('tests', 'core', 'test_structs')
Expand All @@ -5998,7 +6013,7 @@ def do_autodebug(filename):
do_autodebug(filename)

# Compare to each other, and to expected output
self.do_ll_run(filename + '.o.ll.ll', 'AD:-1,1')
self.do_ll_run(filename + '.auto.ll', 'AD:-1,1')

# Test using build_ll_hook
src = '''
Expand Down Expand Up @@ -6183,31 +6198,28 @@ def test_dead_functions(self):
}
'''

def test(expected, args=[], no_build=False):
self.do_run(src, expected, args=args, no_build=no_build)
return open('src.cpp.o.js').read()

# Sanity check that it works and the dead function is emitted
js = test('*1*', ['x'])
self.do_run(src, '*1*', args=['x'])
js = open('src.cpp.o.js').read()
if self.run_name in ['default', 'asm1', 'asm2g']:
assert 'function _unused($' in js
test('*2*', no_build=True)
self.do_run(None, '*2*', no_build=True)

# Kill off the dead function, and check a code path using it aborts
self.set_setting('DEAD_FUNCTIONS', ['_unused'])
test('*2*')
test('abort(', args=['x'], no_build=True)
self.do_run(src, '*2*')
self.do_run(None, 'abort(', args=['x'], no_build=True)

# Kill off a library function, check code aborts
self.set_setting('DEAD_FUNCTIONS', ['_printf'])
test('abort(')
test('abort(', args=['x'], no_build=True)
self.do_run(src, 'abort(')
self.do_run(None, 'abort(', args=['x'], no_build=True)

def test_response_file(self):
response_data = '-o %s/response_file.o.js %s' % (self.get_dir(), path_from_root('tests', 'hello_world.cpp'))
create_test_file('rsp_file', response_data.replace('\\', '\\\\'))
run_process([PYTHON, EMCC, "@rsp_file"] + self.emcc_args)
self.do_run('', 'hello, world', basename='response_file', no_build=True)
self.do_run('response_file.o.js', 'hello, world', no_build=True)

def test_linker_response_file(self):
objfile = 'response_file.o'
Expand All @@ -6218,7 +6230,7 @@ def test_linker_response_file(self):
response_data = objfile + ' --export=foo'
create_test_file('rsp_file', response_data.replace('\\', '\\\\'))
run_process([PYTHON, EMCC, "-Wl,@rsp_file", '-o', 'response_file.o.js'] + self.emcc_args)
self.do_run('', 'hello, world', basename='response_file', no_build=True)
self.do_run('response_file.o.js', 'hello, world', no_build=True)

def test_exported_response(self):
src = r'''
Expand Down
Loading