diff options
53 files changed, 30082 insertions, 29967 deletions
@@ -182,16 +182,31 @@ Options that are modified or new in %s include: ). Note that the path must be absolute, not relative. - -g Use debug info. Note that you need this during - the last compilation phase from bitcode to - JavaScript, or else we will remove it by - default in -O1 and above. - In -O0, line numbers wil be shown in the - generated code. In -O1 and above, the optimizer - removes those comments. This flag does however - have the effect of disabling anything that - causes name mangling or minification (closure - or the registerize pass). + -g Use debug info. When compiling to bitcode, + this is the same as in clang and gcc, it + adds debug info to the object files. When + compiling from source to JS or bitcode to JS, + it is equivalent to -g3 (keep code as debuggable + as possible, except for discarding LLVM + debug info, so no C/C++ line numbers; use + -g4 to get line number debugging info in JS). + + -g<level> When compiling from bitcode to JS, we can + keep the code debuggable to different + degrees. Each of these levels builds on the + previous: + + -g0 Make no effort to keep code debuggable. + Will discard LLVM debug info. (default + in -O1+) + -g1 Preserve (do not minify) whitespace + -g2 Preserve function names + -g3 Preserve variable names + -g4 Preserve LLVM debug info (if -g was + used when compiling the C/C++ sources) + and show line number debug comments. + This is the highest level of debuggability. + (default in -O0) --typed-arrays <mode> 0: No typed arrays 1: Parallel typed arrays @@ -303,12 +318,7 @@ Options that are modified or new in %s include: archive, which is given the same name as the output HTML but with suffix .data.compress - --minify <on> 0: Do not minify the generated JavaScript's - whitespace (default in -O0, -O1, or if - -g is used) - 1: Minify the generated JavaScript's - whitespace (default in -O2+, assuming - -g is not used) + --minify 0 Identical to -g1 --split <size> Splits the resulting javascript file into pieces to ease debugging. This option only works if @@ -689,13 +699,13 @@ try: newargs = sys.argv[1:] opt_level = 0 + debug_level = 0 llvm_opts = None llvm_lto = None closure = None js_transform = None pre_js = '' post_js = '' - minify_whitespace = None split_js_file = None preload_files = [] embed_files = [] @@ -703,8 +713,6 @@ try: ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] - keep_llvm_debug = False - keep_js_debug = False bind = False jcache = False save_bc = False @@ -769,7 +777,7 @@ try: newargs[i+1] = '' elif newargs[i].startswith('--minify'): check_bad_eq(newargs[i]) - minify_whitespace = int(newargs[i+1]) + debug_level = max(1, debug_level) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--split'): @@ -777,9 +785,14 @@ try: split_js_file = int(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' - elif newargs[i] == '-g': - keep_llvm_debug = True - keep_js_debug = True + elif newargs[i].startswith('-g'): + requested_level = newargs[i][2:] or '3' + try: + debug_level = int(requested_level) + assert 0 <= debug_level <= 4 + except: + raise Exception('Invalid debug level: ' + newargs[i]) + newargs[i] = '-g' # discard level for clang args elif newargs[i] == '--bind': bind = True newargs[i] = '' @@ -868,8 +881,7 @@ try: if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] if llvm_lto is None: llvm_lto = opt_level >= 3 - if opt_level <= 0: keep_llvm_debug = keep_js_debug = True # always keep debug in -O0 - if opt_level > 0: keep_llvm_debug = False # JS optimizer wipes out llvm debug info from being visible + if opt_level == 0: debug_level = 4 if closure is None and opt_level == 3: closure = True if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state @@ -1019,15 +1031,12 @@ try: logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types') if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: - keep_llvm_debug = True # must keep debug info to do line-by-line operations + debug_level = 4 # must keep debug info to do line-by-line operations - if (keep_llvm_debug or keep_js_debug) and closure: + if debug_level > 0 and closure: logging.warning('disabling closure because debug info was requested') closure = False - if minify_whitespace is None: - minify_whitespace = opt_level >= 2 and not keep_js_debug - assert shared.LLVM_TARGET in shared.COMPILER_OPTS if shared.LLVM_TARGET == 'i386-pc-linux-gnu': shared.Settings.TARGET_X86 = 1 @@ -1054,7 +1063,7 @@ try: output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') temp_files.append(output_file) args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file] - logging.debug("running:" + call + ' '.join(args)) + logging.debug("running:" + call + ' ' + ' '.join(args)) execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that) if not os.path.exists(output_file): logging.error('compiler frontend failed to generate LLVM bitcode, halting') @@ -1132,6 +1141,8 @@ try: libcxx_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcxx', 'symbols'), exclude=libc_symbols) libcxxabi_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols'), exclude=libc_symbols) + # XXX we should disable EMCC_DEBUG (and EMCC_OPTIMIZE_NORMALLY?) when building libs, just like in the relooper + def build_libc(lib_filename, files): o_s = [] prev_cxx = os.environ.get('EMMAKEN_CXX') @@ -1352,7 +1363,7 @@ try: for haz in has: # remove symbols that are supplied by another of the inputs if haz in need: need.remove(haz) - logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has))) + if shared.Settings.VERBOSE: logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has))) if (force or len(need) > 0) and apply_(need): # We need to build and link the library in logging.debug('including %s' % name) @@ -1396,7 +1407,7 @@ try: # Optimize, if asked to if not LEAVE_INPUTS_RAW: - link_opts = [] if keep_llvm_debug else ['-strip-debug'] # remove LLVM debug info in -O1+, since the optimizer removes it anyhow + link_opts = [] if debug_level >= 4 else ['-strip-debug'] # remove LLVM debug if we are not asked for it if llvm_opts > 0: if not os.environ.get('EMCC_OPTIMIZE_NORMALLY'): shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts) @@ -1538,12 +1549,12 @@ try: logging.debug('running post-closure post-opts') js_optimizer_queue += ['simplifyExpressionsPost'] - if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and not keep_js_debug: - # do this if closure is not enabled (it gives similar speedups), and we do not need to keep debug info around + if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: js_optimizer_queue += ['registerize'] - if minify_whitespace: - js_optimizer_queue += ['compress'] + if opt_level > 0: + if debug_level < 2 and shared.Settings.ASM_JS and shared.Settings.RELOOP: js_optimizer_queue += ['minifyGlobals'] + if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] if closure and shared.Settings.ASM_JS: js_optimizer_queue += ['closure'] diff --git a/emscripten.py b/emscripten.py index c9a5eb59..56f59273 100755 --- a/emscripten.py +++ b/emscripten.py @@ -407,7 +407,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, math_envs = ['Math.min'] # TODO: move min to maths asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs]) - basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat', 'copyTempDouble', 'copyTempFloat'] + [m.replace('.', '_') for m in math_envs] + basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR'] if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] @@ -555,6 +555,24 @@ var asm = (function(global, env, buffer) { threwValue = value; } } + function copyTempFloat(ptr) { + ptr = ptr|0; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; + HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; + HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; + } + function copyTempDouble(ptr) { + ptr = ptr|0; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; + HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; + HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; + HEAP8[tempDoublePtr+4|0] = HEAP8[ptr+4|0]; + HEAP8[tempDoublePtr+5|0] = HEAP8[ptr+5|0]; + HEAP8[tempDoublePtr+6|0] = HEAP8[ptr+6|0]; + HEAP8[tempDoublePtr+7|0] = HEAP8[ptr+7|0]; + } ''' + ''.join([''' function setTempRet%d(value) { value = value|0; diff --git a/src/fastLong.js b/src/fastLong.js index d1ce5d39..4f6efd9f 100644 --- a/src/fastLong.js +++ b/src/fastLong.js @@ -5,12 +5,12 @@ function ___muldsi3($a, $b) { var $1 = 0, $2 = 0, $3 = 0, $6 = 0, $8 = 0, $11 = 0, $12 = 0; $1 = $a & 65535; $2 = $b & 65535; - $3 = Math.imul($2, $1); + $3 = Math.imul($2, $1) | 0; $6 = $a >>> 16; - $8 = ($3 >>> 16) + Math.imul($2, $6) | 0; + $8 = ($3 >>> 16) + (Math.imul($2, $6) | 0) | 0; $11 = $b >>> 16; - $12 = Math.imul($11, $1); - return (tempRet0 = (($8 >>> 16) + Math.imul($11, $6) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; + $12 = Math.imul($11, $1) | 0; + return (tempRet0 = (($8 >>> 16) + (Math.imul($11, $6) | 0) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; } function ___divdi3($a$0, $a$1, $b$0, $b$1) { $a$0 = $a$0 | 0; @@ -47,7 +47,7 @@ function ___remdi3($a$0, $a$1, $b$0, $b$1) { $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1) | 0; $4$1 = tempRet0; $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1) | 0; - ___udivmoddi4($4$0, $4$1, $6$0, tempRet0, $rem); + ___udivmoddi4($4$0, $4$1, $6$0, tempRet0, $rem) | 0; $10$0 = _i64Subtract(HEAP32[$rem >> 2] ^ $1$0, HEAP32[$rem + 4 >> 2] ^ $1$1, $1$0, $1$1) | 0; $10$1 = tempRet0; STACKTOP = __stackBase__; @@ -63,8 +63,8 @@ function ___muldi3($a$0, $a$1, $b$0, $b$1) { $y_sroa_0_0_extract_trunc = $b$0; $1$0 = ___muldsi3($x_sroa_0_0_extract_trunc, $y_sroa_0_0_extract_trunc) | 0; $1$1 = tempRet0; - $2 = Math.imul($a$1, $y_sroa_0_0_extract_trunc); - return (tempRet0 = (Math.imul($b$1, $x_sroa_0_0_extract_trunc) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; + $2 = Math.imul($a$1, $y_sroa_0_0_extract_trunc) | 0; + return (tempRet0 = ((Math.imul($b$1, $x_sroa_0_0_extract_trunc) | 0) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; } function ___udivdi3($a$0, $a$1, $b$0, $b$1) { $a$0 = $a$0 | 0; @@ -84,7 +84,7 @@ function ___uremdi3($a$0, $a$1, $b$0, $b$1) { __stackBase__ = STACKTOP; STACKTOP = STACKTOP + 8 | 0; $rem = __stackBase__ | 0; - ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem); + ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) | 0; STACKTOP = __stackBase__; return (tempRet0 = HEAP32[$rem + 4 >> 2] | 0, HEAP32[$rem >> 2] | 0) | 0; } @@ -258,7 +258,7 @@ function ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) { $149 = $carry_0203 | $q_sroa_0_1199 << 1; $r_sroa_0_0_insert_insert42$0 = 0 | ($r_sroa_0_1201 << 1 | $q_sroa_1_1198 >>> 31); $r_sroa_0_0_insert_insert42$1 = $r_sroa_0_1201 >>> 31 | $r_sroa_1_1200 << 1 | 0; - _i64Subtract($137$0, $137$1, $r_sroa_0_0_insert_insert42$0, $r_sroa_0_0_insert_insert42$1); + _i64Subtract($137$0, $137$1, $r_sroa_0_0_insert_insert42$0, $r_sroa_0_0_insert_insert42$1) | 0; $150$1 = tempRet0; $151$0 = $150$1 >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1; $152 = $151$0 & 1; diff --git a/src/jsifier.js b/src/jsifier.js index 88b9d9f6..062bae6c 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -14,6 +14,8 @@ var asmLibraryFunctions = []; var SETJMP_LABEL = -1; +var INDENTATION = ' '; + // JSifier function JSify(data, functionsOnly, givenFunctions) { var mainPass = !functionsOnly; @@ -591,13 +593,13 @@ function JSify(data, functionsOnly, givenFunctions) { func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; if (PGO) { - func.JS += ' PGOMonitor.called["' + func.ident + '"] = 1;\n'; + func.JS += INDENTATION + 'PGOMonitor.called["' + func.ident + '"] = 1;\n'; } if (ASM_JS) { // spell out argument types func.params.forEach(function(param) { - func.JS += ' ' + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n'; + func.JS += INDENTATION + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n'; }); // spell out local variables @@ -611,7 +613,7 @@ function JSify(data, functionsOnly, givenFunctions) { i += chunkSize; } for (i = 0; i < chunks.length; i++) { - func.JS += ' var ' + chunks[i].map(function(v) { + func.JS += INDENTATION + 'var ' + chunks[i].map(function(v) { var type = getImplementationType(v); if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl); @@ -627,7 +629,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (true) { // TODO: optimize away when not needed if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; - func.JS += ' var label = 0;\n'; + func.JS += INDENTATION + 'var label = 0;\n'; } if (ASM_JS) { @@ -636,12 +638,12 @@ function JSify(data, functionsOnly, givenFunctions) { hasByVal = hasByVal || param.byVal; }); if (hasByVal) { - func.JS += ' var tempParam = 0;\n'; + func.JS += INDENTATION + 'var tempParam = 0;\n'; } } // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up. - func.JS += ' ' + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n'; + func.JS += INDENTATION + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n'; // Make copies of by-value params // XXX It is not clear we actually need this. While without this we fail, it does look like @@ -654,7 +656,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (param.byVal) { var type = removePointing(param.type); var typeInfo = Types.types[type]; - func.JS += ' ' + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + + func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n'; } }); @@ -728,12 +730,12 @@ function JSify(data, functionsOnly, givenFunctions) { } ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n'; ret += block.labels.map(function(label) { - return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' - + getLabelLines(label, indent + ' '); + return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' + + getLabelLines(label, indent + INDENTATION + INDENTATION); }).join('\n') + '\n'; if (func.setjmpTable && ASM_JS) { // emit a label in which we write to the proper local variable, before jumping to the actual label - ret += ' case ' + SETJMP_LABEL + ': '; + ret += INDENTATION + 'case ' + SETJMP_LABEL + ': '; ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n'; }).join(' else '); @@ -741,7 +743,7 @@ function JSify(data, functionsOnly, givenFunctions) { ret += '__THREW__ = threwValue = 0;\n'; ret += 'break;\n'; } - if (ASSERTIONS) ret += indent + ' default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n'; + if (ASSERTIONS) ret += indent + INDENTATION + 'default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n'; ret += indent + '}\n'; if (func.setjmpTable && !ASM_JS) { ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; @@ -797,13 +799,13 @@ function JSify(data, functionsOnly, givenFunctions) { } return ret; } - func.JS += walkBlock(func.block, ' '); + func.JS += walkBlock(func.block, INDENTATION); // Finalize function if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n"; // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts()) if (RELOOP && func.lines.length > 0 && func.returnType != 'void') { var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length; - if (returns == 0) func.JS += ' return ' + asmCoercion('0', func.returnType); + if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType); } func.JS += '}\n'; @@ -1121,7 +1123,7 @@ function JSify(data, functionsOnly, givenFunctions) { ret += value + '{\n'; } var phiSet = getPhiSetsForLabel(phiSets, targetLabel); - ret += ' ' + phiSet + makeBranch(targetLabel, item.currLabelId || null) + '\n'; + ret += INDENTATION + '' + phiSet + makeBranch(targetLabel, item.currLabelId || null) + '\n'; ret += '}\n'; if (RELOOP) { item.groupedLabels.push({ @@ -1185,8 +1187,13 @@ function JSify(data, functionsOnly, givenFunctions) { if (disabled) { ret = call_ + ';'; } else if (ASM_JS) { + if (item.type != 'void') call_ = asmCoercion(call_, item.type); // ensure coercion to ffi in comma operator call_ = call_.replace('; return', ''); // we auto-add returns when aborting, but do not need them here - ret = '(__THREW__ = 0,' + call_ + ');'; + if (item.type == 'void') { + ret = '__THREW__ = 0;' + call_ + ';'; + } else { + ret = '(__THREW__ = 0,' + call_ + ');'; + } } else { ret = '(function() { try { __THREW__ = 0; return ' + call_ + ' ' diff --git a/src/library.js b/src/library.js index 33dcbd5f..01a67804 100644 --- a/src/library.js +++ b/src/library.js @@ -4399,7 +4399,7 @@ LibraryManager.library = { {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; } } else { - _memcpy(dest, src, num); + _memcpy(dest, src, num) | 0; } }, llvm_memmove_i32: 'memmove', diff --git a/src/modules.js b/src/modules.js index b13ab3c5..d26acbd5 100644 --- a/src/modules.js +++ b/src/modules.js @@ -114,7 +114,8 @@ var Debugging = { m = metadataToParentMetadata[m]; assert(m, 'Confused as to parent metadata for llvm #' + l + ', metadata !' + m); } - this.llvmLineToSourceFile[l] = metadataToFilename[m]; + // Normalize Windows path slashes coming from LLVM metadata, so that forward slashes can be assumed as path delimiters. + this.llvmLineToSourceFile[l] = metadataToFilename[m].replace(/\\5C/g, '/'); } this.on = true; diff --git a/src/parseTools.js b/src/parseTools.js index 687faaa8..0b83a12b 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -710,9 +710,9 @@ function splitI64(value, floatConversion) { var lowInput = legalizedI64s ? value : 'VALUE'; if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput); if (legalizedI64s) { - return [lowInput + '>>>0', 'Math.min(Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0']; + return [lowInput + '>>>0', asmCoercion('Math.min(' + asmCoercion('Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0']; } else { - return makeInlineCalculation(makeI64(lowI |