summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rwxr-xr-xemcc26
-rw-r--r--src/analyzer.js68
-rw-r--r--src/jsifier.js28
-rw-r--r--src/library.js27
-rw-r--r--src/parseTools.js38
-rw-r--r--src/preamble.js62
-rw-r--r--tests/cases/allocavartop.ll17
-rw-r--r--tests/cases/legalizer_ta2.ll2
-rw-r--r--tests/cases/longjmp_tiny.ll65
-rw-r--r--tests/cases/longjmp_tiny.txt2
-rwxr-xr-xtests/runner.py255
-rwxr-xr-xtools/bindings_generator.py113
-rw-r--r--tools/eliminator/eliminator-test-output.js11
-rw-r--r--tools/eliminator/eliminator-test.js13
-rw-r--r--tools/eliminator/eliminator.coffee3
-rw-r--r--tools/js-optimizer.js57
-rw-r--r--tools/test-js-optimizer-output.js73
-rw-r--r--tools/test-js-optimizer.js66
19 files changed, 793 insertions, 134 deletions
diff --git a/AUTHORS b/AUTHORS
index 8a46cef7..c9b0d861 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,5 +13,6 @@ under the licensing terms detailed in LICENSE.
* David Yip <yipdw@member.fsf.org>
* Julien Hamaide <julien.hamaide@gmail.com>
* Ehsan Akhgari <ehsan.akhgari@gmail.com> (copyright owned by Mozilla Foundation)
+* Adrian Taylor <adrian@macrobug.com>
diff --git a/emcc b/emcc
index c57bec4f..872c225b 100755
--- a/emcc
+++ b/emcc
@@ -243,6 +243,9 @@ BITCODE_SUFFIXES = ('.bc', '.o')
SHAREDLIB_SUFFIXES = ('.dylib', '.so', '.dll')
ASSEMBLY_SUFFIXES = ('.ll',)
+def suffix(name):
+ return name.split('.')[:-1]
+
def unsuffixed(name):
return '.'.join(name.split('.')[:-1])
@@ -460,6 +463,8 @@ try:
shared.Building.llvm_as(input_file, temp_file)
temp_files.append(temp_file)
+ if not LEAVE_INPUTS_RAW: assert len(temp_files) == len(input_files)
+
# If we were just asked to generate bitcode, stop there
if final_suffix not in ['js', 'html']:
if llvm_opts > 0:
@@ -534,8 +539,8 @@ try:
('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]:
need = []
has = []
- for input_file in input_files:
- symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o'))
+ for temp_file in temp_files:
+ symbols = shared.Building.llvm_nm(temp_file)
for library_symbol in library_symbols:
if library_symbol in symbols.undefs:
need.append(library_symbol)
@@ -550,16 +555,17 @@ try:
if fix:
fix()
- # First, combine the bitcode files if there are several
- if len(input_files) + len(extra_files_to_link) > 1:
- linker_inputs = map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link
+ # First, combine the bitcode files if there are several. We must also link if we have a singleton .a
+ if len(input_files) + len(extra_files_to_link) > 1 or \
+ (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in SHAREDLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])):
+ linker_inputs = temp_files + extra_files_to_link
if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs
shared.Building.link(linker_inputs,
in_temp(target_basename + '.bc'))
final = in_temp(target_basename + '.bc')
else:
if not LEAVE_INPUTS_RAW:
- shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc'))
+ shutil.move(temp_files[0], in_temp(target_basename + '.bc'))
final = in_temp(target_basename + '.bc')
else:
final = input_files[0]
@@ -654,10 +660,10 @@ try:
if DEBUG: save_intermediate('eliminator')
# js optimizer pre-pass
- js_optimizer_queue += ['simplifyExpressionsPre', 'optimizeShiftsConservative']
- ###js_optimizer_queue += ['optimizeShiftsAggressive']
- ###final = shared.Building.eliminator(final) # aggressive shifts optimization introduces some new variables, remove ones that we can
- ###if DEBUG: save_intermediate('eliminator')
+ js_optimizer_queue += ['simplifyExpressionsPre', 'optimizeShiftsAggressive']
+ flush_js_optimizer_queue()
+ final = shared.Building.eliminator(final) # aggressive shifts optimization introduces some new variables, remove ones that we can
+ if DEBUG: save_intermediate('eliminator')
if closure:
flush_js_optimizer_queue()
diff --git a/src/analyzer.js b/src/analyzer.js
index 1c643303..593b4650 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -11,11 +11,8 @@ var VAR_EMULATED = 'emulated';
var ENTRY_IDENT = toNiceIdent('%0');
var ENTRY_IDENTS = set(toNiceIdent('%0'), toNiceIdent('%1'));
-function cleanFunc(func) {
- func.lines = func.lines.filter(function(line) { return line.intertype !== null });
- func.labels.forEach(function(label) {
- label.lines = label.lines.filter(function(line) { return line.intertype !== null });
- });
+function recomputeLines(func) {
+ func.lines = func.labels.map(function(label) { return label.lines }).reduce(concatenator, []);
}
// Handy sets
@@ -71,6 +68,7 @@ function analyzer(data, sidePass) {
subItem.endLineNum = null;
subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them
subItem.labels = [];
+ subItem.forceEmulated = false;
// no explicit 'entry' label in clang on LLVM 2.8 - most of the time, but not all the time! - so we add one if necessary
if (item.items[i+1].intertype !== 'label') {
@@ -142,7 +140,7 @@ function analyzer(data, sidePass) {
var ret = new Array(Math.ceil(bits/32));
var i = 0;
while (bits > 0) {
- ret[i] = { ident: parsed[i].toString(), bits: Math.min(32, bits) };
+ ret[i] = { ident: (parsed[i]|0).toString(), bits: Math.min(32, bits) }; // resign all values
bits -= 32;
i++;
}
@@ -1111,10 +1109,9 @@ function analyzer(data, sidePass) {
});
func.labelIds[toNiceIdent('%0')] = -1; // entry is always -1
- func.hasIndirectBr = false;
func.lines.forEach(function(line) {
if (line.intertype == 'indirectbr') {
- func.hasIndirectBr = true;
+ func.forceEmulated = true;
}
});
@@ -1129,6 +1126,52 @@ function analyzer(data, sidePass) {
return null;
}
+ // Basic longjmp support, see library.js setjmp/longjmp
+ var setjmp = toNiceIdent('@setjmp');
+ func.setjmpTable = null;
+ for (var i = 0; i < func.labels.length; i++) {
+ var label = func.labels[i];
+ for (var j = 0; j < label.lines.length; j++) {
+ var line = label.lines[j];
+ if (line.intertype == 'call' && line.ident == setjmp) {
+ // Add a new label
+ var oldIdent = label.ident;
+ var newIdent = oldIdent + '$$' + i;
+ if (!func.setjmpTable) func.setjmpTable = [];
+ func.setjmpTable.push([oldIdent, newIdent, line.assignTo]);
+ func.labels.splice(i+1, 0, {
+ intertype: 'label',
+ ident: newIdent,
+ lineNum: label.lineNum + 0.5,
+ lines: label.lines.slice(j+1)
+ });
+ label.lines = label.lines.slice(0, j+1);
+ label.lines.push({
+ intertype: 'branch',
+ label: toNiceIdent(newIdent),
+ lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
+ });
+ // Correct phis
+ func.labels.forEach(function(label) {
+ label.lines.forEach(function(phi) {
+ if (phi.intertype == 'phi') {
+ for (var i = 0; i < phi.params.length; i++) {
+ var sourceLabelId = getActualLabelId(phi.params[i].label);
+ if (sourceLabelId == oldIdent) {
+ phi.params[i].label = newIdent;
+ }
+ }
+ }
+ });
+ });
+ }
+ }
+ }
+ if (func.setjmpTable) {
+ func.forceEmulated = true;
+ recomputeLines(func);
+ }
+
if (!MICRO_OPTS) {
// 'Emulate' phis, by doing an if where the phi appears in the .ll. For this
// we need __lastLabel__.
@@ -1223,8 +1266,7 @@ 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') break;
- assert(isNumber(item.allocatedNum));
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ?
calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0;
if (USE_TYPED_ARRAYS === 2) {
@@ -1235,7 +1277,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') break;
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break;
item.allocatedIndex = index;
index += item.allocatedSize;
delete item.allocatedSize;
@@ -1260,7 +1302,7 @@ function analyzer(data, sidePass) {
var finishedInitial = false;
for (var i = 0; i < lines.length; i++) {
var item = lines[i];
- if (!item.assignTo || item.intertype != 'alloca') {
+ if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) {
finishedInitial = true;
continue;
}
@@ -1745,7 +1787,7 @@ function analyzer(data, sidePass) {
// TODO: each of these can be run in parallel
item.functions.forEach(function(func) {
dprint('relooping', "// relooping function: " + func.ident);
- func.block = makeBlock(func.labels, [toNiceIdent(func.labels[0].ident)], func.labelsDict, func.hasIndirectBr);
+ func.block = makeBlock(func.labels, [toNiceIdent(func.labels[0].ident)], func.labelsDict, func.forceEmulated);
});
return finish();
diff --git a/src/jsifier.js b/src/jsifier.js
index ea4dff06..bc827135 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -66,7 +66,7 @@ function JSify(data, functionsOnly, givenFunctions) {
assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.')
libFuncsToInclude = [];
for (var key in LibraryManager.library) {
- if (!key.match(/__(deps|postset)$/)) {
+ if (!key.match(/__(deps|postset|inline)$/)) {
libFuncsToInclude.push(key);
}
}
@@ -576,12 +576,28 @@ function JSify(data, functionsOnly, givenFunctions) {
if (block.entries.length == 1) {
ret += indent + '__label__ = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + block.entries[0] + ' */' : '') + '\n';
} // otherwise, should have been set before!
- ret += indent + 'while(1) switch(__label__) {\n';
+ if (func.setjmpTable) {
+ var setjmpTable = {};
+ 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 },';
+ });
+ ret += 'dummy: 0';
+ ret += '};\n';
+ }
+ ret += indent + 'while(1) ';
+ if (func.setjmpTable) {
+ ret += 'try { ';
+ }
+ ret += 'switch(__label__) {\n';
ret += block.labels.map(function(label) {
return indent + ' case ' + getLabelId(label.ident) + ': // ' + label.ident + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n');
ret += '\n' + indent + ' default: assert(0, "bad label: " + __label__);\n' + indent + '}';
+ if (func.setjmpTable) {
+ ret += ' } catch(e) { if (!e.longjmp) throw(e); setjmpTable[e.label](e.value) }';
+ }
} else {
ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent);
}
@@ -1029,10 +1045,14 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('mathop', processMathop);
makeFuncLineActor('bitcast', function(item) {
- return processMathop({
+ var temp = {
op: 'bitcast', variant: null, type: item.type,
+ assignTo: item.assignTo,
param1: item.params[0]
- });
+ };
+ var ret = processMathop(temp);
+ if (!temp.assignTo) item.assignTo = null; // If the assign was stolen, propagate that
+ return ret;
});
function makeFunctionCall(ident, params, funcData, type) {
diff --git a/src/library.js b/src/library.js
index 6feb37e4..cda318d7 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3296,7 +3296,7 @@ LibraryManager.library = {
},
atexit: function(func, arg) {
- __ATEXIT__.push({ func: func, arg: arg });
+ __ATEXIT__.unshift({ func: func, arg: arg });
},
__cxa_atexit: 'atexit',
@@ -4384,7 +4384,7 @@ LibraryManager.library = {
ptrTV -= {{{ Runtime.QUANTUM_SIZE }}};
var TI = {{{ makeGetValue('ptrTV', '0', '*') }}};
do {
- if (TI == attemptedTI) return 1;
+ if (TI == attemptedTI) return ptr;
// Go to parent class
var type_infoAddr = {{{ makeGetValue('TI', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}};
var type_info = {{{ makeGetValue('type_infoAddr', '0', '*') }}};
@@ -5349,19 +5349,26 @@ LibraryManager.library = {
// ==========================================================================
// setjmp.h
+ //
+ // Basic support for setjmp/longjmp: enough to run the wikipedia example and
+ // hopefully handle most normal behavior. We do not support cases where
+ // longjmp behavior is undefined (for example, if the setjmp function returns
+ // before longjmp is called).
+ //
+ // Note that we need to emulate functions that use setjmp, and also to create
+ // a new label we can return to. Emulation make such functions slower, this
+ // can be alleviated by making a new function containing just the setjmp
+ // related functionality so the slowdown is more limited.
// ==========================================================================
- setjmp: function(env) {
- // XXX print('WARNING: setjmp() not really implemented, will fail if longjmp() is actually called');
- return 0;
+ setjmp__inline: function(env) {
+ // Save the label
+ return '(' + makeSetValue(env, '0', '__label__', 'i32') + ', 0)';
},
- _setjmp: 'setjmp',
- longjmp: function(env, val) {
- // not really working...
- assert(0);
+ longjmp: function(env, value) {
+ throw { longjmp: true, label: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
},
- _longjmp: 'longjmp',
// ==========================================================================
// signal.h
diff --git a/src/parseTools.js b/src/parseTools.js
index 2ab43ebf..c1eff803 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -546,7 +546,7 @@ function splitI64(value) {
// be slightly higher than expected. And if we get 4294967296, that will turn into a 0 if put into a
// HEAP32 or |0'd, etc.
if (legalizedI64s) {
- return [value + '>>>0', 'Math.min(Math.floor(' + value + '/4294967296), 4294967295)'];
+ return [value + '>>>0', 'Math.min(Math.floor((' + value + ')/4294967296), 4294967295)'];
} else {
return makeInlineCalculation(makeI64('VALUE>>>0', 'Math.min(Math.floor(VALUE/4294967296), 4294967295)'), value, 'tempBigIntP');
}
@@ -606,6 +606,22 @@ function parseArbitraryInt(str, bits) {
}
}
+ function mul2(v) { // v *= 2
+ for (var i = v.length-1; i >= 0; i--) {
+ var d = v[i]*2;
+ r = d >= 10;
+ v[i] = d%10;
+ var j = i-1;
+ if (r) {
+ if (j < 0) {
+ v.unshift(1);
+ break;
+ }
+ v[j] += 0.5; // will be multiplied
+ }
+ }
+ }
+
function subtract(v, w) { // v -= w. we assume v >= w
while (v.length > w.length) w.splice(0, 0, 0);
for (var i = 0; i < v.length; i++) {
@@ -635,9 +651,11 @@ function parseArbitraryInt(str, bits) {
if (str[0] == '-') {
// twos-complement is needed
- assert(bits == 64, "we only support 64-bit two's complement so far");
str = str.substr(1);
- v = str2vec('18446744073709551616'); // 2^64
+ v = str2vec('1');
+ for (var i = 0; i < bits; i++) {
+ mul2(v);
+ }
subtract(v, str2vec(str));
} else {
v = str2vec(str);
@@ -1644,7 +1662,7 @@ function processMathop(item) {
case 'fptoui': case 'fptosi': return finish(splitI64(ident1));
case 'icmp': {
switch (variant) {
- case 'uge': return high1 + ' >= ' + high2 + ' && (' + high1 + ' > ' + high + ' || ' +
+ case 'uge': return high1 + ' >= ' + high2 + ' && (' + high1 + ' > ' + high2 + ' || ' +
low1 + ' >= ' + low2 + ')';
case 'sge': return '(' + high1 + '|0) >= (' + high2 + '|0) && ((' + high1 + '|0) > (' + high2 + '|0) || ' +
'(' + low1 + '|0) >= (' + low2 + '|0))';
@@ -1685,9 +1703,17 @@ function processMathop(item) {
var inType = item.param1.type;
var outType = item.type;
if (inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) {
- return makeInlineCalculation('tempDoubleI32[0]=VALUE[0],tempDoubleI32[1]=VALUE[1],tempDoubleF64[0]', ident1, 'tempI64');
+ if (legalizedI64s) {
+ return '(tempDoubleI32[0]=' + ident1 + '$0, tempDoubleI32[1]=' + ident1 + '$1, tempDoubleF64[0])';
+ } else {
+ return makeInlineCalculation('tempDoubleI32[0]=VALUE[0],tempDoubleI32[1]=VALUE[1],tempDoubleF64[0]', ident1, 'tempI64');
+ }
} else if (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES) {
- return '(tempDoubleF64[0]=' + ident1 + ',[tempDoubleI32[0],tempDoubleI32[1]])';
+ if (legalizedI64s) {
+ return 'tempDoubleF64[0]=' + ident1 + '; ' + finish(['tempDoubleI32[0]','tempDoubleI32[1]']);
+ } else {
+ return '(tempDoubleF64[0]=' + ident1 + ',[tempDoubleI32[0],tempDoubleI32[1]])';
+ }
} else {
throw 'Invalid I64_MODE1 bitcast: ' + dump(item) + ' : ' + item.param1.type;
}
diff --git a/src/preamble.js b/src/preamble.js
index e957c212..54e3b04c 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -357,13 +357,71 @@ function assert(condition, text) {
}
}
+var globalScope = this;
+
+// C calling interface. A convenient way to call C functions (in C files, or
+// defined with extern "C").
+//
+// Note: LLVM optimizations can inline and remove functions, after which you will not be
+// able to call them. Adding
+//
+// __attribute__((used))
+//
+// to the function definition will prevent that.
+//
+// Note: Closure optimizations will minify function names, making
+// functions no longer callable. If you run closure (on by default
+// in -O2 and above), you should export the functions you will call
+// by calling emcc with something like
+//
+// -s EXPORTED_FUNCTIONS='["_func1","_func2"]'
+//
+// @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C")
+// @param returnType The return type of the function, one of the JS types 'number' or 'string', or 'pointer' for any type of C pointer.
+// @param argTypes An array of the types of arguments for the function (if there are no arguments, this can be ommitted). Types are as in returnType.
+// @param args An array of the arguments to the function, as native JS values (except for 'pointer', which is a 'number').
+// Note that string arguments will be stored on the stack (the JS string will become a C string on the stack).
+// @return The return value, as a native JS value (except for 'pointer', which is a 'number').
+function ccall(ident, returnType, argTypes, args) {
+ function toC(value, type) {
+ if (type == 'string') {
+ var ret = STACKTOP;
+ Runtime.stackAlloc(value.length+1);
+ writeStringToMemory(value, ret);
+ return ret;
+ }
+ return value;
+ }
+ function fromC(value, type) {
+ if (type == 'string') {
+ return Pointer_stringify(value);
+ }
+ return value;
+ }
+ try {
+ var func = eval('_' + ident);
+ } catch(e) {
+ try {
+ func = globalScope['Module']['_' + ident]; // closure exported function
+ } catch(e) {}
+ }
+ assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)');
+ var i = 0;
+ var cArgs = args ? args.map(function(arg) {
+ return toC(arg, argTypes[i++]);
+ }) : [];
+ return fromC(func.apply(null, cArgs), returnType);
+}
+Module["ccall"] = ccall;
+
// Sets a value in memory in a dynamic way at run-time. Uses the
// type data. This is the same as makeSetValue, except that
// makeSetValue is done at compile-time and generates the needed
// code then, whereas this function picks the right code at
// run-time.
// Note that setValue and getValue only do *aligned* writes and reads!
-
+// Note that ccall uses JS types as for defining types, while setValue and
+// getValue need LLVM types ('i8', 'i32') - this is a lower-level operation
function setValue(ptr, value, type, noSafe) {
type = type || 'i8';
if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit
@@ -398,7 +456,6 @@ function setValue(ptr, value, type, noSafe) {
Module['setValue'] = setValue;
// Parallel to setValue.
-
function getValue(ptr, type, noSafe) {
type = type || 'i8';
if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit
@@ -803,6 +860,7 @@ function writeStringToMemory(string, buffer, dontAddNull) {
{{{ makeSetValue('buffer', 'i', '0', 'i8') }}}
}
}
+Module['writeStringToMemory'] = writeStringToMemory;
var STRING_TABLE = [];
diff --git a/tests/cases/allocavartop.ll b/tests/cases/allocavartop.ll
new file mode 100644
index 00000000..e9520a19
--- /dev/null
+++ b/tests/cases/allocavartop.ll
@@ -0,0 +1,17 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.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(i32 %argc) {
+entry:
+ %retval = alloca i32, i32 %argc, align 4 ; [#uses=1 type=i32*]
+ 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/cases/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll
index 67cd9feb..1249c7eb 100644
--- a/tests/cases/legalizer_ta2.ll
+++ b/tests/cases/legalizer_ta2.ll
@@ -19,7 +19,7 @@ entry:
%temp.buffer = bitcast i8* %buffer to [0 x i8]*
%buffer1 = getelementptr [0 x i8]* %temp.buffer, i32 0, i32 1
%bundled1 = bitcast i8* %buffer1 to i104*
- store i104 31079605376604435891501163880, i104* %bundled1, align 1 ; unaligned
+ store i104 -20251329998275065988055750122136, i104* %bundled1, align 1 ; unaligned
call i32 (i8*)* @puts(i8* %buffer)
; shifts
diff --git a/tests/cases/longjmp_tiny.ll b/tests/cases/longjmp_tiny.ll
new file mode 100644
index 00000000..0045847c
--- /dev/null
+++ b/tests/cases/longjmp_tiny.ll
@@ -0,0 +1,65 @@
+; ModuleID = '/tmp/emscripten_temp/src.cpp.o'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@_ZL3buf = internal global [20 x i16] zeroinitializer, align 2
+@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00", align 1
+@.str1 = private unnamed_addr constant [6 x i8] c"more\0A\00", align 1
+
+define i32 @main() {
+ %retval = alloca i32, align 4
+ store i32 0, i32* %retval
+ %call = call i32 @setjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0)) returns_twice, !dbg !20
+ %tobool = icmp ne i32 %call, 0, !dbg !20
+ br i1 %tobool, label %if.else, label %if.then, !dbg !20
+
+if.then: ; preds = %entry
+ %call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([13 x i8]* @.str, i32 0, i32 0)), !dbg !22
+ call void @longjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0), i32 10), !dbg !24
+ br label %if.end, !dbg !25
+
+if.else: ; preds = %entry
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0)), !dbg !26
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ ret i32 0, !dbg !28
+}
+
+declare i32 @setjmp(i16*) returns_twice
+
+declare i32 @printf(i8*, ...)
+
+declare void @longjmp(i16*, i32)
+
+!llvm.dbg.cu = !{!0}
+
+!0 = metadata !{i32 786449, i32 0, i32 4, metadata !"/tmp/emscripten_temp/src.cpp", metadata !"/home/alon/Dev/emscripten", metadata !"clang version 3.1 (trunk 150936)", i1 true, i1 false, metadata !"", i32 0, metadata !1, metadata !1, metadata !3, metadata !12} ; [ DW_TAG_compile_unit ]
+!1 = metadata !{metadata !2}
+!2 = metadata !{i32 0}
+!3 = metadata !{metadata !4}
+!4 = metadata !{metadata !5}
+!5 = metadata !{i32 786478, i32 0, metadata !6, metadata !"main", metadata !"main", metadata !"", metadata !6, i32 7, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 ()* @main, null, null, metadata !10} ; [ DW_TAG_subprogram ]
+!6 = metadata !{i32 786473, metadata !"/tmp/emscripten_temp/src.cpp", metadata !"/home/alon/Dev/emscripten", null} ; [ DW_TAG_file_type ]
+!7 = metadata !{i32 786453, i32 0, metadata !"", i32 0, i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, i32 0} ; [ DW_TAG_subroutine_type ]
+!8 = metadata !{metadata !9}
+!9 = metadata !{i32 786468, null, metadata !"int", null, i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ]
+!10 = metadata !{metadata !11}
+!11 = metadata !{i32 786468} ; [ DW_TAG_base_type ]
+!12 = metadata !{metadata !13}
+!13 = metadata !{metadata !14}
+!14 = metadata !{i32 786484, i32 0, null, metadata !"buf", metadata !"buf", metadata !"_ZL3buf", metadata !6, i32 5, metadata !15, i32 1, i32 1, [20 x i16]* @_ZL3buf} ; [ DW_TAG_variable ]
+!15 = metadata !{i32 786454, null, metadata !"jmp_buf", metadata !6, i32 279, i64 0, i64 0, i64 0, i32 0, metadata !16} ; [ DW_TAG_typedef ]
+!16 = metadata !{i32 786433, null, metadata !"", null, i32 0, i64 320, i64 16, i32 0, i32 0, metadata !17, metadata !18, i32 0, i32 0} ; [ DW_TAG_array_type ]
+!17 = metadata !{i32 786468, null, metadata !"unsigned short", null, i32 0, i64 16, i64 16, i64 0, i32 0, i32 7} ; [ DW_TAG_base_type ]
+!18 = metadata !{metadata !19}
+!19 = metadata !{i32 786465, i64 0, i64 19} ; [ DW_TAG_subrange_type ]
+!20 = metadata !{i32 8, i32 18, metadata !21, null}
+!21 = metadata !{i32 786443, metadata !5, i32 7, i32 22, metadata !6, i32 0} ; [ DW_TAG_lexical_block ]
+!22 = metadata !{i32 9, i32 15, metadata !23, null}
+!23 = metadata !{i32 786443, metadata !21, i32 8, i32 31, metadata !6, i32 1} ; [ DW_TAG_lexical_block ]
+!24 = metadata !{i32 10, i32 15, metadata !23, null}
+!25 = metadata !{i32 11, i32 13, metadata !23, null}
+!26 = metadata !{i32 12, i32 15, metadata !27, null}
+!27 = metadata !{i32 786443, metadata !21, i32 11, i32 20, metadata !6, i32 2} ; [ DW_TAG_lexical_block ]
+!28 = metadata !{i32 14, i32 13, metadata !21, null}
diff --git a/tests/cases/longjmp_tiny.txt b/tests/cases/longjmp_tiny.txt
new file mode 100644
index 00000000..8a0aa386
--- /dev/null
+++ b/tests/cases/longjmp_tiny.txt
@@ -0,0 +1,2 @@
+hello world
+more
diff --git a/tests/runner.py b/tests/runner.py
index e599d30e..c2240141 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -653,6 +653,97 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
self.do_run(src, '*1*\n*0*\n*0*\n')
+ def test_i64_b(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
+
+ src = r'''
+ #include <stdio.h>
+ #include <sys/time.h>
+
+ typedef long long int64;
+
+ #define PRMJ_USEC_PER_SEC 1000000L
+
+ int main(int argc, char * argv[]) {
+ int64 sec = 1329409675 + argc;
+ int64 usec = 2329509675;
+ int64 mul = int64(sec) * PRMJ_USEC_PER_SEC;
+ int64 add = mul + int64(usec);
+ int add_low = add;
+ int add_high = add >> 32;
+ printf("*%lld,%lld,%u,%u*\n", mul, add, add_low, add_high);
+ return 0;
+ }
+ '''
+
+ self.do_run(src, '*1329409676000000,1329412005509675,3663280683,309527*\n')
+
+ def test_i64_cmp(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
+
+ src = r'''
+ #include <stdio.h>
+
+ typedef long long int64;
+
+ bool compare(int64 val) {
+ return val == -12;
+ }
+
+ bool compare2(int64 val) {
+ return val < -12;
+ }
+
+ int main(int argc, char * argv[]) {
+ printf("*%d,%d,%d,%d,%d,%d*\n", argc, compare(argc-1-12), compare(1000+argc), compare2(argc-1-10), compare2(argc-1-14), compare2(argc+1000));
+ return 0;
+ }
+ '''
+
+ self.do_run(src, '*1,1,0,0,1,0*\n')
+
+ def test_i64_double(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2')
+ src = r'''
+ #include <stdio.h>
+
+ typedef long long int64;
+ #define JSDOUBLE_HI32_SIGNBIT 0x80000000
+
+ bool JSDOUBLE_IS_NEGZERO(double d)
+ {
+ union {
+ struct {
+ unsigned int lo, hi;
+ } s;
+ double d;
+ } x;
+ if (d != 0)
+ return false;
+ x.d = d;
+ return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0;
+ }
+
+ bool JSINT64_IS_NEGZERO(int64 l)
+ {
+ union {
+ int64 i;
+ double d;
+ } x;
+ if (l != 0)
+ return false;
+ x.i = l;
+ return x.d == -0;
+ }
+
+ int main(int argc, char * argv[]) {
+ printf("*%d,%d,%d,%d*\n", JSDOUBLE_IS_NEGZERO(0), JSDOUBLE_IS_NEGZERO(-0), JSDOUBLE_IS_NEGZERO(-1), JSDOUBLE_IS_NEGZERO(+1));
+ printf("*%d,%d,%d,%d*\n", JSINT64_IS_NEGZERO(0), JSINT64_IS_NEGZERO(-0), JSINT64_IS_NEGZERO(-1), JSINT64_IS_NEGZERO(+1));
+ return 0;
+ }
+ '''
+ self.do_run(src, '*0,0,0,0*\n*1,1,0,0*\n') # same as gcc
+
def test_unaligned(self):
if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1')
@@ -1319,6 +1410,41 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
'''
self.do_run(src, 'Assertion failed: 1 == false')
+ def test_longjmp(self):
+ src = r'''
+ #include <stdio.h>
+ #include <setjmp.h>
+
+ static jmp_buf buf;
+
+ void second(void) {
+ printf("second\n"); // prints
+ longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1
+ }
+
+ void first(void) {
+ second();
+ printf("first\n"); // does not print
+ }
+
+ int main() {
+ int x = 0;
+ if ( ! setjmp(buf) ) {
+ x++;
+ first(); // when executed, setjmp returns 0
+ } else { // when longjmp jumps back, setjmp returns 1
+ printf("main: %d\n", x); // prints
+ }
+
+ return 0;
+ }
+ '''
+ # gcc -O0 and -O2 differ in what they do with the saved state of local vars - and we match that
+ if self.emcc_args is None or ('-O1' not in self.emcc_args and '-O2' not in self.emcc_args):
+ self.do_run(src, 'second\nmain: 1\n')
+ else:
+ self.do_run(src, 'second\nmain: 0\n')
+
def test_exceptions(self):
if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1")
@@ -1547,6 +1673,25 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
self.do_run(src, '*11,74,32,1012*\n*11*\n*22*')
def test_dynamic_cast(self):
+ src = r'''
+ #include <stdio.h>
+
+ struct Support {
+ virtual void f() {
+ printf("f()\n");
+ }
+ };
+
+ struct Derived : Support {
+ };
+
+ int main() {
+ Support * p = new Derived;
+ dynamic_cast<Derived*>(p)->f();
+ }
+ '''
+ self.do_run(src, 'f()\n')
+
src = '''
#include <stdio.h>
@@ -2418,6 +2563,27 @@ def process(filename):
self.do_run(src, '*1*', force_c=True)
+ def test_atexit(self):
+ # Confirms they are called in reverse order
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ static void cleanA() {
+ printf("A");
+ }
+ static void cleanB() {
+ printf("B");
+ }
+
+ int main() {
+ atexit(cleanA);
+ atexit(cleanB);
+ return 0;
+ }
+ '''
+ self.do_run(src, 'BA')
+
def test_time(self):
# XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again.
src = open(path_from_root('tests', 'time', 'src.c'), 'r').read()
@@ -4792,6 +4958,69 @@ Block 0: ''', post_build=post1)
### Integration tests
+ def test_ccall(self):
+ if self.emcc_args is not None and '-O2' in self.emcc_args:
+ self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right
+
+ src = r'''
+ #include <stdio.h>
+
+ // Optimizations might wipe out our functions without this
+ #define KEEPALIVE __attribute__((used))
+
+ extern "C" {
+ int KEEPALIVE get_int() { return 5; }
+ float KEEPALIVE get_float() { return 3.14; }
+ char * KEEPALIVE get_string() { return "hello world"; }
+ void KEEPALIVE print_int(int x) { printf("%d\n", x); }
+ void KEEPALIVE print_float(float x) { printf("%.2f\n", x); }
+ void KEEPALIVE print_string(char *x) { printf("%s\n", x); }
+ int KEEPALIVE multi(int x, float y, int z, char *str) { puts(str); return (x+y)*z; }
+ int * KEEPALIVE pointer(int *in) { printf("%d\n", *in); static int ret = 21; return &ret; }
+ }
+
+ int main(int argc, char **argv) {
+ // keep them alive
+ if (argc == 10) return get_int();
+ if (argc == 11) return get_float();
+ if (argc == 12) return get_string()[0];
+ if (argc == 13) print_int(argv[0][0]);
+ if (argc == 14) print_float(argv[0][0]);
+ if (argc == 15) print_string(argv[0]);
+ if (argc == 16) pointer((int*)argv[0]);
+ if (argc % 17 == 12) return multi(argc, float(argc)/2, argc+1, argv[0]);
+ return 0;
+ }
+ '''
+
+ post = '''
+def process(filename):
+ src = \'\'\'
+ var Module = {
+ 'postRun': function() {
+ print('*');
+ var ret;
+ ret = ccall('get_int', 'number'); print([typeof ret, ret]);
+ ret = ccall('get_float', 'number'); print([typeof ret, ret.toFixed(2)]);
+ ret = ccall('get_string', 'string'); print([typeof ret, ret]);
+ ret = ccall('print_int', null, ['number'], [12]); print(typeof ret);
+ ret = ccall('print_float', null, ['number'], [14.56]); print(typeof ret);
+ ret = ccall('print_string', null, ['string'], ["cheez"]); print(typeof ret);
+ ret = ccall('multi', 'number', ['number', 'number', 'number', 'string'], [2, 1.4, 3, 'more']); print([typeof ret, ret]);
+ var p = ccall('malloc', 'pointer', ['number'], [4]);
+ setValue(p, 650, 'i32');
+ ret = ccall('pointer', 'pointer', ['pointer'], [p]); print([typeof ret, getValue(ret, 'i32')]);
+ print('*');
+ }
+ };
+ \'\'\' + open(filename, 'r').read()
+ open(filename, 'w').write(src)
+'''
+
+ Settings.EXPORTED_FUNCTIONS = ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_malloc']
+
+ self.do_run(src, '*\nnumber,5\nnumber,3.14\nstring,hello world\n12\nundefined\n14.56\nundefined\ncheez\nundefined\nmore\nnumber,10\n650\nnumber,21\n*\n', post_build=post)
+
def test_scriptaclass(self):
header_filename = os.path.join(self.get_dir(), 'header.h')
header = '''
@@ -5457,16 +5686,21 @@ def process(filename):
def test_exit_status(self):
Settings.CATCH_EXIT_CODE = 1
- src = '''
+ src = r'''
#include <stdio.h>
#include <stdlib.h>
+ static void cleanup() {
+ printf("cleanup\n");
+ }
+
int main()
{
- printf("hello, world!\\n");
+ atexit(cleanup); // this atexit should still be called
+ printf("hello, world!\n");
exit(118); // Unusual exit status to make sure it's working!
}
'''
- self.do_run(src, 'hello, world!\nExit Status: 118')
+ self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118')
# Generate tests for everything
@@ -5615,7 +5849,7 @@ Options that are modified or new in %s include:
# emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file
# regression check: -o js should create "js", with bitcode content
- for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'js']]:
+ for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]:
target = args[1] if len(args) == 2 else 'hello_world.o'
clear()
Popen([compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate()
@@ -5629,6 +5863,16 @@ Options that are modified or new in %s include:
assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output))
self.assertContained('hello, world!', run_js(target + '.js'))
+ # handle singleton archives
+ clear()
+ Popen([compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate()
+ Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate()
+ assert os.path.exists('a.a')
+ shutil.copyfile('a.a', '/home/alon/a.a')
+ output = Popen([compiler, 'a.a']).communicate()
+ assert os.path.exists('a.out.js'), output
+ self.assertContained('hello, world!', run_js('a.out.js'))
+
# emcc src.ll ==> generates .js
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate()
@@ -5915,6 +6159,7 @@ elif 'benchmark' in str(sys.argv):
pass
finally:
os.chdir(d)
+ fingerprint.append('llvm: ' + LLVM_ROOT)
print 'Running Emscripten benchmarks... [ %s ]' % ' | '.join(fingerprint)
sys.argv = filter(lambda x: x != 'benchmark', sys.argv)
@@ -6075,7 +6320,7 @@ elif 'benchmark' in str(sys.argv):
'''
self.do_benchmark(src, [], 'final: 720.')
- def test_files(self):
+ def zzztest_files(self):
src = r'''
#include<stdio.h>
#include<stdlib.h>
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py
index bdb3e755..17df1231 100755
--- a/tools/bindings_generator.py
+++ b/tools/bindings_generator.py
@@ -95,24 +95,38 @@ all_h.write(text)
all_h.close()
parsed = CppHeaderParser.CppHeader(all_h_name)
-for classname, clazz in parsed.classes.iteritems():
- print 'zz see', classname
+print 'zz dir: ', parsed.__dict__.keys()
+for classname, clazz in parsed.classes.items() + parsed.structs.items():
+ print 'zz see', classname, clazz, type(clazz)
classes[classname] = clazz
- clazz['methods'] = clazz['methods']['public'] # CppHeaderParser doesn't have 'public' etc. in structs. so equalize to that
+ if type(clazz['methods']) == dict:
+ clazz['saved_methods'] = clazz['methods']
+ clazz['methods'] = clazz['methods']['public'] # CppHeaderParser doesn't have 'public' etc. in structs. so equalize to that
if '::' in classname:
assert classname.count('::') == 1
parents[classname.split('::')[1]] = classname.split('::')[0]
- for sname, struct in clazz._public_structs.iteritems():
- parents[sname] = classname
- classes[classname + '::' + sname] = struct
- struct['name'] = sname # Missing in CppHeaderParser
- print 'zz seen struct %s in %s' % (sname, classname)
-
+ if hasattr(clazz, '_public_structs'): # This is a class
+ for sname, struct in clazz._public_structs.iteritems():
+ parents[sname] = classname
+ classes[classname + '::' + sname] = struct
+ struct['name'] = sname # Missing in CppHeaderParser
+ print 'zz seen struct %s in %s' % (sname, classname)
+
+ if 'fields' in clazz: # This is a struct
+ print 'zz add properties!'
+ clazz['properties'] = { 'public': clazz['fields'] }
+ clazz['name'] = classname
+ clazz['inherits'] = []
print 'zz parents: ', parents
-for classname, clazz in classes.iteritems():
+def check_has_constructor(clazz):
+ for method in clazz['methods']:
+ if method['constructor'] and not method['destructor']: return True
+ return False
+
+for classname, clazz in parsed.classes.items() + parsed.structs.items():
# Various precalculations
print 'zz precalc', classname
for method in clazz['methods'][:]:
@@ -157,7 +171,7 @@ for classname, clazz in classes.iteritems():
print 'zz subsubsub ', classname, method['name'], method['parameters'][0]
method['name'] = 'op_sub'
if len(method['parameters'][0]) == 0:
- method['operator'] = ' return -*self; // %d' % len(method['parameters'][0])
+ method['operator'] = ' static %s ret; ret = -*self; return ret;' % method['returns']
else:
method['operator'] = ' return *self -= arg0; // %d : %s' % (len(method['parameters'][0]), method['parameters'][0][0]['name'])
elif 'imul' in method['name']:
@@ -180,8 +194,8 @@ for classname, clazz in classes.iteritems():
method['name'] = 'op_comp'
method['operator'] = ' return arg0 == arg1;' if len(method['parameters'][0]) == 2 else ' return *self == arg0;'
else:
- print 'zz unknown operator:', method['name']
- 1/0.
+ print 'zz unknown operator:', method['name'], ', ignoring'
+ method['ignore'] = True
# Fill in some missing stuff
method['returns_text'] = method['returns_text'].replace('&', '').replace('*', '')
@@ -237,21 +251,31 @@ for classname, clazz in classes.iteritems():
}]],
})
- # Add destroyer
- if not clazz.get('abstract'):
- clazz['methods'].append({
- 'destroyer': True,
- 'name': '__destroy__',
- 'constructor': False,
- 'destructor': False,
- 'static': False,
- 'returns': 'void',
- 'returns_text': 'void',
- 'returns_reference': False,
- 'returns_pointer': False,
- 'pure_virtual': False,
- 'parameters': [[]],
- })
+ print 'zz is effectively abstract?', clazz['name'], classname, '0'
+ if 'saved_methods' in clazz and not check_has_constructor(clazz):
+ print 'zz is effectively abstract?', clazz['name'], '1'
+ # Having a private constructor and no public constructor means you are, in effect, abstract
+ for private_method in clazz['saved_methods']['private']:
+ print 'zz is effectively abstract?', clazz['name'], '2'
+ if private_method['constructor']:
+ print 'zz is effectively abstract?', clazz['name'], '3'
+ clazz['effectively_abstract'] = True
+
+ # Add destroyer
+ if not clazz.get('abstract') and not clazz.get('effectively_abstract'):
+ clazz['methods'].append({
+ 'destroyer': True,
+ 'name': '__destroy__',
+ 'constructor': False,
+ 'destructor': False,
+ 'static': False,
+ 'returns': 'void',
+ 'returns_text': 'void',
+ 'returns_reference': False,
+ 'returns_pointer': False,
+ 'pure_virtual': False,
+ 'parameters': [[]],
+ })
clazz['methods'] = filter(lambda method: not method.get('ignore'), clazz['methods'])
@@ -277,7 +301,7 @@ def copy_args(args):
ret.append(copiedarg)
return ret
-for classname, clazz in parsed.classes.iteritems():
+for classname, clazz in parsed.classes.items() + parsed.structs.items():
clazz['final_methods'] = {}
def explore(subclass, template_name=None, template_value=None):
@@ -286,10 +310,16 @@ for classname, clazz in parsed.classes.iteritems():
print classname, 'exploring', subclass['name'], '::', method['name']
if method['constructor']:
- if clazz != subclass: continue # Subclasses cannot directly use their parent's constructors
- if method['destructor']: continue # Nothing to do there
+ if clazz != subclass:
+ print "zz Subclasses cannot directly use their parent's constructors"
+ continue
+ if method['destructor']:
+ print 'zz Nothing to do there'
+ continue
- if method.get('operator') and subclass is not clazz: continue # Do not use parent class operators. Cast to that class if you need those operators (castObject)
+ if method.get('operator') and subclass is not clazz:
+ print 'zz Do not use parent class operators. Cast to that class if you need those operators (castObject)'
+ continue
if method['name'] not in clazz['final_methods']:
copied = clazz['final_methods'][method['name']] = {}
@@ -485,6 +515,7 @@ def generate_wrapping_code(classname):
# %(classname)s.prototype['fields'] = Runtime.generateStructInfo(null, '%(classname)s'); - consider adding this
def generate_class(generating_classname, classname, clazz): # TODO: deprecate generating?
+ print 'zz generating:', generating_classname, classname
generating_classname_head = generating_classname.split('::')[-1]
classname_head = classname.split('::')[-1]
@@ -498,6 +529,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
gen_js.write('''Module['%s'] = %s;
''' % (generating_classname_head, generating_classname_head))
+ print 'zz methods: ', clazz['final_methods'].keys()
for method in clazz['final_methods'].itervalues():
mname = method['name']
if classname_head + '::' + mname in ignored:
@@ -509,7 +541,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
destructor = method['destructor']
static = method['static']
- #print 'zz generating %s::%s. gets %s and returns %s' % (generating_classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text'])
+ print 'zz generating %s::%s' % (generating_classname, method['name'])
if destructor: continue
if constructor and inherited: continue
@@ -644,7 +676,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
print 'zz making return', classname, method['name'], method['returns'], return_value
if method['returns'] in classes:
# Generate a wrapper
- calls += 'return wrapPointer(%s, %s);' % (return_value, method['returns'].split('::')[-1])
+ calls += 'return wrapPointer(%s, Module.%s);' % (return_value, method['returns'].split('::')[-1])
else:
# Normal return
calls += ('return ' if ret != 'void' else '') + return_value + ';'
@@ -686,7 +718,7 @@ Module['%s'] = %s;
# Main loop
-for classname, clazz in classes.iteritems():
+for classname, clazz in parsed.classes.items() + parsed.structs.items():
if any([name in ignored for name in classname.split('::')]):
print 'zz ignoring', classname
continue
@@ -727,18 +759,17 @@ for classname, clazz in classes.iteritems():
print 'zz ignoring pure virtual class', classname, 'due to', method['name']
return True
- clazz['abstract'] = check_pure_virtual(clazz, [])
+ clazz['abstract'] = check_pure_virtual(clazz, []) or clazz.get('effectively_abstract')
+
print 'zz', classname, 'is abstract?', clazz['abstract']
#if check_pure_virtual(clazz, []):
# continue
# Add a constructor if none exist
- has_constructor = False
- for method in clazz['methods']:
- has_constructor = has_constructor or (method['constructor'] and not method['destructor'])
-
+ has_constructor = check_has_constructor(clazz)
+
print 'zz', classname, 'has constructor?', has_constructor
-
+
if not has_constructor:
if not clazz['abstract']:
print 'zz no constructor for', classname, 'and not abstract, so ignoring'
diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js
index d914a5da..aac21e87 100644
--- a/tools/eliminator/eliminator-test-output.js
+++ b/tools/eliminator/eliminator-test-output.js
@@ -117,4 +117,13 @@ function f3($s, $tree, $k) {
}
HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $0;
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3"]
+function llvm3_1() {
+ while (check()) {
+ if ($curri_01 % $zj_0 == 0) {
+ break;
+ }
+ var $j_0 = $aj_0 + 1;
+ run($j_0 / 2);
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1"]
diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js
index 35bed0bb..55f74d67 100644
--- a/tools/eliminator/eliminator-test.js
+++ b/tools/eliminator/eliminator-test.js
@@ -130,5 +130,14 @@ function f3($s, $tree, $k) {
}
HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $0;
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3"]
-
+function llvm3_1() {
+ while (check()) {
+ var $inc = $aj_0 + 1;
+ if ($curri_01 % $zj_0 == 0) {
+ break;
+ }
+ var $j_0 = $inc;
+ run($j_0 / 2);
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1"]
diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee
index b78a5a7e..ba91aa89 100644
--- a/tools/eliminator/eliminator.coffee
+++ b/tools/eliminator/eliminator.coffee
@@ -49,9 +49,6 @@ NODES_WITHOUT_SIDE_EFFECTS =
# Nodes which may break control flow. Moving a variable beyond them may have
# side effects.
CONTROL_FLOW_NODES =
- return: true
- break: true
- continue: true
new: true
throw: true
call: true
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d84c664a..a0595b88 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -239,7 +239,7 @@ function emptyNode() {
// Dump the AST. Useful for debugging. For example,
// node tools/js-optimizer.js ABSOLUTE_PATH_TO_FILE dumpAst
function dumpAst(ast) {
- printErr(JSON.stringify(ast));
+ printErr(JSON.stringify(ast, null, ' '));
}
function dumpSrc(ast) {
@@ -801,18 +801,24 @@ function vacuum(ast) {
if (node[0] == 'block' && (!node[1] || (typeof node[1] != 'object') || node[1].length == 0 || (node[1].length == 1 && isEmpty(node[1])))) return true;
return false;
}
- function simplifyList(node, i) {
+ function simplifyList(node, si) {
var changed = false;
- var pre = node[i].length;
- node[i] = node[i].filter(function(node) { return !isEmpty(node) });
- if (node[i].length < pre) changed = true;
- // Also, seek blocks with single items we can simplify
- node[i] = node[i].map(function(subNode) {
- if (subNode[0] == 'block' && typeof subNode[1] == 'object' && subNode[1].length == 1 && subNode[1][0][0] == 'if') {
- return subNode[1][0];
+ // Merge block items into this list, thus removing unneeded |{ .. }|'s
+ var statements = node[si];
+ var i = 0;
+ while (i < statements.length) {
+ var subNode = statements[i];
+ if (subNode[0] == 'block') {
+ statements.splice.apply(statements, [i, 1].concat(subNode[1] || []));
+ changed = true;
+ } else {
+ i++;
}
- return subNode;
- });
+ }
+ // Remove empty items
+ var pre = node[si].length;
+ node[si] = node[si].filter(function(node) { return !isEmpty(node) });
+ if (node[si].length < pre) changed = true;
if (changed) {
return node;
}
@@ -1002,6 +1008,35 @@ function hoistMultiples(ast) {
});
vacuum(ast);
+
+ // Afterpass: Reduce
+ // if (..) { .. break|continue } else { .. }
+ // to
+ // if (..) { .. break|continue } ..
+ traverseGenerated(ast, function(container, type) {
+ var statements = getStatements(container);
+ if (!statements) return;
+ for (var i = 0; i < statements.length; i++) {
+ var node = statements[i];
+ if (node[0] == 'if' && node[2][0] == 'block' && node[3] && node[3][0] == 'block') {
+ var stat1 = node[2][1], stat2 = node[3][1];
+ // If break|continue in the latter and not the former, reverse them
+ if (!(stat1[stat1.length-1][0] in LOOP_FLOW) && (stat2[stat2.length-1][0] in LOOP_FLOW)) {
+ var temp = node[3];
+ node[3] = node[2];
+ node[2] = temp;
+ node[1] = ['unary-prefix', '!', node[1]];
+ simplifyNotComps(node[1]); // bake the ! into the expression
+ stat1 = node[2][1];
+ stat2 = node[3][1];
+ }
+ if (stat1[stat1.length-1][0] in LOOP_FLOW) {
+ statements.splice.apply(statements, [i+1, 0].concat(stat2));
+ node[3] = null;
+ }
+ }
+ }
+ });
}
// Simplifies loops
diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js
index 17c06a95..ca76cae1 100644
--- a/tools/test-js-optimizer-output.js
+++ b/tools/test-js-optimizer-output.js
@@ -68,9 +68,7 @@ function loopy() {
something();
} while (0);
next();
- {
- something();
- }
+ something();
}
function ignoreLoopy() {
b$for_cond$4 : while (1) {
@@ -141,12 +139,11 @@ function hoisting() {
}
pause(7);
while (1) {
- if ($i < $N) {
- somethingElse();
- } else {
+ if ($i >= $N) {
__label__ = 3;
break;
}
+ somethingElse();
if ($i < $N) {
somethingElse();
}
@@ -166,15 +163,43 @@ function hoisting() {
}
__label__ = 39;
break;
- } else {
- __label__ = 38;
}
+ __label__ = 38;
} while (0);
if (__label__ == 38) {
var $79 = $_pr6;
}
pause(9);
var $cmp70 = ($call69 | 0) != 0;
+ pause(10);
+ while (check()) {
+ if ($i < $N) {
+ callOther();
+ break;
+ }
+ somethingElse();
+ if ($i1 < $N) {
+ callOther();
+ continue;
+ }
+ somethingElse();
+ if ($i2 >= $N) {
+ somethingElse();
+ break;
+ }
+ callOther();
+ if ($i3 >= $N) {
+ somethingElse();
+ continue;
+ }
+ callOther();
+ if ($i4 < $N) {
+ callOther();
+ break;
+ }
+ somethingElse();
+ continue;
+ }
}
function innerShouldAlsoBeHoisted() {
function hoisting() {
@@ -208,9 +233,7 @@ function sleep() {
}
function demangle($cmp) {
do {
- if ($cmp) {
- __label__ = 3;
- } else {
+ if (!$cmp) {
if (something()) {
__label__ = 3;
break;
@@ -218,6 +241,7 @@ function demangle($cmp) {
more();
break;
}
+ __label__ = 3;
} while (0);
if (__label__ == 3) {
final();
@@ -259,25 +283,21 @@ function moreLabels() {
}
if ($cmp1) {
break;
- } else {
- inc();
}
+ inc();
}
pause(999);
$while_body$$while_end$31 : do {
if ($cmp3) {
var $6 = $5;
- {
- while (1) {
- var $6;
- $iter = $6 + 3;
- if (FHEAP[$iter + 1] < $pct_addr) {
- var $6 = $iter;
- } else {
- var $_lcssa = $iter;
- break $while_body$$while_end$31;
- }
+ while (1) {
+ var $6;
+ $iter = $6 + 3;
+ if (FHEAP[$iter + 1] >= $pct_addr) {
+ var $_lcssa = $iter;
+ break $while_body$$while_end$31;
}
+ var $6 = $iter;
}
} else {
var $_lcssa = $5;
@@ -286,4 +306,9 @@ function moreLabels() {
var $_lcssa;
cheez();
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels"]
+function notComps() {
+ if (HEAP32[$incdec_ptr71_i + 8 >> 2] != 0) {
+ shoo();
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps"]
diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js
index 95985bc8..18c9ac75 100644
--- a/tools/test-js-optimizer.js
+++ b/tools/test-js-optimizer.js
@@ -226,6 +226,65 @@ function hoisting() {
$if_then72$$if_end73$126 : do {
if (__label__ == 40) {} else if (__label__ == 41) {}
} while (0);
+ pause(10);
+ while(check()) {
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ break;
+ } else if (__label__ == 3) {
+ somethingElse();
+ }
+ if ($i1 < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ continue;
+ } else if (__label__ == 3) {
+ somethingElse();
+ }
+ if ($i2 < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ } else if (__label__ == 3) {
+ somethingElse();
+ break;
+ }
+ if ($i3 < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ } else if (__label__ == 3) {
+ somethingElse();
+ continue;
+ }
+ if ($i4 < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ break;
+ } else if (__label__ == 3) {
+ somethingElse();
+ continue;
+ }
+ }
}
function innerShouldAlsoBeHoisted() {
function hoisting() {
@@ -358,4 +417,9 @@ function moreLabels() {
var $_lcssa;
cheez();
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels"]
+function notComps() {
+ if (!(HEAP32[$incdec_ptr71_i + 8 >> 2] == 0)) {
+ shoo();
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps"]