aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc3
-rwxr-xr-xemscripten.py1
-rw-r--r--src/intertyper.js1
-rw-r--r--src/jsifier.js8
-rw-r--r--src/library.js4
-rw-r--r--src/library_gl.js5
-rw-r--r--src/parseTools.js22
-rw-r--r--src/preamble.js2
-rwxr-xr-xtests/runner.py166
-rw-r--r--tools/eliminator/eliminator-test-output.js8
-rw-r--r--tools/eliminator/eliminator-test.js12
-rw-r--r--tools/js-optimizer.js2
-rw-r--r--tools/shared.py14
13 files changed, 235 insertions, 13 deletions
diff --git a/emcc b/emcc
index 48631d64..2190ff1c 100755
--- a/emcc
+++ b/emcc
@@ -808,7 +808,8 @@ try:
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:
+ if ('.' + final_suffix) in BITCODE_SUFFIXES and len(libs) > 0:
+ print >> sys.stderr, 'emcc: warning: not linking against libraries since only compiling to bitcode'
libs = []
# Find library files
diff --git a/emscripten.py b/emscripten.py
index 15beb4ee..98dcb6bb 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -110,6 +110,7 @@ def emscript(infile, settings, outfile, libraries=[]):
elif line.find(' = type { ') > 0:
pre.append(line) # type
elif line.startswith('!'):
+ if line.startswith('!llvm.module'): continue # we can ignore that
meta.append(line) # metadata
else:
pre.append(line) # pre needs it so we know about globals in pre and funcs. So emit globals there
diff --git a/src/intertyper.js b/src/intertyper.js
index 7db1a2fe..1d18c3cf 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -919,6 +919,7 @@ function intertyper(data, sidePass, baseLineNums) {
processItem: function(item) {
return [{
intertype: 'resume',
+ ident: toNiceIdent(item.tokens[2].text),
lineNum: item.lineNum
}];
}
diff --git a/src/jsifier.js b/src/jsifier.js
index aaa827bb..4192cd3f 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -633,7 +633,7 @@ function JSify(data, functionsOnly, givenFunctions) {
} // otherwise, should have been set before!
if (func.setjmpTable) {
var setjmpTable = {};
- ret += indent + 'var setjmped = false;'; // set to true if we setjmp in this invocation
+ ret += indent + 'var mySetjmpIds = {};\n';
ret += indent + 'var setjmpTable = {';
func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },';
@@ -652,7 +652,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}).join('\n');
ret += '\n' + indent + ' default: assert(0, "bad label: " + label);\n' + indent + '}';
if (func.setjmpTable) {
- ret += ' } catch(e) { if (!setjmped) throw(e); if (!e.longjmp) throw(e); setjmpTable[e.label](e.value) }';
+ ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
}
} else {
ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
@@ -1049,8 +1049,10 @@ function JSify(data, functionsOnly, givenFunctions) {
return ret + ';';
});
makeFuncLineActor('resume', function(item) {
+ // If there is no current exception, set this one as it (during a resume, the current exception can be wiped out)
return (EXCEPTION_DEBUG ? 'Module.print("Resuming exception");' : '') +
- 'throw ' + makeGetValue('_llvm_eh_exception.buf', '0', 'void*') + ';';
+ 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, item.ident + '.f0', 'void*') + ' } ' +
+ 'throw ' + item.ident + '.f0;';
});
makeFuncLineActor('invoke', function(item) {
// Wrapping in a function lets us easily return values if we are
diff --git a/src/library.js b/src/library.js
index 5a8a9ae7..1bdd840d 100644
--- a/src/library.js
+++ b/src/library.js
@@ -5940,11 +5940,11 @@ LibraryManager.library = {
setjmp__inline: function(env) {
// Save the label
- return '(setjmped = true, ' + makeSetValue(env, '0', 'label', 'i32') + ', 0)';
+ return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32') + ', 0)';
},
longjmp: function(env, value) {
- throw { longjmp: true, label: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
+ throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
},
// ==========================================================================
diff --git a/src/library_gl.js b/src/library_gl.js
index b4098813..0f28a6a0 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -2099,6 +2099,11 @@ var LibraryGL = {
},
glColor4f: function(r, g, b, a) {
+ r = Math.max(Math.min(r, 1), 0);
+ g = Math.max(Math.min(g, 1), 0);
+ b = Math.max(Math.min(b, 1), 0);
+ a = Math.max(Math.min(a, 1), 0);
+
// TODO: make ub the default, not f, save a few mathops
if (GL.immediate.mode) {
var start = GL.immediate.vertexCounter << 2;
diff --git a/src/parseTools.js b/src/parseTools.js
index 4a76a9a2..b2093da3 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1328,7 +1328,27 @@ function makePointer(slab, pos, allocator, type, ptr) {
types = de[0];
}
}
- return 'allocate(' + slab + ', ' + JSON.stringify(types) + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')';
+ // JS engines sometimes say array initializers are too large. Work around that by chunking and calling concat to combine at runtime
+ var chunkSize = 10240;
+ function chunkify(array) {
+ // break very large slabs into parts
+ var ret = '';
+ var index = 0;
+ while (index < array.length) {
+ ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')' : '');
+ index += chunkSize;
+ }
+ return ret;
+ }
+ if (typeof slab == 'string' && evaled && evaled.length > chunkSize) {
+ slab = chunkify(evaled);
+ }
+ if (typeof types != 'string' && types.length > chunkSize) {
+ types = chunkify(types);
+ } else {
+ types = JSON.stringify(types);
+ }
+ return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')';
}
function makeGetSlabs(ptr, type, allowMultiple, unsigned) {
diff --git a/src/preamble.js b/src/preamble.js
index 9342bf2b..0917f977 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -276,6 +276,8 @@ Module['printProfiling'] = printProfiling;
//========================================
var __THREW__ = false; // Used in checking for thrown exceptions.
+var setjmpId = 1; // Used in setjmp/longjmp
+var setjmpLabels = {};
var ABORT = false;
diff --git a/tests/runner.py b/tests/runner.py
index 1480afe7..775225c6 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -2078,6 +2078,109 @@ Setjmp error execution path, level: 0
Exiting stack_manipulate_func, level: 0
''')
+ def test_longjmp3(self):
+ src = r'''
+ #include <setjmp.h>
+ #include <stdio.h>
+
+ typedef struct {
+ jmp_buf* jmp;
+ } jmp_state;
+
+ void setjmp_func(jmp_state* s, int level) {
+ jmp_buf* prev_jmp = s->jmp;
+ jmp_buf c_jmp;
+
+ if (level == 2) {
+ printf("level is 2, perform longjmp!\n");
+ longjmp(*(s->jmp), 1);
+ }
+
+ if (setjmp(c_jmp) == 0) {
+ printf("setjmp normal execution path, level: %d\n", level);
+ s->jmp = &c_jmp;
+ setjmp_func(s, level + 1);
+ } else {
+ printf("setjmp exception execution path, level: %d\n", level);
+ if (prev_jmp) {
+ printf("prev_jmp is not empty, continue with longjmp!\n");
+ s->jmp = prev_jmp;
+ longjmp(*(s->jmp), 1);
+ }
+ }
+
+ printf("Exiting setjmp function, level: %d\n", level);
+ }
+
+ int main(int argc, char *argv[]) {
+ jmp_state s;
+ s.jmp = NULL;
+
+ setjmp_func(&s, 0);
+
+ return 0;
+ }
+ '''
+ self.do_run(src, '''setjmp normal execution path, level: 0
+setjmp normal execution path, level: 1
+level is 2, perform longjmp!
+setjmp exception execution path, level: 1
+prev_jmp is not empty, continue with longjmp!
+setjmp exception execution path, level: 0
+Exiting setjmp function, level: 0
+''')
+
+ def test_longjmp4(self):
+ src = r'''
+ #include <setjmp.h>
+ #include <stdio.h>
+
+ typedef struct {
+ jmp_buf* jmp;
+ } jmp_state;
+
+ void second_func(jmp_state* s);
+
+ void first_func(jmp_state* s) {
+ jmp_buf* prev_jmp = s->jmp;
+ jmp_buf c_jmp;
+ volatile int once = 0;
+
+ if (setjmp(c_jmp) == 0) {
+ printf("Normal execution path of first function!\n");
+
+ s->jmp = &c_jmp;
+ second_func(s);
+ } else {
+ printf("Exception execution path of first function! %d\n", once);
+
+ if (!once) {
+ printf("Calling longjmp the second time!\n");
+ once = 1;
+ longjmp(*(s->jmp), 1);
+ }
+ }
+ }
+
+ void second_func(jmp_state* s) {
+ longjmp(*(s->jmp), 1);
+ }
+
+ int main(int argc, char *argv[]) {
+ jmp_state s;
+ s.jmp = NULL;
+
+ first_func(&s);
+
+ return 0;
+ }
+ '''
+ self.do_run(src, '''Normal execution path of first function!
+Exception execution path of first function! 0
+Calling longjmp the second time!
+Exception execution path of first function! 1
+''')
+
def test_exceptions(self):
if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1")
@@ -2217,6 +2320,69 @@ Exiting stack_manipulate_func, level: 0
expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read()
self.do_run(src, expected)
+ def test_multiexception(self):
+ Settings.DISABLE_EXCEPTION_CATCHING = 0
+ Settings.EXCEPTION_DEBUG = 0 # Messes up expected output.
+ src = r'''
+#include <stdio.h>
+
+static int current_exception_id = 0;
+
+typedef struct {
+ int jmp;
+} jmp_state;
+
+void setjmp_func(jmp_state* s, int level) {
+ int prev_jmp = s->jmp;
+ int c_jmp;
+
+ if (level == 2) {
+ printf("level is 2, perform longjmp!\n");
+ throw 1;
+ }
+
+ c_jmp = current_exception_id++;
+ try {
+ printf("setjmp normal execution path, level: %d, prev_jmp: %d\n", level, prev_jmp);
+ s->jmp = c_jmp;
+ setjmp_func(s, level + 1);
+ } catch (int catched_eid) {
+ printf("caught %d\n", catched_eid);
+ if (catched_eid == c_jmp) {
+ printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp);
+ if (prev_jmp != -1) {
+ printf("prev_jmp is not empty, continue with longjmp!\n");
+ s->jmp = prev_jmp;
+ throw s->jmp;
+ }
+ } else {
+ throw;
+ }
+ }
+
+ printf("Exiting setjmp function, level: %d, prev_jmp: %d\n", level, prev_jmp);
+}
+
+int main(int argc, char *argv[]) {
+ jmp_state s;
+ s.jmp = -1;
+
+ setjmp_func(&s, 0);
+
+ return 0;
+}
+'''
+ self.do_run(src, '''setjmp normal execution path, level: 0, prev_jmp: -1
+setjmp normal execution path, level: 1, prev_jmp: 0
+level is 2, perform longjmp!
+caught 1
+setjmp exception execution path, level: 1, prev_jmp: 0
+prev_jmp is not empty, continue with longjmp!
+caught 0
+setjmp exception execution path, level: 0, prev_jmp: -1
+Exiting setjmp function, level: 0, prev_jmp: -1
+''')
+
def test_class(self):
src = '''
#include <stdio.h>
diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js
index ede34103..01f092d5 100644
--- a/tools/eliminator/eliminator-test-output.js
+++ b/tools/eliminator/eliminator-test-output.js
@@ -6132,4 +6132,12 @@ function _mallocNoU($bytes) {
return $mem_0;
return null;
}
+function phi() {
+ if (wat()) {
+ var $10 = 1;
+ } else {
+ var $10 = (_init_mparams() | 0) != 0;
+ }
+ var $10;
+}
diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js
index e44f28ad..8b376305 100644
--- a/tools/eliminator/eliminator-test.js
+++ b/tools/eliminator/eliminator-test.js
@@ -8828,5 +8828,15 @@ function _mallocNoU($bytes) {
return $mem_0;
return null;
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU"]
+function phi() {
+ if (wat()) {
+ var $10 = 1;
+ } else {
+ var $7=_init_mparams();
+ var $8=(($7)|0)!=0;
+ var $10 = $8;
+ }
+ var $10;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU", "asm", "phi"]
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 29aecdcf..62161738 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1464,7 +1464,7 @@ function eliminate(ast, memSafe) {
for (var name in locals) {
if (definitions[name] == 1 && uses[name] == 1) {
potentials[name] = 1;
- } else if (uses[name] == 0) {
+ } else if (uses[name] == 0 && (!definitions[name] || definitions[name] <= 1)) { // no uses, no def or 1 def (cannot operate on phis, and the llvm optimizer will remove unneeded phis anyhow)
var hasSideEffects = false;
if (values[name]) {
traverse(values[name], function(node, type) {
diff --git a/tools/shared.py b/tools/shared.py
index 86a5ba77..fdcdbbda 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -92,7 +92,7 @@ else:
node = 'node'
try:
node = Popen(['which', 'node'], stdout=PIPE).communicate()[0].replace('\n', '') or \
- Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '')
+ Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '') or node
except:
pass
config_file = config_file.replace('{{{ NODE }}}', node)
@@ -749,21 +749,26 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
contents = filter(os.path.exists, map(os.path.abspath, contents))
added_contents = set()
added = True
+ #print >> sys.stderr, ' initial undef are now ', unresolved_symbols, '\n'
while added: # recursively traverse until we have everything we need
+ #print >> sys.stderr, ' running loop of archive including for', f
added = False
for content in contents:
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
+ #print >> sys.stderr, content, 'DEF', new_symbols.defs, '\n'
if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1:
if Building.is_bitcode(content):
- #print >> sys.stderr, ' adding object', content
+ #print >> sys.stderr, ' adding object', content, '\n'
resolved_symbols = resolved_symbols.union(new_symbols.defs)
unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs)
+ #print >> sys.stderr, ' undef are now ', unresolved_symbols, '\n'
actual_files.append(content)
added_contents.add(content)
added = True
+ #print >> sys.stderr, ' done running loop of archive including for', f
finally:
os.chdir(cwd)
try_delete(target)
@@ -785,6 +790,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
seen_symbols = seen_symbols.union(symbols.defs)
# Finish link
+ if DEBUG: print >>sys.stderr, 'emcc: llvm-linking:', actual_files
output = Popen([LLVM_LINK] + actual_files + ['-o', target], stdout=PIPE).communicate()[0]
assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output + '\nemcc: If you get duplicate symbol errors, try --remove-duplicates'
for temp_dir in temp_dirs:
@@ -1053,7 +1059,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
Building._is_ar_cache[filename] = sigcheck
return sigcheck
except Exception, e:
- print 'shared.Building.is_ar failed to test whether file \'%s\' is a llvm archive file! Failed on exception: %s' % (filename, e)
+ print >> sys.stderr, 'shared.Building.is_ar failed to test whether file \'%s\' is a llvm archive file! Failed on exception: %s' % (filename, e)
return False
@staticmethod
@@ -1073,7 +1079,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
assert os.path.exists(test_ll)
try_delete(test_ll)
except Exception, e:
- print 'shared.Building.is_bitcode failed to test whether file \'%s\' is a llvm bitcode file! Failed on exception: %s' % (filename, e)
+ print >> sys.stderr, 'shared.Building.is_bitcode failed to test whether file \'%s\' is a llvm bitcode file! Failed on exception: %s' % (filename, e)
return False
# look for magic signature