diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-12-07 14:50:07 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-12-07 14:50:07 -0800 |
commit | 33ca644b5b3e78af1998db308efda6a57c42bd28 (patch) | |
tree | fc35b9a14272c00543d1f7edafa752a61cb6de6a | |
parent | 19e25f10d8808a5f27e5fea0df15de3f326a31b5 (diff) | |
parent | ab399ca532fb1deb7607f6cc2feb01186d75ec2b (diff) |
Merge branch 'incoming'
-rw-r--r-- | AUTHORS | 1 | ||||
-rwxr-xr-x | emcc | 70 | ||||
-rw-r--r-- | src/parseTools.js | 2 | ||||
-rw-r--r-- | system/include/emscripten/bind.h | 2 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 5 | ||||
-rw-r--r-- | tests/embind/embind_test.js | 2 | ||||
-rwxr-xr-x | tests/runner.py | 123 | ||||
-rw-r--r-- | tools/shared.py | 38 |
8 files changed, 200 insertions, 43 deletions
@@ -41,4 +41,5 @@ a license to everyone to use it as detailed in LICENSE.) * Joel Martin <github@martintribe.org> * Manuel Wellmann <manuel.wellmann@autodesk.com> (copyright owned by Autodesk, Inc.) * Xuejie Xiao <xxuejie@gmail.com> +* Dominic Wong <dom@slowbunyip.org> @@ -374,10 +374,12 @@ or LLVM assembly files in human-readable form. emcc is affected by several environment variables. For details, view the source of emcc (search for 'os.environ'). +emcc: supported targets: llvm bitcode, javascript, NOT elf +(autoconf likes to see elf above to enable shared object support) ''' % (this, this, this) exit(0) elif len(sys.argv) == 2 and sys.argv[1] == '-v': # -v with no inputs - print 'emcc (Emscripten GCC-like replacement) 2.0' + print 'emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) 2.0' exit(subprocess.call([shared.CLANG, '-v'])) def is_minus_s_for_emcc(newargs,i): @@ -540,6 +542,15 @@ for i in range(len(sys.argv)-1): sys.argv = sys.argv[:i] + sys.argv[i+2:] break +specified_target = target +target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate +target_basename = unsuffixed_basename(target) + +if '.' in target: + final_suffix = target.split('.')[-1] +else: + final_suffix = '' + if header: # header or such if len(sys.argv) >= 3: # if there is a source and a target, then copy, otherwise do nothing sys.argv = filter(lambda arg: not arg.startswith('-I'), sys.argv) @@ -751,7 +762,7 @@ try: if i > 0: prev = newargs[i-1] - if prev in ['-MT', '-install_name']: continue # ignore this gcc-style argument + if prev in ['-MT', '-install_name', '-I', '-L']: continue # ignore this gcc-style argument if not arg.startswith('-') and (arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg)): # we already removed -o <target>, so all these should be inputs newargs[i] = '' @@ -789,6 +800,17 @@ try: newargs = [ arg for arg in newargs if arg is not '' ] + # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode + has_dash_c = '-c' in newargs + if has_dash_c: + assert has_source_inputs, 'Must have source code inputs to use -c' + target = target_basename + '.o' + final_suffix = 'o' + + # do not link in libs when just generating object code (not an 'executable', i.e. JS, or a library) + if ('.' + final_suffix) in BITCODE_SUFFIXES: + libs = [] + # Find library files for lib in libs: if DEBUG: print >> sys.stderr, 'emcc: looking for library "%s"' % lib @@ -816,22 +838,6 @@ try: newargs += CC_ADDITIONAL_ARGS - specified_target = target - target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate - - target_basename = unsuffixed_basename(target) - - # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode - has_dash_c = '-c' in newargs - if has_dash_c: - assert has_source_inputs, 'Must have source code inputs to use -c' - target = target_basename + '.o' - - if '.' in target: - final_suffix = target.split('.')[-1] - else: - final_suffix = '' - assert not (Compression.on and final_suffix != 'html'), 'Compression only works when generating HTML' # If we are using embind and generating JS, now is the time to link in bind.cpp @@ -904,7 +910,7 @@ try: assert len(original_input_files) == 1 or not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + ':' + str(original_input_files) # We have a specified target (-o <target>), which is not JavaScript or HTML, and # we have multiple files: Link them - if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files) + if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files), specified_target shared.Building.link(temp_files, specified_target, remove_duplicates=remove_duplicates) exit(0) @@ -985,20 +991,32 @@ try: for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), ('libcxxabi', create_libcxxabi, fix_libcxxabi, libcxxabi_symbols), ('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]: - need = [] - has = [] + need = set() + has = set() for temp_file in temp_files: symbols = shared.Building.llvm_nm(temp_file) for library_symbol in library_symbols: if library_symbol in symbols.undefs: - need.append(library_symbol) + need.add(library_symbol) if library_symbol in symbols.defs: - has.append(library_symbol) - if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need |%s| and have |%s|' % (name, str(need), str(has)) - if force or (need and not has): + has.add(library_symbol) + for haz in has: # remove symbols that are supplied by another of the inputs + if haz in need: + need.remove(haz) + if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need %s and have %s' % (name, str(need), str(has)) + if force or len(need) > 0: # We need to build and link the library in if DEBUG: print >> sys.stderr, 'emcc: including %s' % name - extra_files_to_link.append(shared.Cache.get(name, create)) + libfile = shared.Cache.get(name, create) + if len(has) > 0: + # remove the symbols we do not need + fixed = in_temp(uniquename(libfile)) + '.bc' + shutil.copyfile(libfile, fixed) + for haz in has: + if DEBUG: print >> sys.stderr, 'emcc: including: removing symbol "%s" that we have' % haz + shared.Building.remove_symbol(fixed, haz) + libfile = fixed + extra_files_to_link.append(libfile) force = True if fix: fix() diff --git a/src/parseTools.js b/src/parseTools.js index 7387bf31..4a76a9a2 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1750,7 +1750,7 @@ function processMathop(item) { } } } - case 'uitofp': case 'sitofp': return low1 + ' + ' + high1 + '*4294967296'; + case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'); case 'fptoui': case 'fptosi': return finish(splitI64(idents[0])); case 'icmp': { switch (variant) { diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 4a82eacb..8ce95de7 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -218,7 +218,7 @@ namespace emscripten { template<typename ClassType, typename ReturnType, typename... Args> struct MethodInvoker { typedef ReturnType (ClassType::*MemberPointer)(Args...); - typename internal::BindingType<ReturnType>::WireType invoke( + static typename internal::BindingType<ReturnType>::WireType invoke( ClassType* ptr, const MemberPointer& method, typename internal::BindingType<Args>::WireType... args diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index e7b4d985..dc052d1a 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -96,6 +96,10 @@ public: this->v = v;
}
+ int returnIntPlusFive( int x ) {
+ return x + 5;
+ }
+
static int some_class_method(int i) {
return i;
}
@@ -286,6 +290,7 @@ EMSCRIPTEN_BINDINGS(([]() { .constructor<val>()
.method("getVal", &ValHolder::getVal)
.method("setVal", &ValHolder::setVal)
+ .method("returnIntPlusFive", &ValHolder::returnIntPlusFive)
.classmethod("some_class_method", &ValHolder::some_class_method)
;
function("emval_test_return_ValHolder", &emval_test_return_ValHolder);
diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js index e01f0236..8c61553b 100644 --- a/tests/embind/embind_test.js +++ b/tests/embind/embind_test.js @@ -137,6 +137,8 @@ module({ c.setVal('1234'); assert.equal('1234', c.getVal()); + assert.equal(1239, c.returnIntPlusFive(1234)); + c.delete(); assert.equal(0, cm.count_emval_handles()); }, diff --git a/tests/runner.py b/tests/runner.py index cef14e95..1480afe7 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -1083,6 +1083,32 @@ m_divisor is 1091269979 ''' self.do_run(src, '<=0') + def test_i64_qdouble(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include <stdio.h> + typedef long long qint64; /* 64 bit signed */ + typedef double qreal; + + + int main(int argc, char **argv) + { + qreal c = 111; + qint64 d = -111 + (argc - 1); + c += d; + if (c < -1 || c > 1) + { + printf("Failed!\n"); + } + else + { + printf("Succeeded!\n"); + } + }; + ''' + self.do_run(src, 'Succeeded!') + def test_i32_mul_precise(self): if self.emcc_args == None: return self.skip('needs ta2') @@ -5779,6 +5805,39 @@ int main(int argc, char **argv) { ]: self.do_run(src.replace('{{{ NEW }}}', new).replace('{{{ DELETE }}}', delete), '*1,0*') + def test_dlmalloc_partial(self): + if self.emcc_args is None: return self.skip('only emcc will link in dlmalloc') + # present part of the symbols of dlmalloc, not all + src = open(path_from_root('tests', 'new.cpp')).read().replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' +void * +operator new(size_t size) +{ + printf("new %d!\\n", size); + return malloc(size); +} +''' + self.do_run(src, 'new 4!\n*1,0*') + + def test_dlmalloc_partial_2(self): + if self.emcc_args is None or 'SAFE_HEAP' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') + # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. + src = r''' + #include <stdio.h> + #include <stdlib.h> + void *malloc(size_t size) + { + return (void*)123; + } + int main() { + void *x = malloc(10); + printf("got %p\n", x); + free(x); + printf("freed the faker\n"); + return 1; + } +''' + self.do_run(src, 'got 0x7b\nfreed') + def test_libcxx(self): self.do_run(open(path_from_root('tests', 'hashtest.cpp')).read(), 'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march') @@ -7850,6 +7909,70 @@ f.close() self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs' + def test_multiply_defined_libsymbols(self): + lib = "int mult() { return 1; }" + lib_name = os.path.join(self.get_dir(), 'libA.c') + open(lib_name, 'w').write(lib) + a2 = "void x() {}" + a2_name = os.path.join(self.get_dir(), 'a2.c') + open(a2_name, 'w').write(a2) + b2 = "void y() {}" + b2_name = os.path.join(self.get_dir(), 'b2.c') + open(b2_name, 'w').write(b2) + main = r''' + #include <stdio.h> + int mult(); + int main() { + printf("result: %d\n", mult()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(lib_name, output_filename='libA.so') + + Building.emcc(a2_name, ['-L.', '-lA']) + Building.emcc(b2_name, ['-L.', '-lA']) + + Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js') + + self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_multiply_defined_libsymbols_2(self): + a = "int x() { return 55; }" + a_name = os.path.join(self.get_dir(), 'a.c') + open(a_name, 'w').write(a) + b = "int y() { return 2; }" + b_name = os.path.join(self.get_dir(), 'b.c') + open(b_name, 'w').write(b) + c = "int z() { return 5; }" + c_name = os.path.join(self.get_dir(), 'c.c') + open(c_name, 'w').write(c) + main = r''' + #include <stdio.h> + int x(); + int y(); + int z(); + int main() { + printf("result: %d\n", x() + y() + z()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(a_name) # a.c.o + Building.emcc(b_name) # b.c.o + Building.emcc(c_name) # c.c.o + lib_name = os.path.join(self.get_dir(), 'libLIB.a') + Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b + + # a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though + Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js') + + self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_abspaths(self): # Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones. diff --git a/tools/shared.py b/tools/shared.py index e292dbf3..86a5ba77 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -707,6 +707,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e return generated_libs @staticmethod + def remove_symbol(filename, symbol): + Popen([LLVM_EXTRACT, filename, '-delete', '-glob=' + symbol, '-o', filename], stderr=PIPE).communicate() + Popen([LLVM_EXTRACT, filename, '-delete', '-func=' + symbol, '-o', filename], stderr=PIPE).communicate() + + @staticmethod def link(files, target, remove_duplicates=False): actual_files = [] unresolved_symbols = set(['main']) # tracking unresolveds is necessary for .a linking, see below. (and main is always a necessary symbol) @@ -731,6 +736,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e os.makedirs(temp_dir) os.chdir(temp_dir) contents = filter(lambda x: len(x) > 0, Popen([LLVM_AR, 't', f], stdout=PIPE).communicate()[0].split('\n')) + #print >> sys.stderr, ' considering archive', f, ':', contents if len(contents) == 0: print >> sys.stderr, 'Warning: Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f else: @@ -741,20 +747,23 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e Popen([LLVM_AR, 'x', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory contents = map(lambda content: os.path.join(temp_dir, content), contents) contents = filter(os.path.exists, map(os.path.abspath, contents)) - needed = False # We add or do not add the entire archive. We let llvm dead code eliminate parts we do not need, instead of - # doing intra-dependencies between archive contents - for content in contents: - new_symbols = Building.llvm_nm(content) - # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld) - if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1: - needed = True - if needed: + added_contents = set() + added = True + while added: # recursively traverse until we have everything we need + added = False for content in contents: - if Building.is_bitcode(content): - new_symbols = Building.llvm_nm(content) - resolved_symbols = resolved_symbols.union(new_symbols.defs) - unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs) - actual_files.append(content) + if content in added_contents: continue + new_symbols = Building.llvm_nm(content) + # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld) + #print >> sys.stderr, 'need', content, '?', unresolved_symbols, 'and we can supply', new_symbols.defs + if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1: + if Building.is_bitcode(content): + #print >> sys.stderr, ' adding object', content + resolved_symbols = resolved_symbols.union(new_symbols.defs) + unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs) + actual_files.append(content) + added_contents.add(content) + added = True finally: os.chdir(cwd) try_delete(target) @@ -771,8 +780,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e print >> sys.stderr, 'emcc: warning: removing duplicates in', actual for dupe in dupes: print >> sys.stderr, 'emcc: warning: removing duplicate', dupe - Popen([LLVM_EXTRACT, actual, '-delete', '-glob=' + dupe, '-o', actual], stderr=PIPE).communicate() - Popen([LLVM_EXTRACT, actual, '-delete', '-func=' + dupe, '-o', actual], stderr=PIPE).communicate() + Building.remove_symbol(actual, dupe) Popen([LLVM_EXTRACT, actual, '-delete', '-glob=.str', '-o', actual], stderr=PIPE).communicate() # garbage that appears here seen_symbols = seen_symbols.union(symbols.defs) |