summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rwxr-xr-xemcc31
-rwxr-xr-xemscripten.py91
-rw-r--r--src/analyzer.js112
-rw-r--r--src/compiler.js11
-rw-r--r--src/intertyper.js49
-rw-r--r--src/jsifier.js12
-rw-r--r--src/library.js8
-rw-r--r--src/library_browser.js1
-rw-r--r--src/library_fs.js34
-rw-r--r--src/library_gl.js184
-rw-r--r--src/library_glut.js37
-rw-r--r--src/library_memfs.js1
-rw-r--r--src/library_sdl.js2
-rw-r--r--src/modules.js6
-rw-r--r--src/parseTools.js21
-rw-r--r--src/postamble.js3
-rw-r--r--src/preamble.js149
-rw-r--r--tests/cases/phiptrtoint.ll138
-rw-r--r--tests/cases/phiptrtoint.txt0
-rw-r--r--tests/cases/sub_11_0.ll16
-rw-r--r--tests/gles2_uniform_arrays.cpp121
-rw-r--r--tests/glut_wheelevents.c68
-rw-r--r--tests/sdl_rotozoom.c2
-rw-r--r--tests/sdl_rotozoom.pngbin431168 -> 711242 bytes
-rw-r--r--tests/stdio/test_rename.c10
-rw-r--r--tests/test_browser.py9
-rw-r--r--tests/test_core.py50
-rw-r--r--tests/test_other.py70
-rw-r--r--tests/test_sanity.py8
-rw-r--r--tools/find_bigfuncs.py2
-rw-r--r--tools/js-optimizer.js26
-rw-r--r--tools/shared.py9
-rw-r--r--tools/test-js-optimizer-asm-minlast-output.js2
-rw-r--r--tools/test-js-optimizer-asm-minlast.js8
35 files changed, 1078 insertions, 215 deletions
diff --git a/AUTHORS b/AUTHORS
index 751875b1..1233841d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -99,5 +99,7 @@ a license to everyone to use it as detailed in LICENSE.)
* Tobias Vrinssen <tobias@vrinssen.de>
* Patrick R. Martin <patrick.martin.r@gmail.com>
* Richard Quirk <richard.quirk@gmail.com>
+* Marcos Scriven <marcos@scriven.org>
+* Antoine Lambert <antoine.lambert33@gmail.com>
* Daniel Aquino <mr.danielaquino@gmail.com>
diff --git a/emcc b/emcc
index ff81e424..9e5de103 100755
--- a/emcc
+++ b/emcc
@@ -129,18 +129,18 @@ Most normal gcc/g++ options will work, for example:
Options that are modified or new in %s include:
-O0 No optimizations (default)
-O1 Simple optimizations, including asm.js, LLVM -O1
- optimizations, and no runtime assertions
+ optimizations, relooping, and no runtime assertions
or C++ exception catching (to re-enable
C++ exception catching, use
- -s DISABLE_EXCEPTION_CATCHING=0 ).
- (For details on the affects of different
- opt levels, see apply_opt_level() in
- tools/shared.py and also src/settings.js.)
- -O2 As -O1, plus the relooper (loop recreation),
- LLVM -O3 optimizations, and
+ -s DISABLE_EXCEPTION_CATCHING=0 ), and enables
-s ALIASING_FUNCTION_POINTERS=1
+ (For details on the affects of different
+ opt levels, see apply_opt_level() in
+ tools/shared.py and also src/settings.js.)
+ -O2 As -O1, plus various js-level optimizations and
+ LLVM -O3 optimizations
-O3 As -O2, plus dangerous optimizations that may
break the generated code! This adds
@@ -258,6 +258,9 @@ Options that are modified or new in %s include:
try adjusting JAVA_HEAP_SIZE in the environment
(for example, to 4096m for 4GB).
+ Note: Closure is only run if js opts are being
+ done (-O2 or above, or --js-opts 1).
+
--js-transform <cmd> <cmd> will be called on the generated code
before it is optimized. This lets you modify
the JavaScript, for example adding some code
@@ -933,7 +936,7 @@ try:
if default_cxx_std:
newargs = newargs + [default_cxx_std]
- if js_opts is None: js_opts = opt_level >= 1
+ if js_opts is None: js_opts = opt_level >= 2
if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level]
if llvm_lto is None and opt_level >= 3: llvm_lto = 3
if opt_level == 0: debug_level = 4
@@ -1105,6 +1108,16 @@ try:
shared.Settings.CORRECT_OVERFLOWS = 1
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
+ heap = 4096
+ while heap < shared.Settings.TOTAL_MEMORY:
+ if heap <= 16*1024*1024:
+ heap *= 2
+ else:
+ heap += 16*1024*1024
+ if heap != shared.Settings.TOTAL_MEMORY:
+ logging.warning('increasing TOTAL_MEMORY to %d to be more reasonable for asm.js' % heap)
+ shared.Settings.TOTAL_MEMORY = heap
+
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
debug_level = 4 # must keep debug info to do line-by-line operations
@@ -1218,7 +1231,7 @@ try:
file_suffix = filename_type_suffix(input_file)
if file_suffix.endswith(SOURCE_SUFFIXES):
temp_file = temp_files[i]
- logging.debug('optimizing %s with -O%d' % (input_file, llvm_opts))
+ logging.debug('optimizing %s with -O%s' % (input_file, llvm_opts))
shared.Building.llvm_opt(temp_file, llvm_opts)
# If we were just asked to generate bitcode, stop there
diff --git a/emscripten.py b/emscripten.py
index 2e90fa48..b7f85e6f 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -49,15 +49,8 @@ if STDERR_FILE:
logging.info('logging stderr in js compiler phase into %s' % STDERR_FILE)
STDERR_FILE = open(STDERR_FILE, 'w')
-def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)):
+def process_funcs((i, funcs_file, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, DEBUG)):
try:
- funcs_file = temp_files.get('.func_%d.ll' % i).name
- f = open(funcs_file, 'w')
- f.write(funcs)
- funcs = None
- f.write('\n')
- f.write(meta)
- f.close()
#print >> sys.stderr, 'running', str([settings_file, funcs_file, 'funcs', forwarded_file] + libraries).replace("'/", "'") # can use this in src/compiler_funcs.html arguments,
# # just copy temp dir to under this one
out = jsrun.run_js(
@@ -100,42 +93,42 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
ll = open(infile).read()
scan(ll, settings)
total_ll_size = len(ll)
- ll = None # allow collection
if DEBUG: logging.debug(' emscript: scan took %s seconds' % (time.time() - t))
# Split input into the relevant parts for each phase
+
+ if DEBUG: t = time.time()
+
pre = []
funcs = [] # split up functions here, for parallelism later
- meta = [] # needed by each function XXX
- if DEBUG: t = time.time()
- in_func = False
- ll_lines = open(infile).readlines()
- curr_func = None
- for line in ll_lines:
- if in_func:
- curr_func.append(line)
- if line.startswith('}'):
- in_func = False
- funcs.append((curr_func[0], ''.join(curr_func))) # use the entire line as the identifier
- # pre needs to know about all implemented functions, even for non-pre func
- pre.append(curr_func[0])
- pre.append(line)
- curr_func = None
- else:
- if line.startswith(';'): continue
- if line.startswith('define '):
- in_func = True
- curr_func = [line]
- 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
- ll_lines = None
- meta = ''.join(meta)
+ meta_start = ll.find('\n!')
+ if meta_start > 0:
+ meta = ll[meta_start:]
+ else:
+ meta = ''
+ meta_start = -1
+
+ start = ll.find('\n') if ll[0] == ';' else 0 # ignore first line, which contains ; ModuleID = '/dir name'
+
+ func_start = start
+ last = func_start
+ while 1:
+ last = func_start
+ func_start = ll.find('\ndefine ', func_start)
+ if func_start > last:
+ pre.append(ll[last:min(func_start+1, meta_start)] + '\n')
+ if func_start < 0:
+ pre.append(ll[last:meta_start] + '\n')
+ break
+ header = ll[func_start+1:ll.find('\n', func_start+1)+1]
+ end = ll.find('\n}', func_start)
+ last = end+3
+ funcs.append((header, ll[func_start+1:last]))
+ pre.append(header + '}\n')
+ func_start = last
+ ll = None
+
if DEBUG and len(meta) > 1024*1024: logging.debug('emscript warning: large amounts of metadata, will slow things down')
if DEBUG: logging.debug(' emscript: split took %s seconds' % (time.time() - t))
@@ -195,6 +188,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
jcache.set(shortkey, keys, out)
pre, forwarded_data = out.split('//FORWARDED_DATA:')
forwarded_file = temp_files.get('.json').name
+ pre_input = None
open(forwarded_file, 'w').write(forwarded_data)
if DEBUG: logging.debug(' emscript: phase 1 took %s seconds' % (time.time() - t))
@@ -254,11 +248,20 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
if DEBUG: logging.debug(' emscript: phase 2 working on %d chunks %s (intended chunk size: %.2f MB, meta: %.2f MB, forwarded: %.2f MB, total: %.2f MB)' % (len(chunks), ('using %d cores' % cores) if len(chunks) > 1 else '', chunk_size/(1024*1024.), len(meta)/(1024*1024.), len(forwarded_data)/(1024*1024.), total_ll_size/(1024*1024.)))
- commands = [
- (i, chunk, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine,# + ['--prof'],
- temp_files, DEBUG)
- for i, chunk in enumerate(chunks)
- ]
+ commands = []
+ for i in range(len(chunks)):
+ funcs_file = temp_files.get('.func_%d.ll' % i).name
+ f = open(funcs_file, 'w')
+ f.write(chunks[i])
+ if not jcache:
+ chunks[i] = None # leave chunks array alive (need its length later)
+ f.write('\n')
+ f.write(meta)
+ f.close()
+ commands.append(
+ (i, funcs_file, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine,# + ['--prof'],
+ DEBUG)
+ )
if len(chunks) > 1:
pool = multiprocessing.Pool(processes=cores)
@@ -346,7 +349,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
else:
curr = i
i += settings['FUNCTION_POINTER_ALIGNMENT']
- #logging.debug('function indexing', indexed, curr, sig)
+ #logging.debug('function indexing ' + str([indexed, curr, sig]))
forwarded_json['Functions']['indexedFunctions'][indexed] = curr # make sure not to modify this python object later - we use it in indexize
def split_32(x):
diff --git a/src/analyzer.js b/src/analyzer.js
index 17ad26ad..95fbccc7 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -188,7 +188,7 @@ function analyzer(data, sidePass) {
if (USE_TYPED_ARRAYS == 2) {
function getLegalVars(base, bits, allowLegal) {
bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits
- if (allowLegal && bits <= 32) return [{ ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits }];
+ if (allowLegal && bits <= 32) return [{ intertype: 'value', ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits, type: 'i' + bits }];
if (isNumber(base)) return getLegalLiterals(base, bits);
if (base[0] == '{') {
warnOnce('seeing source of illegal data ' + base + ', likely an inline struct - assuming zeroinit');
@@ -198,7 +198,7 @@ function analyzer(data, sidePass) {
var i = 0;
if (base == 'zeroinitializer' || base == 'undef') base = 0;
while (bits > 0) {
- ret[i] = { ident: base ? base + '$' + i : '0', bits: Math.min(32, bits) };
+ ret[i] = { intertype: 'value', ident: base ? base + '$' + i : '0', bits: Math.min(32, bits), type: 'i' + Math.min(32, bits) };
bits -= 32;
i++;
}
@@ -209,7 +209,7 @@ function analyzer(data, sidePass) {
var ret = new Array(Math.ceil(bits/32));
var i = 0;
while (bits > 0) {
- ret[i] = { ident: (parsed[i]|0).toString(), bits: Math.min(32, bits) }; // resign all values
+ ret[i] = { intertype: 'value', ident: (parsed[i]|0).toString(), bits: Math.min(32, bits), type: 'i' + Math.min(32, bits) }; // resign all values
bits -= 32;
i++;
}
@@ -225,7 +225,8 @@ function analyzer(data, sidePass) {
return getLegalLiterals(value.ident, bits);
} else if (value.intertype == 'structvalue') {
return getLegalStructuralParts(value).map(function(part) {
- return { ident: part.ident, bits: part.type.substr(1) };
+ part.bits = part.type.substr(1); // can be some nested IR, like LLVM calls
+ return part;
});
} else {
return getLegalVars(value.ident, bits);
@@ -550,11 +551,7 @@ function analyzer(data, sidePass) {
return {
intertype: 'phiparam',
label: param.label,
- value: {
- intertype: 'value',
- ident: values[k++][j].ident,
- type: 'i' + element.bits,
- }
+ value: values[k++][j]
};
})
});
@@ -802,27 +799,65 @@ function analyzer(data, sidePass) {
var whole = shifts >= 0 ? Math.floor(shifts/32) : Math.ceil(shifts/32);
var fraction = Math.abs(shifts % 32);
if (signed) {
- var signedFill = '(' + makeSignOp(sourceElements[sourceElements.length-1].ident, 'i' + sourceElements[sourceElements.length-1].bits, 're', 1, 1) + ' < 0 ? -1 : 0)';
- var signedKeepAlive = { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i32' };
- }
- for (var j = 0; j < targetElements.length; j++) {
- var result = {
- intertype: 'value',
- ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : (signed ? signedFill : '0'),
- params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null],
+ var signedFill = {
+ intertype: 'mathop',
+ op: 'select',
+ variant: 's',
type: 'i32',
+ params: [{
+ intertype: 'mathop',
+ op: 'icmp',
+ variant: 'slt',
+ type: 'i32',
+ params: [
+ { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i' + Math.min(sourceBits, 32) },
+ { intertype: 'value', ident: '0', type: 'i32' }
+ ]
+ },
+ { intertype: 'value', ident: '-1', type: 'i32' },
+ { intertype: 'value', ident: '0', type: 'i32' },
+ ]
};
- if (j == 0 && sourceBits < 32) {
- // zext sign correction
- result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1);
- }
- if (fraction != 0) {
- var other = {
+ }
+ for (var j = 0; j < targetElements.length; j++) {
+ var inBounds = j + whole >= 0 && j + whole < sourceElements.length;
+ var result;
+ if (inBounds || !signed) {
+ result = {
intertype: 'value',
- ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : (signed ? signedFill : '0'),
- params: [(signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null],
- type: 'i32',
+ ident: inBounds ? sourceElements[j + whole].ident : '0',
+ type: 'i' + Math.min(sourceBits, 32),
};
+ if (j == 0 && sourceBits < 32) {
+ // zext sign correction
+ var result2 = {
+ intertype: 'mathop',
+ op: isUnsignedOp(value.op) ? 'zext' : 'sext',
+ params: [result, {
+ intertype: 'type',
+ ident: 'i32',
+ type: 'i' + sourceBits
+ }],
+ type: 'i32'
+ };
+ result = result2;
+ }
+ } else {
+ // out of bounds and signed
+ result = copy(signedFill);
+ }
+ if (fraction != 0) {
+ var other;
+ var otherInBounds = j + sign + whole >= 0 && j + sign + whole < sourceElements.length;
+ if (otherInBounds || !signed) {
+ other = {
+ intertype: 'value',
+ ident: otherInBounds ? sourceElements[j + sign + whole].ident : '0',
+ type: 'i32',
+ };
+ } else {
+ other = copy(signedFill);
+ }
other = {
intertype: 'mathop',
op: shiftOp,
@@ -872,10 +907,17 @@ function analyzer(data, sidePass) {
}
if (targetBits <= 32) {
// We are generating a normal legal type here
- legalValue = {
- intertype: 'value',
- ident: targetElements[0].ident + (targetBits < 32 ? '&' + (Math.pow(2, targetBits)-1) : ''),
- type: 'rawJS'
+ legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'i32' };
+ if (targetBits < 32) {
+ legalValue = {
+ intertype: 'mathop',
+ op: 'and',
+ type: 'i32',
+ params: [
+ legalValue,
+ { intertype: 'value', ident: (Math.pow(2, targetBits)-1).toString(), type: 'i32' }
+ ]
+ }
};
legalValue.assignTo = item.assignTo;
toAdd.push(legalValue);
@@ -1117,7 +1159,7 @@ function analyzer(data, sidePass) {
rawLinesIndex: i
};
if (variable.origin === 'alloca') {
- variable.allocatedNum = item.allocatedNum;
+ variable.allocatedNum = item.ident;
}
if (variable.origin === 'call') {
variable.type = getReturnType(variable.type);
@@ -1608,9 +1650,9 @@ function analyzer(data, sidePass) {
var lines = func.labels[0].lines;
for (var i = 0; i < lines.length; i++) {
var item = lines[i];
- if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break;
item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ?
- calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0;
+ calcAllocatedSize(item.allocatedType)*item.ident: 0;
if (USE_TYPED_ARRAYS === 2) {
// We need to keep the stack aligned
item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN);
@@ -1619,7 +1661,7 @@ function analyzer(data, sidePass) {
var index = 0;
for (var i = 0; i < lines.length; i++) {
var item = lines[i];
- if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break;
item.allocatedIndex = index;
index += item.allocatedSize;
delete item.allocatedSize;
@@ -1647,7 +1689,7 @@ function analyzer(data, sidePass) {
for (var i = 0; i < lines.length; i++) {
var item = lines[i];
- if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum))) {
+ if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident))) {
finishedInitial = true;
}
if (item.intertype == 'alloca' && finishedInitial) {
diff --git a/src/compiler.js b/src/compiler.js
index 90060837..d490f454 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -185,7 +185,6 @@ if (SAFE_HEAP) USE_BSS = 0; // must initialize heap for safe heap
assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4');
if (ASM_JS) {
assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap');
- assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2');
}
assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals');
@@ -207,7 +206,12 @@ if (phase == 'pre') {
if (VERBOSE) printErr('VERBOSE is on, this generates a lot of output and can slow down compilation');
// Load struct and define information.
-var temp = JSON.parse(read(STRUCT_INFO));
+try {
+ var temp = JSON.parse(read(STRUCT_INFO));
+} catch(e) {
+ printErr('cannot load struct info at ' + STRUCT_INFO + ' : ' + e + ', trying in current dir');
+ temp = JSON.parse(read('struct_info.compiled.json'));
+}
C_STRUCTS = temp.structs;
C_DEFINES = temp.defines;
@@ -312,3 +316,6 @@ if (ll_file) {
}
}
+//var M = keys(tokenCacheMisses).map(function(m) { return [m, misses[m]] }).sort(function(a, b) { return a[1] - b[1] });
+//printErr(dump(M.slice(M.length-10)));
+
diff --git a/src/intertyper.js b/src/intertyper.js
index 09bdaa33..781c8187 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -5,14 +5,16 @@
var fastPaths = 0, slowPaths = 0;
+var tokenCache = {};
+['=', 'i32', 'label', ';', '4', '0', '1', '2', '255', 'align', 'i8*', 'i8', 'i16', 'getelementptr', 'inbounds', 'unnamed_addr', 'x', 'load', 'preds', 'br', 'i32*', 'i1', 'store', '<label>', 'constant', 'c', 'private', 'null', 'internal', 'to', 'bitcast', 'define', 'nounwind', 'nocapture', '%this', 'call', '...'].forEach(function(text) { tokenCache[text] = { text: text } });
+
+//var tokenCacheMisses = {};
+
// Line tokenizer
-function tokenizer(item, inner) {
- //assert(item.lineNum != 40000);
- //if (item.lineNum) print(item.lineNum);
+function tokenize(text, lineNum) {
var tokens = [];
var quotes = 0;
var lastToken = null;
- var CHUNKSIZE = 64; // How much forward to peek forward. Too much means too many string segments copied
// Note: '{' is not an encloser, as its use in functions is split over many lines
var enclosers = {
'[': 0,
@@ -26,22 +28,33 @@ function tokenizer(item, inner) {
function makeToken(text) {
if (text.length == 0) return;
// merge certain tokens
- if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.test(text) ) ) {
+ if (lastToken && /^\**$/.test(text)) {
lastToken.text += text;
return;
}
+ var cached = tokenCache[text];
+ if (cached) {
+ //assert(cached.text === text);
+ tokens.push(cached);
+ lastToken = cached;
+ return;
+ }
+ //tokenCacheMisses[text] = (misses[text] || 0) + 1;
+
var token = {
text: text
};
if (text[0] in enclosers) {
- token.item = tokenizer({
- lineText: text.substr(1, text.length-2)
- }, true);
+ token.item = tokenize(text.substr(1, text.length-2));
token.type = text[0];
}
// merge certain tokens
if (lastToken && isType(lastToken.text) && isFunctionDef(token)) {
+ if (lastToken.text in tokenCache) {
+ // create a copy of the cached value
+ lastToken = tokens[tokens.length-1] = { text: lastToken.text };
+ }
lastToken.text += ' ' + text;
} else if (lastToken && text[0] == '}') { // }, }*, etc.
var openBrace = tokens.length-1;
@@ -63,7 +76,7 @@ function tokenizer(item, inner) {
}
}
// Split using meaningful characters
- var lineText = item.lineText + ' ';
+ var lineText = text + ' ';
var re = /[\[\]\(\)<>, "]/g;
var segments = lineText.split(re);
segments.pop();
@@ -141,15 +154,11 @@ function tokenizer(item, inner) {
var newItem = {
tokens: tokens,
indent: lineText.search(/[^ ]/),
- lineNum: item.lineNum
+ lineNum: lineNum || 0
};
return newItem;
}
-function tokenize(text) {
- return tokenizer({ lineText: text }, true);
-}
-
// Handy sets
var ENCLOSER_STARTERS = set('[', '(', '<');
@@ -251,7 +260,7 @@ function intertyper(lines, sidePass, baseLineNums) {
if (mainPass && /^}.*/.test(line)) {
inFunction = false;
if (mainPass) {
- var func = funcHeaderHandler(tokenizer({ lineText: currFunctionLines[0], lineNum: currFunctionLineNum }, true));
+ var func = funcHeaderHandler(tokenize(currFunctionLines[0], currFunctionLineNum));
if (SKIP_STACK_IN_SMALL && /emscripten_autodebug/.exec(func.ident)) {
warnOnce('Disabling SKIP_STACK_IN_SMALL because we are apparently processing autodebugger data');
@@ -766,15 +775,13 @@ function intertyper(lines, sidePass, baseLineNums) {
return item;
}
// 'alloca'
- var allocaPossibleVars = ['allocatedNum'];
function allocaHandler(item) {
item.intertype = 'alloca';
item.allocatedType = item.tokens[1].text;
if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) {
- item.allocatedNum = toNiceIdent(item.tokens[4].text);
- item.possibleVars = allocaPossibleVars;
+ item.ident = toNiceIdent(item.tokens[4].text);
} else {
- item.allocatedNum = 1;
+ item.ident = 1;
}
item.type = addPointing(item.tokens[1].text); // type of pointer we will get
Types.needAnalysis[item.type] = 0;
@@ -1099,7 +1106,7 @@ function intertyper(lines, sidePass, baseLineNums) {
if (ret) {
if (COMPILER_ASSERTIONS) {
//printErr(['\n', dump(ret), '\n', dump(triager(tokenizer(line)))]);
- var normal = triager(tokenizer(line));
+ var normal = triager(tokenize(line));
delete normal.tokens;
delete normal.indent;
assert(sortedJsonCompare(normal, ret), 'fast path: ' + dump(normal) + '\n vs \n' + dump(ret));
@@ -1124,7 +1131,7 @@ function intertyper(lines, sidePass, baseLineNums) {
//var time = Date.now();
- var t = tokenizer(line);
+ var t = tokenize(line.lineText, line.lineNum);
item = triager(t);
/*
diff --git a/src/jsifier.js b/src/jsifier.js
index 0e5f8ef3..f638ea08 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -234,8 +234,8 @@ function JSify(data, functionsOnly, givenFunctions) {
function globalVariableHandler(item) {
function needsPostSet(value) {
if (typeof value !== 'string') return false;
- return value[0] in UNDERSCORE_OPENPARENS || value.substr(0, 14) === 'CHECK_OVERFLOW'
- || value.substr(0, 6) === 'GLOBAL';
+ // (' is ok, as it is something we can indexize later into a concrete int: ('{{ FI_ ...
+ return /^([(_][^']|CHECK_OVERFLOW|GLOBAL).*/.test(value);
}
item.intertype = 'GlobalVariableStub';
@@ -308,6 +308,8 @@ function JSify(data, functionsOnly, givenFunctions) {
JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors
});
constant[i] = '0';
+ } else {
+ if (typeof value === 'string') constant[i] = deParenCarefully(value);
}
});
@@ -405,6 +407,9 @@ function JSify(data, functionsOnly, givenFunctions) {
var snippet = LibraryManager.library[ident];
var redirectedIdent = null;
var deps = LibraryManager.library[ident + '__deps'] || [];
+ deps.forEach(function(dep) {
+ if (typeof snippet === 'string' && !(dep in LibraryManager.library)) warn('missing library dependency ' + dep + ', make sure you are compiling with the right options (see #ifdefs in src/library*.js)');
+ });
var isFunction = false;
if (typeof snippet === 'string') {
@@ -536,7 +541,6 @@ function JSify(data, functionsOnly, givenFunctions) {
case 'unreachable': line.JS = unreachableHandler(line); break;
default: throw 'what is this line? ' + dump(line);
}
- assert(line.JS);
//if (ASM_JS) assert(line.JS.indexOf('var ') < 0, dump(line));
if (line.assignTo) makeAssign(line);
Framework.currItem = null;
@@ -1361,7 +1365,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (item.allocatedSize === 0) return ''; // This will not actually be shown - it's nativized
return asmCoercion(getFastValue('sp', '+', item.allocatedIndex.toString()), 'i32');
} else {
- return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum));
+ return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.ident));
}
}
function va_argHandler(item) {
diff --git a/src/library.js b/src/library.js
index 5e71b087..875d8bab 100644
--- a/src/library.js
+++ b/src/library.js
@@ -4296,16 +4296,16 @@ LibraryManager.library = {
},
llvm_trap: function() {
- throw 'trap! ' + new Error().stack;
+ abort('trap!');
},
__assert_fail: function(condition, filename, line, func) {
ABORT = true;
- throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + new Error().stack;
+ throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace();
},
__assert_func: function(filename, line, func, condition) {
- throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + new Error().stack;
+ throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace();
},
__cxa_guard_acquire: function(variable) {
@@ -4353,7 +4353,7 @@ LibraryManager.library = {
___cxa_throw.initialized = true;
}
#if EXCEPTION_DEBUG
- Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack);
+ Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + stackTrace());
#endif
{{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}}
{{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}}
diff --git a/src/library_browser.js b/src/library_browser.js
index cba8ecdf..dd60a581 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -749,6 +749,7 @@ mergeInto(LibraryManager.library, {
if (e instanceof ExitStatus) {
return;
} else {
+ if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]);
throw e;
}
}
diff --git a/src/library_fs.js b/src/library_fs.js
index dc5c20f8..da1a4e7f 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -1,5 +1,5 @@
mergeInto(LibraryManager.library, {
- $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'],
+ $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'],
$FS__postset: 'FS.staticInit();' +
'__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' +
'__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' +
@@ -27,24 +27,10 @@ mergeInto(LibraryManager.library, {
// to modify the filesystem freely before run() is called.
ignorePermissions: true,
- ErrnoError: (function() {
- function ErrnoError(errno) {
- this.errno = errno;
- for (var key in ERRNO_CODES) {
- if (ERRNO_CODES[key] === errno) {
- this.code = key;
- break;
- }
- }
- this.message = ERRNO_MESSAGES[errno];
- };
- ErrnoError.prototype = new Error();
- ErrnoError.prototype.constructor = ErrnoError;
- return ErrnoError;
- }()),
+ ErrnoError: null, // set during init
handleFSError: function(e) {
- if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack;
+ if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace();
return ___setErrNo(e.errno);
},
@@ -1091,6 +1077,20 @@ mergeInto(LibraryManager.library, {
assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
FS.init.initialized = true;
+ FS.ErrnoError = function ErrnoError(errno) {
+ this.errno = errno;
+ for (var key in ERRNO_CODES) {
+ if (ERRNO_CODES[key] === errno) {
+ this.code = key;
+ break;
+ }
+ }
+ this.message = ERRNO_MESSAGES[errno];
+ this.stack = stackTrace();
+ };
+ FS.ErrnoError.prototype = new Error();
+ FS.ErrnoError.prototype.constructor = FS.ErrnoError;
+
// Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
Module['stdin'] = input || Module['stdin'];
Module['stdout'] = output || Module['stdout'];
diff --git a/src/library_gl.js b/src/library_gl.js
index 511709ae..1ea8efc2 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -374,6 +374,9 @@ var LibraryGL = {
},
#endif
+ // In WebGL, extensions must be explicitly enabled to be active, see http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14
+ // In GLES2, all extensions are enabled by default without additional operations. Init all extensions we need to give to GLES2 user
+ // code here, so that GLES2 code can operate without changing behavior.
initExtensions: function() {
if (GL.initExtensions.done) return;
GL.initExtensions.done = true;
@@ -394,6 +397,7 @@ var LibraryGL = {
GL.generateTempBuffers();
#endif
+ // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist.
GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') ||
Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') ||
Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
@@ -404,12 +408,80 @@ var LibraryGL = {
GL.floatExt = Module.ctx.getExtension('OES_texture_float');
- GL.elementIndexUintExt = Module.ctx.getExtension('OES_element_index_uint');
- GL.standardDerivativesExt = Module.ctx.getExtension('OES_standard_derivatives');
+ // These are the 'safe' feature-enabling extensions that don't add any performance impact related to e.g. debugging, and
+ // should be enabled by default so that client GLES2/GL code will not need to go through extra hoops to get its stuff working.
+ // As new extensions are ratified at http://www.khronos.org/registry/webgl/extensions/ , feel free to add your new extensions
+ // here, as long as they don't produce a performance impact for users that might not be using those extensions.
+ // E.g. debugging-related extensions should probably be off by default.
+ var automaticallyEnabledExtensions = [ "OES_texture_float", "OES_texture_half_float", "OES_standard_derivatives",
+ "OES_vertex_array_object", "WEBGL_compressed_texture_s3tc", "WEBGL_depth_texture",
+ "OES_element_index_uint", "EXT_texture_filter_anisotropic", "ANGLE_instanced_arrays",
+ "OES_texture_float_linear", "OES_texture_half_float_linear", "WEBGL_compressed_texture_atc",
+ "WEBGL_compressed_texture_pvrtc", "EXT_color_buffer_half_float", "WEBGL_color_buffer_float",
+ "EXT_frag_depth", "EXT_sRGB", "WEBGL_draw_buffers", "WEBGL_shared_resources" ];
+
+ function shouldEnableAutomatically(extension) {
+ for(var i in automaticallyEnabledExtensions) {
+ var include = automaticallyEnabledExtensions[i];
+ if (ext.indexOf(include) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ var extensions = Module.ctx.getSupportedExtensions();
+ for(var e in extensions) {
+ var ext = extensions[e].replace('MOZ_', '').replace('WEBKIT_', '');
+ if (automaticallyEnabledExtensions.indexOf(ext) != -1) {
+ Module.ctx.getExtension(ext); // Calling .getExtension enables that extension permanently, no need to store the return value to be enabled.
+ }
+ }
+ },
- GL.depthTextureExt = Module.ctx.getExtension("WEBGL_depth_texture") ||
- Module.ctx.getExtension("MOZ_WEBGL_depth_texture") ||
- Module.ctx.getExtension("WEBKIT_WEBGL_depth_texture");
+ // In WebGL, uniforms in a shader program are accessed through an opaque object type 'WebGLUniformLocation'.
+ // In GLES2, uniforms are accessed via indices. Therefore we must generate a mapping of indices -> WebGLUniformLocations
+ // to provide the client code the API that uses indices.
+ // This function takes a linked GL program and generates a mapping table for the program.
+ // NOTE: Populating the uniform table is performed eagerly at glLinkProgram time, so glLinkProgram should be considered
+ // to be a slow/costly function call. Calling glGetUniformLocation is relatively fast, since it is always a read-only
+ // lookup to the table populated in this function call.
+ populateUniformTable: function(program) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.programs, program, 'populateUniformTable', 'program');
+#endif
+ var p = GL.programs[program];
+ GL.uniformTable[program] = {};
+ var ptable = GL.uniformTable[program];
+ // A program's uniformTable maps the string name of an uniform to an integer location of that uniform.
+ // The global GL.uniforms map maps integer locations to WebGLUniformLocations.
+ var numUniforms = Module.ctx.getProgramParameter(p, Module.ctx.ACTIVE_UNIFORMS);
+ for (var i = 0; i < numUniforms; ++i) {
+ var u = Module.ctx.getActiveUniform(p, i);
+
+ var name = u.name;
+ // Strip off any trailing array specifier we might have got, e.g. "[0]".
+ if (name.indexOf(']', name.length-1) !== -1) {
+ var ls = name.lastIndexOf('[');
+ name = name.slice(0, ls);
+ }
+
+ // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then
+ // only store the string 'colors' in ptable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i.
+ // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices.
+ var loc = Module.ctx.getUniformLocation(p, name);
+ var id = GL.getNewId(GL.uniforms);
+ ptable[name] = [u.size, id];
+ GL.uniforms[id] = loc;
+
+ for (var j = 1; j < u.size; ++j) {
+ var n = name + '['+j+']';
+ loc = Module.ctx.getUniformLocation(p, n);
+ id = GL.getNewId(GL.uniforms);
+
+ GL.uniforms[id] = loc;
+ }
+ }
}
},
@@ -431,7 +503,13 @@ var LibraryGL = {
case 0x1F02 /* GL_VERSION */:
return allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL);
case 0x1F03 /* GL_EXTENSIONS */:
- return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ')), 'i8', ALLOC_NORMAL);
+ var exts = Module.ctx.getSupportedExtensions();
+ var gl_exts = [];
+ for (i in exts) {
+ gl_exts.push(exts[i]);
+ gl_exts.push("GL_" + exts[i]);
+ }
+ return allocate(intArrayFromString(gl_exts.join(' ')), 'i8', ALLOC_NORMAL); // XXX this leaks! TODO: Cache all results like this in library_gl.js to be clean and nice and avoid leaking.
case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */:
return allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL);
default:
@@ -811,6 +889,7 @@ var LibraryGL = {
glGetUniformfv: function(program, location, params) {
#if GL_ASSERTIONS
GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program');
+ GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformfv', 'location');
#endif
var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]);
if (typeof data == 'number') {
@@ -826,6 +905,7 @@ var LibraryGL = {
glGetUniformiv: function(program, location, params) {
#if GL_ASSERTIONS
GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program');
+ GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformiv', 'location');
#endif
var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]);
if (typeof data == 'number' || typeof data == 'boolean') {
@@ -843,16 +923,31 @@ var LibraryGL = {
GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program');
#endif
name = Pointer_stringify(name);
+
+ var arrayOffset = 0;
+ // If user passed an array accessor "[index]", parse the array index off the accessor.
+ if (name.indexOf(']', name.length-1) !== -1) {
+ var ls = name.lastIndexOf('[');
+ var arrayIndex = name.slice(ls+1, -1);
+ if (arrayIndex.length > 0) {
+ arrayOffset = parseInt(arrayIndex);
+ if (arrayOffset < 0) {
+ return -1;
+ }
+ }
+ name = name.slice(0, ls);
+ }
+
var ptable = GL.uniformTable[program];
- if (!ptable) ptable = GL.uniformTable[program] = {};
- var id = ptable[name];
- if (id) return id;
- var loc = Module.ctx.getUniformLocation(GL.programs[program], name);
- if (!loc) return -1;
- id = GL.getNewId(GL.uniforms);
- GL.uniforms[id] = loc;
- ptable[name] = id;
- return id;
+ if (!ptable) {
+ return -1;
+ }
+ var uniformInfo = ptable[name]; // returns pair [ dimension_of_uniform_array, uniform_location ]
+ if (uniformInfo && arrayOffset < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1.
+ return uniformInfo[1]+arrayOffset;
+ } else {
+ return -1;
+ }
},
glGetVertexAttribfv__sig: 'viii',
@@ -923,54 +1018,81 @@ var LibraryGL = {
glUniform1f__sig: 'vif',
glUniform1f: function(location, v0) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform1f', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform1f(location, v0);
},
glUniform2f__sig: 'viff',
glUniform2f: function(location, v0, v1) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform2f', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform2f(location, v0, v1);
},
glUniform3f__sig: 'vifff',
glUniform3f: function(location, v0, v1, v2) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform3f', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform3f(location, v0, v1, v2);
},
glUniform4f__sig: 'viffff',
glUniform4f: function(location, v0, v1, v2, v3) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform4f', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform4f(location, v0, v1, v2, v3);
},
glUniform1i__sig: 'vii',
glUniform1i: function(location, v0) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform1i', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform1i(location, v0);
},
glUniform2i__sig: 'viii',
glUniform2i: function(location, v0, v1) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform2i', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform2i(location, v0, v1);
},
glUniform3i__sig: 'viiii',
glUniform3i: function(location, v0, v1, v2) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform3i', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform3i(location, v0, v1, v2);
},
glUniform4i__sig: 'viiiii',
glUniform4i: function(location, v0, v1, v2, v3) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform4i', 'location');
+#endif
location = GL.uniforms[location];
Module.ctx.uniform4i(location, v0, v1, v2, v3);
},
glUniform1iv__sig: 'viii',
glUniform1iv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform1iv', 'location');
+#endif
location = GL.uniforms[location];
value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}};
Module.ctx.uniform1iv(location, value);
@@ -978,6 +1100,9 @@ var LibraryGL = {
glUniform2iv__sig: 'viii',
glUniform2iv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform2iv', 'location');
+#endif
location = GL.uniforms[location];
count *= 2;
value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}};
@@ -986,6 +1111,9 @@ var LibraryGL = {
glUniform3iv__sig: 'viii',
glUniform3iv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform3iv', 'location');
+#endif
location = GL.uniforms[location];
count *= 3;
value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}};
@@ -994,6 +1122,9 @@ var LibraryGL = {
glUniform4iv__sig: 'viii',
glUniform4iv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform4iv', 'location');
+#endif
location = GL.uniforms[location];
count *= 4;
value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}};
@@ -1002,6 +1133,9 @@ var LibraryGL = {
glUniform1fv__sig: 'viii',
glUniform1fv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform1fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1016,6 +1150,9 @@ var LibraryGL = {
glUniform2fv__sig: 'viii',
glUniform2fv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform2fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1031,6 +1168,9 @@ var LibraryGL = {
glUniform3fv__sig: 'viii',
glUniform3fv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform3fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1047,6 +1187,9 @@ var LibraryGL = {
glUniform4fv__sig: 'viii',
glUniform4fv: function(location, count, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniform4fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1064,6 +1207,9 @@ var LibraryGL = {
glUniformMatrix2fv__sig: 'viiii',
glUniformMatrix2fv: function(location, count, transpose, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix2fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1080,6 +1226,9 @@ var LibraryGL = {
glUniformMatrix3fv__sig: 'viiii',
glUniformMatrix3fv: function(location, count, transpose, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix3fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1096,6 +1245,9 @@ var LibraryGL = {
glUniformMatrix4fv__sig: 'viiii',
glUniformMatrix4fv: function(location, count, transpose, value) {
+#if GL_ASSERTIONS
+ GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix4fv', 'location');
+#endif
location = GL.uniforms[location];
var view;
if (count == 1) {
@@ -1340,6 +1492,7 @@ var LibraryGL = {
#endif
Module.ctx.linkProgram(GL.programs[program]);
GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking
+ GL.populateUniformTable(program);
},
glGetProgramInfoLog__sig: 'viiii',
@@ -4320,6 +4473,7 @@ var LibraryGL = {
return 1 /* GL_TRUE */;
},
+ gluOrtho2D__deps: ['glOrtho'],
gluOrtho2D: function(left, right, bottom, top) {
_glOrtho(left, right, bottom, top, -1, 1);
},
diff --git a/src/library_glut.js b/src/library_glut.js
index 60dc6540..2321486a 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -189,12 +189,12 @@ var LibraryGLUT = {
}
},
- onMouseButtonDown: function(event){
+ onMouseButtonDown: function(event) {
Browser.calculateMouseEvent(event);
GLUT.buttons |= (1 << event['button']);
- if(event.target == Module["canvas"] && GLUT.mouseFunc){
+ if (event.target == Module["canvas"] && GLUT.mouseFunc) {
try {
event.target.setCapture();
} catch (e) {}
@@ -204,21 +204,40 @@ var LibraryGLUT = {
}
},
- onMouseButtonUp: function(event){
+ onMouseButtonUp: function(event) {
Browser.calculateMouseEvent(event);
GLUT.buttons &= ~(1 << event['button']);
- if(GLUT.mouseFunc) {
+ if (GLUT.mouseFunc) {
event.preventDefault();
GLUT.saveModifiers(event);
Runtime.dynCall('viiii', GLUT.mouseFunc, [event['button'], 1/*GLUT_UP*/, Browser.mouseX, Browser.mouseY]);
}
},
+ onMouseWheel: function(event) {
+ Browser.calculateMouseEvent(event);
+
+ // cross-browser wheel delta
+ var e = window.event || event; // old IE support
+ var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
+
+ var button = 3; // wheel up
+ if (delta < 0) {
+ button = 4; // wheel down
+ }
+
+ if (GLUT.mouseFunc) {
+ event.preventDefault();
+ GLUT.saveModifiers(event);
+ Runtime.dynCall('viiii', GLUT.mouseFunc, [button, 0/*GLUT_DOWN*/, Browser.mouseX, Browser.mouseY]);
+ }
+ },
+
// TODO add fullscreen API ala:
// http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
- onFullScreenEventChange: function(event){
+ onFullScreenEventChange: function(event) {
var width;
var height;
if (document["fullScreen"] || document["mozFullScreen"] || document["webkitIsFullScreen"]) {
@@ -279,6 +298,10 @@ var LibraryGLUT = {
window.addEventListener("mousemove", GLUT.onMousemove, true);
window.addEventListener("mousedown", GLUT.onMouseButtonDown, true);
window.addEventListener("mouseup", GLUT.onMouseButtonUp, true);
+ // IE9, Chrome, Safari, Opera
+ window.addEventListener("mousewheel", GLUT.onMouseWheel, true);
+ // Firefox
+ window.addEventListener("DOMMouseScroll", GLUT.onMouseWheel, true);
}
Browser.resizeListeners.push(function(width, height) {
@@ -298,6 +321,10 @@ var LibraryGLUT = {
window.removeEventListener("mousemove", GLUT.onMousemove, true);
window.removeEventListener("mousedown", GLUT.onMouseButtonDown, true);
window.removeEventListener("mouseup", GLUT.onMouseButtonUp, true);
+ // IE9, Chrome, Safari, Opera
+ window.removeEventListener("mousewheel", GLUT.onMouseWheel, true);
+ // Firefox
+ window.removeEventListener("DOMMouseScroll", GLUT.onMouseWheel, true);
}
Module["canvas"].width = Module["canvas"].height = 1;
} });
diff --git a/src/library_memfs.js b/src/library_memfs.js
index 4e56d996..94fd767e 100644
--- a/src/library_memfs.js
+++ b/src/library_memfs.js
@@ -140,6 +140,7 @@ mergeInto(LibraryManager.library, {
delete old_node.parent.contents[old_node.name];
old_node.name = new_name;
new_dir.contents[new_name] = old_node;
+ old_node.parent = new_dir;
},
unlink: function(parent, name) {
delete parent.contents[name];
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 27f2c0da..e64f117f 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1122,7 +1122,7 @@ var LibrarySDL = {
var ret = SDL.makeSurface(diagonal, diagonal, srcData.flags, false, 'rotozoomSurface');
var dstData = SDL.surfaces[ret];
dstData.ctx.translate(diagonal / 2, diagonal / 2);
- dstData.ctx.rotate(angle * Math.PI / 180);
+ dstData.ctx.rotate(-angle * Math.PI / 180);
dstData.ctx.drawImage(srcData.canvas, -w / 2, -h / 2, w, h);
return ret;
},
diff --git a/src/modules.js b/src/modules.js
index cc9ca549..76e5db11 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -285,11 +285,7 @@ var Functions = {
} else {
if (!singlePhase) return 'NO_INDEX'; // Should not index functions in post
ret = this.indexedFunctions[ident];
- if (!ret) {
- ret = this.nextIndex;
- this.nextIndex += FUNCTION_POINTER_ALIGNMENT;
- this.indexedFunctions[ident] = ret;
- }
+ assert(ret);
ret = ret.toString();
}
if (SIDE_MODULE && sig) { // sig can be undefined for the GL library functions
diff --git a/src/parseTools.js b/src/parseTools.js
index addf0f21..e3b1df6d 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -280,7 +280,7 @@ function isFunctionType(type, out) {
i--;
}
assert(argText);
- return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2), true) }, out);
+ return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2)) }, out);
}
function getReturnType(type) {
@@ -914,13 +914,13 @@ function parseI64Constant(str, legalized) {
}
function parseNumerical(value, type) {
- if ((!type || type == 'double' || type == 'float') && (value.substr && value.substr(0,2) == '0x')) {
+ if ((!type || type === 'double' || type === 'float') && /^0x/.test(value)) {
// Hexadecimal double value, as the llvm docs say,
// "The one non-intuitive notation for constants is the hexadecimal form of floating point constants."
value = IEEEUnHex(value);
} else if (USE_TYPED_ARRAYS == 2 && isIllegalType(type)) {
return value; // do not parseFloat etc., that can lead to loss of precision
- } else if (value == 'null') {
+ } else if (value === 'null') {
// NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.)
value = '0';
} else if (value === 'true') {
@@ -930,7 +930,7 @@ function parseNumerical(value, type) {
}
if (isNumber(value)) {
var ret = parseFloat(value); // will change e.g. 5.000000e+01 to 50
- if (type in Runtime.FLOAT_TYPES) {
+ if (type === 'double' || type === 'float') {
if (value[0] === '-' && ret === 0) return '-.0'; // fix negative 0, toString makes it 0
if (!RUNNING_JS_OPTS) ret = asmEnsureFloat(ret, type);
}
@@ -2474,13 +2474,6 @@ function walkInterdata(item, pre, post, obj) {
if (walkInterdata(item.params[i], pre, post, obj)) return true;
}
}
- if (item.possibleVars) { // other attributes that might contain interesting data; here, variables
- var box = { intertype: 'value', ident: '' };
- for (i = 0; i <= item.possibleVars.length; i++) {
- box.ident = item[item.possibleVars[i]];
- if (walkInterdata(box, pre, post, obj)) return true;
- }
- }
return post && post(item, originalObj, obj);
}
@@ -2500,7 +2493,6 @@ function walkAndModifyInterdata(item, pre) {
if (repl = walkAndModifyInterdata(item.params[i], pre)) item.params[i] = repl;
}
}
- // Ignore possibleVars because we can't replace them anyhow
}
function parseBlockAddress(segment) {
@@ -2582,6 +2574,11 @@ function deParen(text) {
return text;
}
+function deParenCarefully(text) {
+ if (text[0] === '(' && text.indexOf('(', 1) < 0 && text[text.length-1] === ')') return text.substr(1, text.length-2);
+ return text;
+}
+
function addVariable(ident, type, funcData) {
funcData = funcData || Framework.currItem.funcData;
assert(type);
diff --git a/src/postamble.js b/src/postamble.js
index cd892733..d64fb220 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -96,6 +96,7 @@ Module['callMain'] = Module.callMain = function callMain(args) {
Module['noExitRuntime'] = true;
return;
} else {
+ if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]);
throw e;
}
} finally {
@@ -179,7 +180,7 @@ function abort(text) {
ABORT = true;
EXITSTATUS = 1;
- throw 'abort() at ' + (new Error().stack);
+ throw 'abort() at ' + stackTrace();
}
Module['abort'] = Module.abort = abort;
diff --git a/src/preamble.js b/src/preamble.js
index 88aaff77..aedb0e7c 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -36,7 +36,7 @@ var SAFE_HEAP_ERRORS = 0;
var ACCEPTABLE_SAFE_HEAP_ERRORS = 0;
function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) {
- //if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + new Error().stack); // Something like this may be useful, in debugging
+ //if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + stackTrace()); // Something like this may be useful, in debugging
assert(dest > 0, 'segmentation fault');
@@ -63,7 +63,7 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) {
try {
if (HEAP[dest].toString() === 'NaN') error = false; // NaN is acceptable, as a double value
} catch(e){}
- if (error) throw('Warning: Reading an invalid value at ' + dest + ' :: ' + new Error().stack + '\n');
+ if (error) throw('Warning: Reading an invalid value at ' + dest + ' :: ' + stackTrace() + '\n');
}
#endif
if (type === null) return;
@@ -72,14 +72,14 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) {
if (!ignore)
assert(history, 'Must have a history for a safe heap load! ' + dest + ':' + type); // Warning - bit fields in C structs cause loads+stores for each store, so
// they will show up here...
-// assert((history && history[0]) /* || HEAP[dest] === 0 */, "Loading from where there was no store! " + dest + ',' + HEAP[dest] + ',' + type + ', \n\n' + new Error().stack + '\n');
+// assert((history && history[0]) /* || HEAP[dest] === 0 */, "Loading from where there was no store! " + dest + ',' + HEAP[dest] + ',' + type + ', \n\n' + stackTrace() + '\n');
// if (history[0].type !== type) {
if (history !== type && !ignore) {
Module.print('Load-store consistency assumption failure! ' + dest);
Module.print('\n');
Module.print(JSON.stringify(history));
Module.print('\n');
- Module.print('LOAD: ' + type + ', ' + new Error().stack);
+ Module.print('LOAD: ' + type + ', ' + stackTrace());
Module.print('\n');
SAFE_HEAP_ERRORS++;
assert(SAFE_HEAP_ERRORS <= ACCEPTABLE_SAFE_HEAP_ERRORS, 'Load-store consistency assumption failure!');
@@ -93,9 +93,9 @@ function SAFE_HEAP_STORE(dest, value, type, ignore) {
#endif
if (!ignore && !value && (value === null || value === undefined)) {
- throw('Warning: Writing an invalid value of ' + JSON.stringify(value) + ' at ' + dest + ' :: ' + new Error().stack + '\n');
+ throw('Warning: Writing an invalid value of ' + JSON.stringify(value) + ' at ' + dest + ' :: ' + stackTrace() + '\n');
}
- //if (!ignore && (value === Infinity || value === -Infinity || isNaN(value))) throw [value, typeof value, new Error().stack];
+ //if (!ignore && (value === Infinity || value === -Infinity || isNaN(value))) throw [value, typeof value, stackTrace()];
SAFE_HEAP_ACCESS(dest, type, true, ignore, value);
if (dest in HEAP_WATCHED) {
@@ -640,6 +640,143 @@ function stringToUTF32(str, outPtr) {
}
Module['stringToUTF32'] = stringToUTF32;
+function demangle(func) {
+ try {
+ if (typeof func === 'number') func = Pointer_stringify(func);
+ if (func[0] !== '_') return func;
+ if (func[1] !== '_') return func; // C function
+ if (func[2] !== 'Z') return func;
+ var i = 3;
+ // params, etc.
+ var basicTypes = {
+ 'v': 'void',
+ 'b': 'bool',
+ 'c': 'char',
+ 's': 'short',
+ 'i': 'int',
+ 'l': 'long',
+ 'f': 'float',
+ 'd': 'double',
+ 'w': 'wchar_t',
+ 'a': 'signed char',
+ 'h': 'unsigned char',
+ 't': 'unsigned short',
+ 'j': 'unsigned int',
+ 'm': 'unsigned long',
+ 'x': 'long long',
+ 'y': 'unsigned long long',
+ 'z': '...'
+ };
+ function dump(x) {
+ //return;
+ if (x) Module.print(x);
+ Module.print(func);
+ var pre = '';
+ for (var a = 0; a < i; a++) pre += ' ';
+ Module.print (pre + '^');
+ }
+ var subs = [];
+ function parseNested() {
+ i++;
+ if (func[i] === 'K') i++;
+ var parts = [];
+ while (func[i] !== 'E') {
+ if (func[i] === 'S') { // substitution
+ i++;
+ var next = func.indexOf('_', i);
+ var num = func.substring(i, next) || 0;
+ parts.push(subs[num] || '?');
+ i = next+1;
+ continue;
+ }
+ var size = parseInt(func.substr(i));
+ var pre = size.toString().length;
+ if (!size || !pre) { i--; break; } // counter i++ below us
+ var curr = func.substr(i + pre, size);
+ parts.push(curr);
+ subs.push(curr);
+ i += pre + size;
+ }
+ i++; // skip E
+ return parts;
+ }
+ function parse(rawList, limit, allowVoid) { // main parser
+ limit = limit || Infinity;
+ var ret = '', list = [];
+ function flushList() {
+ return '(' + list.join(', ') + ')';
+ }
+ var name;
+ if (func[i] !== 'N') {
+ // not namespaced
+ if (func[i] === 'K') i++;
+ var size = parseInt(func.substr(i));
+ if (size) {
+ var pre = size.toString().length;
+ name = func.substr(i + pre, size);
+ i += pre + size;
+ }
+ } else {
+ // namespaced N-E
+ name = parseNested().join('::');
+ limit--;
+ if (limit === 0) return rawList ? [name] : name;
+ }
+ if (func[i] === 'I') {
+ i++;
+ var iList = parse(true);
+ var iRet = parse(true, 1, true);
+ ret += iRet[0] + ' ' + name + '<' + iList.join(', ') + '>';
+ } else {
+ ret = name;
+ }
+ paramLoop: while (i < func.length && limit-- > 0) {
+ //dump('paramLoop');
+ var c = func[i++];
+ if (c in basicTypes) {
+ list.push(basicTypes[c]);
+ } else {
+ switch (c) {
+ case 'P': list.push(parse(true, 1, true)[0] + '*'); break; // pointer
+ case 'R': list.push(parse(true, 1, true)[0] + '&'); break; // reference
+ case 'L': { // literal
+ i++; // skip basic type
+ var end = func.indexOf('E', i);
+ var size = end - i;
+ list.push(func.substr(i, size));
+ i += size + 2; // size + 'EE'
+ break;
+ }
+ case 'A': { // array
+ var size = parseInt(func.substr(i));
+ i += size.toString().length;
+ if (func[i] !== '_') throw '?';
+ i++; // skip _
+ list.push(parse(true, 1, true)[0] + ' [' + size + ']');
+ break;
+ }
+ case 'E': break paramLoop;
+ default: ret += '?' + c; break paramLoop;
+ }
+ }
+ }
+ if (!allowVoid && list.length === 1 && list[0] === 'void') list = []; // avoid (void)
+ return rawList ? list : ret + flushList();
+ }
+ return parse();
+ } catch(e) {
+ return func;
+ }
+}
+
+function demangleAll(text) {
+ return text.replace(/__Z[\w\d_]+/g, function(x) { var y = demangle(x); return x === y ? x : (x + ' [' + y + ']') });
+}
+
+function stackTrace() {
+ return demangleAll(new Error().stack);
+}
+
// Memory management
var PAGE_SIZE = 4096;
diff --git a/tests/cases/phiptrtoint.ll b/tests/cases/phiptrtoint.ll
new file mode 100644
index 00000000..d682dc06
--- /dev/null
+++ b/tests/cases/phiptrtoint.ll
@@ -0,0 +1,138 @@
+; ModuleID = '/tmp/tmpJctwj0/bug.bc'
+; just an asm validation check, no output
+target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32"
+target triple = "le32-unknown-nacl"
+
+%"class.test::Processor" = type { i32, %"class.test::StateMachine" }
+%"class.test::StateMachine" = type { { i32, i32 } }
+
+@_ZN4test9ProcessorC1Ev = alias internal void (%"class.test::Processor"*)* @_ZN4test9ProcessorC2Ev
+@_ZN4test9ProcessorD1Ev = alias internal void (%"class.test::Processor"*)* @_ZN4test9ProcessorD2Ev
+
+define internal void @_ZN4test9ProcessorC2Ev(%"class.test::Processor"* nocapture %this) unnamed_addr nounwind align 2 {
+ %1 = getelementptr inbounds %"class.test::Processor"* %this, i32 0, i32 0
+ store i32 0, i32* %1, align 4
+ %2 = getelementptr inbounds %"class.test::Processor"* %this, i32 0, i32 1, i32 0
+ store { i32, i32 } zeroinitializer, { i32, i32 }* %2, align 4
+ ret void
+}
+
+define internal void @_ZN4test9ProcessorD2Ev(%"class.test::Processor"* nocapture %this) unnamed_addr nounwind readnone align 2 {
+ ret void
+}
+
+define internal zeroext i1 @_ZN4test9Processor16handleFirstStateEv(%"class.test::Processor"* nocapture %this) align 2 {
+ %1 = tail call i32 @rand()
+ %2 = getelementptr inbounds %"class.test::Processor"* %this, i32 0, i32 0
+ %3 = load i32* %2, align 4
+ %4 = add nsw i32 %3, %1
+ store i32 %4, i32* %2, align 4
+ %5 = and i32 %4, 1
+ %6 = icmp eq i32 %5, 0
+ ret i1 %6
+}
+
+declare i32 @rand()
+
+define internal zeroext i1 @_ZN4test9Processor15handleLastStateEv(%"class.test::Processor"* nocapture %this) align 2 {
+ %1 = tail call i32 @rand()
+ %2 = getelementptr inbounds %"class.test::Processor"* %this, i32 0, i32 0
+ %3 = load i32* %2, align 4
+ %4 = add nsw i32 %3, %1
+ store i32 %4, i32* %2, align 4
+ ret i1 true
+}
+
+define internal zeroext i1 @_ZN4test9Processor3runEv(%"class.test::Processor"* %this) align 2 {
+ %1 = getelementptr inbounds %"class.test::Processor"* %this, i32 0, i32 1, i32 0
+ store { i32, i32 } { i32 ptrtoint (i1 (%"class.test::Processor"*)* @_ZN4test9Processor16handleFirstStateEv to i32), i32 0 }, { i32, i32 }* %1, align 4
+ %2 = bitcast %"class.test::Processor"* %this to i8*
+ br label %.backedge
+
+.backedge: ; preds = %25, %..backedge_crit_edge, %0
+ %3 = phi { i32, i32 } [ { i32 ptrtoint (i1 (%"class.test::Processor"*)* @_ZN4test9Processor16handleFirstStateEv to i32), i32 0 }, %0 ], [ %.pre.pre, %..backedge_crit_edge ], [ { i32 ptrtoint (i1 (%"class.test::Processor"*)* @_ZN4test9Processor15handleLastStateEv to i32), i32 0 }, %25 ]
+ %.fca.0.extract = extractvalue { i32, i32 } %3, 0
+ %.fca.1.extract = extractvalue { i32, i32 } %3, 1
+ %4 = icmp ne i32 %.fca.0.extract, ptrtoint (i1 (%"class.test::Processor"*)* @_ZN4test9Processor15handleLastStateEv to i32)
+ %5 = icmp ne i32 %.fca.0.extract, 0
+ %6 = icmp ne i32 %.fca.1.extract, 0
+ %7 = and i1 %5, %6
+ %8 = or i1 %4, %7
+ %9 = getelementptr inbounds i8* %2, i32 %.fca.1.extract
+ %10 = bitcast i8* %9 to %"class.test::Processor"*
+ %11 = and i32 %.fca.0.extract, 1
+ %12 = icmp eq i32 %11, 0
+ br i1 %12, label %20, label %13
+
+; <label>:13 ; preds = %.backedge
+ %14 = bitcast i8* %9 to i8**
+ %15 = load i8** %14, align 4
+ %16 = add i32 %.fca.0.extract, -1
+ %17 = getelementptr i8* %15, i32 %16
+ %18 = bitcast i8* %17 to i1 (%"class.test::Processor"*)**
+ %19 = load i1 (%"class.test::Processor"*)** %18, align 4
+ br label %_ZN4test12StateMachineINS_9ProcessorEE11handleStateEPS1_.exit
+
+; <label>:20 ; preds = %.backedge
+ %21 = inttoptr i32 %.fca.0.extract to i1 (%"class.test::Processor"*)*
+ br label %_ZN4test12StateMachineINS_9ProcessorEE11handleStateEPS1_.exit
+
+_ZN4test12StateMachineINS_9ProcessorEE11handleStateEPS1_.exit: ; preds = %20, %13
+ %22 = phi i1 (%"class.test::Processor"*)* [ %19, %13 ], [ %21, %20 ]
+ %23 = tail call zeroext i1 %22(%"class.test::Processor"* %10)
+ br i1 %8, label %24, label %26
+
+; <label>:24 ; preds = %_ZN4test12StateMachineINS_9ProcessorEE11handleStateEPS1_.exit
+ br i1 %23, label %25, label %..backedge_crit_edge
+
+..backedge_crit_edge: ; preds = %24
+ %.pre.pre = load { i32, i32 }* %1, align 4
+ br label %.backedge
+
+; <label>:25 ; preds = %24
+ store { i32, i32 } { i32 ptrtoint (i1 (%"class.test::Processor"*)* @_ZN4test9Processor15handleLastStateEv to i32), i32 0 }, { i32, i32 }* %1, align 4
+ br label %.backedge
+
+; <label>:26 ; preds = %_ZN4test12StateMachineINS_9ProcessorEE11handleStateEPS1_.exit
+ ret i1 %23
+}
+
+define internal i32 @_ZNK4test9Processor6resultEv(%"class.test::Processor"* nocapture %this) nounwind readonly align 2 {
+ %1 = getelementptr inbounds %"class.test::Processor"* %this, i32 0, i32 0
+ %2 = load i32* %1, align 4
+ ret i32 %2
+}
+
+define i32 @runProcess() {
+ %processor = alloca %"class.test::Processor", align 4
+ call void @_ZN4test9ProcessorC1Ev(%"class.test::Processor"* %processor)
+ %1 = invoke zeroext i1 @_ZN4test9Processor3runEv(%"class.test::Processor"* %processor)
+ to label %2 unwind label %5
+
+; <label>:2 ; preds = %0
+ %3 = invoke i32 @_ZNK4test9Processor6resultEv(%"class.test::Processor"* %processor)
+ to label %4 unwind label %5
+
+; <label>:4 ; preds = %2
+ call void @_ZN4test9ProcessorD1Ev(%"class.test::Processor"* %processor)
+ ret i32 %3
+
+; <label>:5 ; preds = %2, %0
+ %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
+ cleanup
+ invoke void @_ZN4test9ProcessorD1Ev(%"class.test::Processor"* %processor)
+ to label %7 unwind label %8
+
+; <label>:7 ; preds = %5
+ resume { i8*, i32 } %6
+
+; <label>:8 ; preds = %5
+ %9 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
+ catch i8* null
+ call void @_ZSt9terminatev() noreturn nounwind
+ unreachable
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+declare void @_ZSt9terminatev()
diff --git a/tests/cases/phiptrtoint.txt b/tests/cases/phiptrtoint.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/cases/phiptrtoint.txt
diff --git a/tests/cases/sub_11_0.ll b/tests/cases/sub_11_0.ll
new file mode 100644
index 00000000..7f0bb285
--- /dev/null
+++ b/tests/cases/sub_11_0.ll
@@ -0,0 +1,16 @@
+; ModuleID = 'tests/hello_world.bc'
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+; [#uses=0]
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ %0 = sub nsw i32 1, 1
+ store i32 %0, i32* %retval
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
diff --git a/tests/gles2_uniform_arrays.cpp b/tests/gles2_uniform_arrays.cpp
new file mode 100644
index 00000000..84e394dc
--- /dev/null
+++ b/tests/gles2_uniform_arrays.cpp
@@ -0,0 +1,121 @@
+#include "SDL/SDL_opengl.h"
+#include "SDL/SDL.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+void RunTest(int testVariant)
+{
+ GLuint vs = 0;
+
+ const char *vsCode = "#version 100\n"
+ "attribute vec4 pos; void main() { gl_Position = pos; }";
+
+ vs = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vs, 1, &vsCode, NULL);
+ glCompileShader(vs);
+
+ GLuint ps = 0;
+
+ const char *psCode = "#version 100\n"
+ "precision lowp float;\n"
+ "uniform vec3 color;\n"
+ "uniform vec3 colors[3];\n"
+ "void main() { gl_FragColor = vec4(color,1) + vec4(colors[0].r, colors[1].g, colors[2].b, 1); }";
+
+ ps = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(ps, 1, &psCode, NULL);
+ glCompileShader(ps);
+
+ GLuint program = 0;
+ program = glCreateProgram();
+ glAttachShader(program, vs);
+ glAttachShader(program, ps);
+ glBindAttribLocation(program, 0, "pos");
+ glLinkProgram(program);
+
+ int color_loc = glGetUniformLocation(program, "color");
+ assert(color_loc != -1);
+
+ glUseProgram(program);
+ float col[3] = { 0.2f, 0.2f, 0.2f };
+ glUniform3fv(color_loc, 1, col);
+
+ int loc = glGetUniformLocation(program, "colors");
+ assert(loc != -1);
+ // In previous Emscripten GL layer code, calling glGetUniformLocation would do extra caching operations that interacts how glUniform** after that will work,
+ // so to exhibit extra issues in old code (and to keep new code from regressing), must test both with and without excess glGetUniformLocation calls.
+ if ((testVariant&1) != 0)
+ {
+ // Deliberately check in odd order to make sure any kind of lazy operations won't affect the indices we get.
+ assert(glGetUniformLocation(program, "colors[2]") == loc+2);
+ assert(glGetUniformLocation(program, "colors[0]") == loc);
+ assert(glGetUniformLocation(program, "colors[3]") == -1);
+ assert(glGetUniformLocation(program, "colors[1]") == loc+1);
+ assert(glGetUniformLocation(program, "colors[]") == loc);
+ assert(glGetUniformLocation(program, "colors[-100]") == -1);
+ assert(glGetUniformLocation(program, "colors[bleh]") == -1);
+ }
+
+ float colors[4*3] = { 1,0,0, 0,0.5,0, 0,0,0.2, 1,1,1 };
+
+ if ((testVariant&2)!=0)
+ {
+ glUniform3fv(loc+1, 3, colors+3); // Pass the actual colors (testing a nonzero location offset), but do a mistake by setting one index too many. Spec says this should be gracefully handled, and that excess elements are ignored.
+ assert(glGetError() == GL_NO_ERROR);
+ glUniform3fv(loc, 1, colors); // Set the first index as well.
+ assert(glGetError() == GL_NO_ERROR);
+ }
+ else
+ {
+ glUniform3fv(loc, 4, colors); // Just directly set the full array.
+ assert(glGetError() == GL_NO_ERROR);
+ }
+
+ assert(glGetError() == GL_NO_ERROR);
+
+ GLuint vbo = 0;
+ const GLfloat v[] = { -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1 };
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), 0);
+ glEnableVertexAttribArray(0);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ unsigned char pixel[4];
+ glReadPixels(1,1,1,1,GL_RGBA,GL_UNSIGNED_BYTE, pixel);
+ //printf("%d,%d,%d,%d\n", pixel[0], pixel[1], pixel[2], pixel[3]);
+ assert(pixel[0] == 255);
+ assert(pixel[1] == 178);
+ assert(pixel[2] == 102);
+ assert(pixel[3] == 255);
+
+ printf("OK: Case %d passed.\n", testVariant);
+ // Lazy, don't clean up afterwards.
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ for(int i = 0; i < 4; ++i)
+ RunTest(i);
+
+ return 0;
+}
diff --git a/tests/glut_wheelevents.c b/tests/glut_wheelevents.c
new file mode 100644
index 00000000..7be01636
--- /dev/null
+++ b/tests/glut_wheelevents.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glut.h>
+#include <EGL/egl.h>
+#include <emscripten.h>
+
+#define MULTILINE(...) #__VA_ARGS__
+
+int wheel_up = 0;
+int wheel_down = 0;
+
+int result = 0;
+
+void mouseCB(int button, int state, int x, int y)
+{
+ if(button == 3)
+ {
+ wheel_up = 1;
+ }
+ else if (button == 4)
+ {
+ wheel_down = 1;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ emscripten_run_script(MULTILINE(
+ Module.injectWheelEvent = function(x, y, delta) {
+ var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
+ var event = document.createEvent("MouseEvents");
+ if (!isFirefox) {
+ // mouse wheel event for IE9, Chrome, Safari, Opera
+ event.initMouseEvent('mousewheel', true, true, window,
+ 0, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y,
+ 0, 0, 0, 0, 0, null);
+ event.wheelDelta = delta;
+ } else {
+ // mouse wheel event for Firefox, the delta sign is inversed for that browser and is stored in the detail property of the mouse event
+ event.initMouseEvent('DOMMouseScroll', true, true, window,
+ -delta, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y,
+ 0, 0, 0, 0, 0, null);
+ }
+ Module['canvas'].dispatchEvent(event);
+ }
+ ));
+
+
+ glutInit(&argc, argv);
+
+ glutMouseFunc(&mouseCB);
+
+ // inject wheel up event (delta > 0)
+ emscripten_run_script("Module.injectWheelEvent(100, 100, 1)");
+ if (wheel_up) {
+ printf("%s\n", "mouse wheel up event received");
+ }
+ // inject wheel down event (delta < 0)
+ emscripten_run_script("Module.injectWheelEvent(100, 100, -1)");
+ if (wheel_down) {
+ printf("%s\n", "mouse wheel down event received");
+ }
+
+ result = wheel_up && wheel_down;
+
+ REPORT_RESULT();
+ return 0;
+}
diff --git a/tests/sdl_rotozoom.c b/tests/sdl_rotozoom.c
index cdbdcc6f..2c0d35df 100644
--- a/tests/sdl_rotozoom.c
+++ b/tests/sdl_rotozoom.c
@@ -38,7 +38,7 @@ int main(int argc, char **argv) {
sprite[2] = zoomSurface(sprite[0], 0.5, 0.5, SMOOTHING_ON);
sprite[3] = zoomSurface(sprite[1], 0.5, 0.5, SMOOTHING_ON);
sprite[4] = rotozoomSurface(sprite[0], -20, 0.3, SMOOTHING_ON);
- sprite[5] = rotozoomSurface(sprite[1], 45, 0.5, SMOOTHING_ON);
+ sprite[5] = rotozoomSurface(sprite[1], 20, 1, SMOOTHING_ON);
sprite[6] = zoomSurface(sprite[0], -0.5, 0.5, SMOOTHING_ON);
sprite[7] = zoomSurface(sprite[0], -0.5, -0.5, SMOOTHING_ON);
sprite[8] = rotozoomSurface(sprite[1], 0, 0.5, SMOOTHING_ON);
diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png
index 5933754f..ebde79f2 100644
--- a/tests/sdl_rotozoom.png
+++ b/tests/sdl_rotozoom.png
Binary files differ
diff --git a/tests/stdio/test_rename.c b/tests/stdio/test_rename.c
index f15c8140..1a5017c1 100644
--- a/tests/stdio/test_rename.c
+++ b/tests/stdio/test_rename.c
@@ -25,6 +25,8 @@ void setup() {
mkdir("dir/subdir", 0777);
mkdir("dir-readonly", 0555);
mkdir("dir-nonempty", 0777);
+ mkdir("dir/subdir3", 0777);
+ mkdir("dir/subdir3/subdir3_1", 0777);
create_file("dir-nonempty/file", "abcdef", 0777);
}
@@ -38,6 +40,9 @@ void cleanup() {
rmdir("dir/subdir");
rmdir("dir/subdir1");
rmdir("dir/subdir2");
+ rmdir("dir/subdir3/subdir3_1/subdir1 renamed");
+ rmdir("dir/subdir3/subdir3_1");
+ rmdir("dir/subdir3");
rmdir("dir");
rmdir("dir-readonly");
unlink("dir-nonempty/file");
@@ -96,6 +101,11 @@ void test() {
err = access("dir/subdir2", F_OK);
assert(!err);
+ err = rename("dir/subdir2", "dir/subdir3/subdir3_1/subdir1 renamed");
+ assert(!err);
+ err = access("dir/subdir3/subdir3_1/subdir1 renamed", F_OK);
+ assert(!err);
+
puts("success");
}
diff --git a/tests/test_browser.py b/tests/test_browser.py
index 799759a1..ecd331fd 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -866,6 +866,9 @@ keydown(100);keyup(100); // trigger the end
def test_glut_touchevents(self):
self.btest('glut_touchevents.c', '1')
+ def test_glut_wheelevents(self):
+ self.btest('glut_wheelevents.c', '1')
+
def test_emscripten_get_now(self):
self.btest('emscripten_get_now.cpp', '1')
@@ -1308,6 +1311,10 @@ keydown(100);keyup(100); // trigger the end
def test_gl_vertex_buffer(self):
self.btest('gl_vertex_buffer.c', reference='gl_vertex_buffer.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1)
+ # Does not pass due to https://bugzilla.mozilla.org/show_bug.cgi?id=924264 so disabled for now.
+ # def test_gles2_uniform_arrays(self):
+ # self.btest('gles2_uniform_arrays.cpp', args=['-s', 'GL_ASSERTIONS=1'], expected=['1'])
+
def test_matrix_identity(self):
self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'], args=['-s', 'LEGACY_GL_EMULATION=1'])
@@ -1385,7 +1392,7 @@ keydown(100);keyup(100); // trigger the end
def test_sdl_rotozoom(self):
shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png'))
- self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'], reference_slack=5)
+ self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'])
def test_sdl_gfx_primitives(self):
self.btest('sdl_gfx_primitives.c', reference='sdl_gfx_primitives.png', reference_slack=1)
diff --git a/tests/test_core.py b/tests/test_core.py
index c1bfce6f..87925082 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1073,7 +1073,6 @@ Succeeded!
self.do_run(open(path_from_root('tests', 'cube2md5.cpp')).read(), open(path_from_root('tests', 'cube2md5.ok')).read())
def test_cube2hash(self):
-
try:
old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or ''
os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = '1' # test splitting out each function to a chunk in emscripten.py (21 functions here)
@@ -1091,6 +1090,17 @@ Succeeded!
finally:
os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = old_chunk_size
+ assert 'asm1' in test_modes
+ if self.run_name == 'asm1':
+ assert Settings.RELOOP
+ generated = open('src.cpp.o.js').read()
+ main = generated[generated.find('function _main'):]
+ main = main[:main.find('\n}')]
+ num_vars = 0
+ for v in re.findall('var [^;]+;', main):
+ num_vars += v.count(',') + 1
+ assert num_vars == 10, 'no variable elimination should have been run, but seeing %d' % num_vars
+
def test_unaligned(self):
if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1')
@@ -8608,9 +8618,10 @@ void*:16
assert 'asm1' in test_modes
if self.run_name == 'asm1':
- assert not Settings.RELOOP
- Settings.RELOOP = 1 # check for mixing of relooping with asm1
- self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
+ generated = open('src.cpp.o.js').read()
+ main = generated[generated.find('function runPostSets'):]
+ main = main[:main.find('\n}')]
+ assert main.count('\n') == 7, 'must not emit too many postSets: %d' % main.count('\n')
def test_gcc_unmangler(self):
Settings.NAMED_GLOBALS = 1 # test coverage for this
@@ -8649,7 +8660,10 @@ void*:16
def test_freetype(self):
if self.emcc_args is None: return self.skip('requires emcc')
if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix')
- if Settings.ASM_JS and '-O2' not in self.emcc_args: return self.skip('mozilla bug 863867')
+
+ assert 'asm2g' in test_modes
+ if self.run_name == 'asm2g':
+ Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip for some more coverage here
if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
@@ -9462,6 +9476,32 @@ def process(filename):
Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip the test
self.do_run(src, '''Hello 7 from JS!''')
+ def test_demangle_stacks(self):
+ if Settings.ASM_JS: return self.skip('spidermonkey has stack trace issues')
+
+ src = r'''
+ #include<stdio.h>
+ #include<stdlib.h>
+
+ namespace NameSpace {
+ class Class {
+ public:
+ int Aborter(double x, char y, int *z) {
+ int addr = x+y+(int)z;
+ void *p = (void*)addr;
+ for (int i = 0; i < 100; i++) free(p); // will abort, should show proper stack trace
+ }
+ };
+ }
+
+ int main(int argc, char **argv) {
+ NameSpace::Class c;
+ c.Aborter(1.234, 'a', NULL);
+ return 0;
+ }
+ '''
+ self.do_run(src, 'NameSpace::Class::Aborter(double, char, int*)');
+
def test_embind(self):
if self.emcc_args is None: return self.skip('requires emcc')
Building.COMPILER_TEST_OPTS += ['--bind']
diff --git a/tests/test_other.py b/tests/test_other.py
index afad1927..185b4853 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -125,8 +125,6 @@ Options that are modified or new in %s include:
(['-o', 'something.js', '-O0'], 0, None, 0, 0),
(['-o', 'something.js', '-O1'], 1, None, 0, 0),
(['-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), # no closure since debug
- (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0),
- (['-o', 'something.js', '-O1', '--closure', '1', '-s', 'ASM_JS=0'], 1, None, 1, 0),
(['-o', 'something.js', '-O2'], 2, None, 0, 1),
(['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0),
(['-o', 'something.js', '-Os'], 2, None, 0, 1),
@@ -169,9 +167,9 @@ Options that are modified or new in %s include:
# closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
assert '._main = ' not in generated, 'closure compiler should not have been run'
if keep_debug:
- assert ('(label)' in generated or '(label | 0)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2'
+ assert ('(label)' in generated or '(label | 0)' in generated) == (opt_level <= 0), 'relooping should be in opt >= 1'
assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
- assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated or 'var $original = 0' in generated, 'micro opts should always be on'
+ assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or '$i_04' in generated or '$i_05' in generated or 'var $original = 0' in generated, 'micro opts should always be on'
if opt_level >= 2 and '-g' in params:
assert re.search('HEAP8\[\$?\w+ ?\+ ?\(+\$?\w+ ?', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2
assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts'
@@ -1647,6 +1645,8 @@ f.close()
['asm', 'outline']),
(path_from_root('tools', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline3-output.js')).read(),
['asm', 'outline']),
+ (path_from_root('tools', 'test-js-optimizer-asm-minlast.js'), open(path_from_root('tools', 'test-js-optimizer-asm-minlast-output.js')).read(),
+ ['asm', 'minifyWhitespace', 'last']),
]:
print input
output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
@@ -1666,20 +1666,16 @@ f.close()
try:
os.environ['EMCC_DEBUG'] = '1'
os.environ['EMCC_CORES'] = '2' # standardize over machines
- for asm, linkable, chunks, js_chunks in [
- (0, 0, 2, 2), (0, 1, 2, 4),
- (1, 0, 2, 2), (1, 1, 2, 4)
+ for asm, linkable, chunks in [
+ (0, 0, 2), (0, 1, 2),
+ (1, 0, 2), (1, 1, 2)
]:
- print asm, linkable, chunks, js_chunks
+ print asm, linkable, chunks
output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate()
ok = False
for c in range(chunks, chunks+2):
ok = ok or ('phase 2 working on %d chunks' % c in err)
assert ok, err
- ok = False
- for c in range(js_chunks, js_chunks+2):
- ok = ok or ('splitting up js optimization into %d chunks' % c in err)
- assert ok, err
finally:
del os.environ['EMCC_DEBUG']
del os.environ['EMCC_CORES']
@@ -1910,3 +1906,53 @@ done.
assert '''tests/hello_world.c"''' in out
assert '''printf("hello, world!''' in out
+ def test_demangle(self):
+ open('src.cpp', 'w').write('''
+ #include <stdio.h>
+ #include <emscripten.h>
+ void two(char c) {
+ EM_ASM(Module.print(stackTrace()));
+ }
+ void one(int x) {
+ two(x % 17);
+ }
+ int main() {
+ EM_ASM(Module.print(demangle('__Znwj'))); // check for no aborts
+ EM_ASM(Module.print(demangle('_main')));
+ EM_ASM(Module.print(demangle('__Z2f2v')));
+ EM_ASM(Module.print(demangle('__Z12abcdabcdabcdi')));
+ EM_ASM(Module.print(demangle('__Z4testcsifdPvPiPc')));
+ EM_ASM(Module.print(demangle('__ZN4test5moarrEcslfdPvPiPc')));
+ EM_ASM(Module.print(demangle('__ZN4Waka1f12a234123412345pointEv')));
+ EM_ASM(Module.print(demangle('__Z3FooIiEvv')));
+ EM_ASM(Module.print(demangle('__Z3FooIidEvi')));
+ EM_ASM(Module.print(demangle('__ZN3Foo3BarILi5EEEvv')));
+ EM_ASM(Module.print(demangle('__ZNK10__cxxabiv120__si_class_type_info16search_below_dstEPNS_19__dynamic_cast_infoEPKvib')));
+ EM_ASM(Module.print(demangle('__Z9parsewordRPKciRi')));
+ EM_ASM(Module.print(demangle('__Z5multiwahtjmxyz')));
+ EM_ASM(Module.print(demangle('__Z1aA32_iPA5_c')));
+ one(17);
+ return 0;
+ }
+ ''')
+
+ Popen([PYTHON, EMCC, 'src.cpp', '-s', 'LINKABLE=1']).communicate()
+ output = run_js('a.out.js')
+ self.assertContained('''main
+f2()
+abcdabcdabcd(int)
+test(char, short, int, float, double, void*, int*, char*)
+test::moarr(char, short, long, float, double, void*, int*, char*)
+Waka::f::a23412341234::point()
+void Foo<int>()
+void Foo<int, double>(int)
+void Foo::Bar<5>()
+__cxxabiv1::__si_class_type_info::search_below_dst(__cxxabiv1::__dynamic_cast_info*, void*, int, bool)
+parseword(char*&, int, int&)
+multi(wchar_t, signed char, unsigned char, unsigned short, unsigned int, unsigned long, long long, unsigned long long, ...)
+a(int [32], char [5]*)
+''', output)
+ # test for multiple functions in one stack trace
+ assert 'one(int)' in output
+ assert 'two(char)' in output
+
diff --git a/tests/test_sanity.py b/tests/test_sanity.py
index aa3f1242..a0fff252 100644
--- a/tests/test_sanity.py
+++ b/tests/test_sanity.py
@@ -429,12 +429,12 @@ fi
stdout=PIPE, stderr=PIPE).communicate()
self.assertContained('hello, world!', run_js('a.out.js'))
output = '\n'.join(output)
- assert ('bootstrapping relooper succeeded' in output) == (i == 2), 'only bootstrap on first O2: ' + output
- assert os.path.exists(RELOOPER) == (i >= 2), 'have relooper on O2: ' + output
+ assert ('bootstrapping relooper succeeded' in output) == (i == 1), 'only bootstrap on first O2: ' + output
+ assert os.path.exists(RELOOPER) == (i >= 1), 'have relooper on O2: ' + output
src = open('a.out.js').read()
main = src.split('function _main()')[1].split('\n}\n')[0]
- assert ('while (1) {' in main or 'while(1){' in main or '} while ($' in main or '}while($' in main) == (i >= 2), 'reloop code on O2: ' + main
- assert ('switch' not in main) == (i >= 2), 'reloop code on O2: ' + main
+ assert ('while (1) {' in main or 'while(1){' in main or 'while(1) {' in main or '} while ($' in main or '}while($' in main) == (i >= 1), 'reloop code on O2: ' + main
+ assert ('switch' not in main) == (i >= 1), 'reloop code on O2: ' + main
def test_jcache(self):
PRE_LOAD_MSG = 'loading pre from jcache'
diff --git a/tools/find_bigfuncs.py b/tools/find_bigfuncs.py
index c8e29833..79136343 100644
--- a/tools/find_bigfuncs.py
+++ b/tools/find_bigfuncs.py
@@ -18,6 +18,6 @@ for line in open(filename):
size = i - start
data.append([curr, size])
curr = None
-#data.sort(lambda x, y: x[1] - y[1])
+data.sort(lambda x, y: x[1] - y[1])
print ''.join(['%6d : %s' % (x[1], x[0]) for x in data])
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index e5e0d287..022bdf47 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -3788,11 +3788,27 @@ function asmLastOpts(ast) {
node[1] = simplifyNotCompsDirect(['unary-prefix', '!', conditionToBreak]);
return node;
}
- } else if (type == 'binary' && node[1] == '&' && node[3][0] == 'unary-prefix' && node[3][1] == '-' && node[3][2][0] == 'num' && node[3][2][1] == 1) {
- // Change &-1 into |0, at this point the hint is no longer needed
- node[1] = '|';
- node[3] = node[3][2];
- node[3][1] = 0;
+ } else if (type == 'binary') {
+ if (node[1] === '&') {
+ if (node[3][0] === 'unary-prefix' && node[3][1] === '-' && node[3][2][0] === 'num' && node[3][2][1] === 1) {
+ // Change &-1 into |0, at this point the hint is no longer needed
+ node[1] = '|';
+ node[3] = node[3][2];
+ node[3][1] = 0;
+ }
+ } else if (node[1] === '-' && node[3][0] === 'unary-prefix') {
+ // avoid X - (-Y) because some minifiers buggily emit X--Y which is invalid as -- can be a unary. Transform to
+ // X + Y
+ if (node[3][1] === '-') { // integer
+ node[1] = '+';
+ node[3] = node[3][2];
+ } else if (node[3][1] === '+') { // float
+ if (node[3][2][0] === 'unary-prefix' && node[3][2][1] === '-') {
+ node[1] = '+';
+ node[3][2] = node[3][2][2];
+ }
+ }
+ }
}
});
});
diff --git a/tools/shared.py b/tools/shared.py
index b3c3257b..c816f091 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -758,7 +758,6 @@ class Settings2(type):
self.attrs['ASM_JS'] = 1
self.attrs['ASSERTIONS'] = 0
self.attrs['DISABLE_EXCEPTION_CATCHING'] = 1
- if opt_level >= 2:
self.attrs['RELOOP'] = 1
self.attrs['ALIASING_FUNCTION_POINTERS'] = 1
if opt_level >= 3:
@@ -1425,11 +1424,11 @@ class Building:
emcc_leave_inputs_raw = os.environ.get('EMCC_LEAVE_INPUTS_RAW')
if emcc_leave_inputs_raw: del os.environ['EMCC_LEAVE_INPUTS_RAW']
- def make(opt_level):
+ def make(opt_level, reloop):
raw = relooper + '.raw.js'
Building.emcc(os.path.join('relooper', 'Relooper.cpp'), ['-I' + os.path.join('relooper'), '--post-js',
os.path.join('relooper', 'emscripten', 'glue.js'),
- '--memory-init-file', '0',
+ '--memory-init-file', '0', '-s', 'RELOOP=%d' % reloop,
'-s', 'EXPORTED_FUNCTIONS=["_rl_set_output_buffer","_rl_make_output_buffer","_rl_new_block","_rl_delete_block","_rl_block_add_branch_to","_rl_new_relooper","_rl_delete_relooper","_rl_relooper_add_block","_rl_relooper_calculate","_rl_relooper_render", "_rl_set_asm_js_mode"]',
'-s', 'DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=["memcpy", "memset", "malloc", "free", "puts"]',
'-s', 'RELOOPER="' + relooper + '"',
@@ -1444,10 +1443,10 @@ class Building:
# bootstrap phase 1: generate unrelooped relooper, for which we do not need a relooper (so we cannot recurse infinitely in this function)
logging.info(' bootstrap phase 1')
- make(1)
+ make(2, 0)
# bootstrap phase 2: generate relooped relooper, using the unrelooped relooper (we see relooper.js exists so we cannot recurse infinitely in this function)
logging.info(' bootstrap phase 2')
- make(2)
+ make(2, 1)
logging.info('bootstrapping relooper succeeded')
logging.info('=======================================')
ok = True
diff --git a/tools/test-js-optimizer-asm-minlast-output.js b/tools/test-js-optimizer-asm-minlast-output.js
new file mode 100644
index 00000000..d25c6d9b
--- /dev/null
+++ b/tools/test-js-optimizer-asm-minlast-output.js
@@ -0,0 +1,2 @@
+function test($34){var $35=0;$35=$34+130.0;$35=$34+130;return $35|0}
+
diff --git a/tools/test-js-optimizer-asm-minlast.js b/tools/test-js-optimizer-asm-minlast.js
new file mode 100644
index 00000000..6d172899
--- /dev/null
+++ b/tools/test-js-optimizer-asm-minlast.js
@@ -0,0 +1,8 @@
+function test($34) {
+ var $35 = 0;
+ $35=($34)-((+-130));
+ $35=($34)-(-130);
+ return $35 | 0;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test"]
+