diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 19 | ||||
-rw-r--r-- | src/corruptionCheck.js | 6 | ||||
-rw-r--r-- | src/intertyper.js | 12 | ||||
-rw-r--r-- | src/jsifier.js | 50 | ||||
-rw-r--r-- | src/library.js | 76 | ||||
-rw-r--r-- | src/library_gl.js | 2 | ||||
-rw-r--r-- | src/modules.js | 2 | ||||
-rw-r--r-- | src/parseTools.js | 133 | ||||
-rw-r--r-- | src/preamble.js | 6 | ||||
-rw-r--r-- | src/runtime.js | 32 | ||||
-rw-r--r-- | src/settings.js | 6 |
11 files changed, 255 insertions, 89 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 7fbdf24d..03d44cb7 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -469,6 +469,23 @@ function analyzer(data, sidePass) { i++; continue; // special case, handled in makeComparison } + case 'va_arg': { + assert(value.type == 'i64'); + assert(value.value.type == 'i32*', value.value.type); + i += removeAndAdd(label.lines, i, range(2).map(function(x) { + return { + intertype: 'va_arg', + assignTo: value.assignTo + '$' + x, + type: 'i32', + value: { + intertype: 'value', + ident: value.value.ident, // We read twice from the same i32* var, incrementing // + '$' + x, + type: 'i32*' + } + }; + })); + continue; + } case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue, // but in theory they can run on packed structs too (see use getStructuralTypePartBits) // potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general @@ -1492,7 +1509,7 @@ function analyzer(data, sidePass) { calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0; if (USE_TYPED_ARRAYS === 2) { // We need to keep the stack aligned - item.allocatedSize = Runtime.forceAlign(item.allocatedSize, QUANTUM_SIZE); + item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN); } } var index = 0; diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js index 315f5cf0..8b37120a 100644 --- a/src/corruptionCheck.js +++ b/src/corruptionCheck.js @@ -42,7 +42,7 @@ var CorruptionChecker = { CorruptionChecker.checkAll(); var size = CorruptionChecker.ptrs[ptr]; //Module.printErr('free ' + ptr + ' of size ' + size); - assert(size); + assert(size, ptr); var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; //Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation); delete CorruptionChecker.ptrs[ptr]; @@ -67,12 +67,12 @@ var CorruptionChecker = { }, fillBuffer: function(buffer, size) { for (var x = buffer; x < buffer + size; x++) { - {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}}; + {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8', null, null, null, 1) }}}; } }, checkBuffer: function(buffer, size) { for (var x = buffer; x < buffer + size; x++) { - if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) { + if (({{{ makeGetValue('x', 0, 'i8', null, null, null, null, 1) }}}&255) != CorruptionChecker.canary(x)) { assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]); } } diff --git a/src/intertyper.js b/src/intertyper.js index 57e3011d..445c37f4 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -336,6 +336,8 @@ function intertyper(data, sidePass, baseLineNums) { return 'InsertValue'; if (tokensLength >= 3 && token0Text == 'phi') return 'Phi'; + if (tokensLength >= 3 && token0Text == 'va_arg') + return 'va_arg'; if (tokensLength >= 3 && token0Text == 'landingpad') return 'Landingpad'; if (token0Text == 'fence') @@ -817,6 +819,16 @@ function intertyper(data, sidePass, baseLineNums) { this.forwardItem(item, 'Reintegrator'); } }); + // 'phi' + substrate.addActor('va_arg', { + processItem: function(item) { + item.intertype = 'va_arg'; + var segments = splitTokenList(item.tokens.slice(1)); + item.type = segments[1][0].text; + item.value = parseLLVMSegment(segments[0]); + this.forwardItem(item, 'Reintegrator'); + } + }); // mathops substrate.addActor('Mathops', { processItem: function(item) { diff --git a/src/jsifier.js b/src/jsifier.js index aab21eea..9207f65d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -485,8 +485,10 @@ function JSify(data, functionsOnly, givenFunctions) { // Shared libraries reuse the runtime of their parents. item.JS = ''; } else { - if (!LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { - if (ASSERTIONS) printErr('warning: missing function: ' + shortident); + // If this is not linkable, anything not in the library is definitely missing + if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { + if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident); + if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident); LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); } item.JS = addFromLibrary(shortident); @@ -1223,7 +1225,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (DISABLE_EXCEPTION_CATCHING && USE_TYPED_ARRAYS == 2) { ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;'; item.assignTo = null; - if (ASSERTIONS) warnOnce('landingpad, but exceptions are disabled!'); + if (VERBOSE) warnOnce('landingpad, but exceptions are disabled!'); return ret; } var catchTypeArray = item.catchables.map(finalizeLLVMParameter).map(function(element) { return asmCoercion(element, 'i32') }).join(','); @@ -1293,6 +1295,14 @@ function JSify(data, functionsOnly, givenFunctions) { return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum)); } }); + makeFuncLineActor('va_arg', function(item) { + assert(TARGET_LE32); + var ident = item.value.ident; + var move = Runtime.STACK_ALIGN; + return '(tempInt=' + makeGetValue(ident, 4, '*') + ',' + + makeSetValue(ident, 4, 'tempInt + ' + move, '*') + ',' + + makeGetValue(makeGetValue(ident, 0, '*'), 'tempInt', item.type) + ')'; + }); makeFuncLineActor('mathop', processMathop); @@ -1316,16 +1326,21 @@ function JSify(data, functionsOnly, givenFunctions) { ident = Variables.resolveAliasToIdent(ident); var shortident = ident.slice(1); var simpleIdent = shortident; - var callIdent = LibraryManager.getRootIdent(simpleIdent); - if (callIdent) { - simpleIdent = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is - if (callIdent.indexOf('.') < 0) { - callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix - } + if (isLocalVar(ident)) { + var callIdent = ident; } else { - callIdent = ident; + // Not a local var, check if in library + var callIdent = LibraryManager.getRootIdent(simpleIdent); + if (callIdent) { + simpleIdent = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is + if (callIdent.indexOf('.') < 0) { + callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix + } + } else { + callIdent = ident; + } + if (callIdent == '0') return 'abort(-2)'; } - if (callIdent == '0') return 'abort(-2)'; var args = []; var argsTypes = []; @@ -1356,6 +1371,7 @@ function JSify(data, functionsOnly, givenFunctions) { } else { size = Runtime.getNativeFieldSize(param.type); } + size = Runtime.alignMemory(size, Runtime.STACK_ALIGN); varargs.push(val); varargs = varargs.concat(zeros(size-1)); // TODO: replace concats like this with push @@ -1389,15 +1405,14 @@ function JSify(data, functionsOnly, givenFunctions) { var type = varargsTypes[i]; if (type == 0) return null; var ret; + assert(offset % Runtime.STACK_ALIGN == 0); // varargs must be aligned if (!varargsByVals[i]) { ret = makeSetValue(getFastValue('tempInt', '+', offset), 0, arg, type, null, null, QUANTUM_SIZE, null, ','); - offset += Runtime.getNativeFieldSize(type); + offset += Runtime.alignMemory(Runtime.getNativeFieldSize(type), Runtime.STACK_ALIGN); } else { - assert(offset % 4 == 0); // varargs must be aligned var size = calcAllocatedSize(removeAllPointing(type)); - assert(size % 4 == 0); // varargs must stay aligned ret = makeCopyValues(getFastValue('tempInt', '+', offset), arg, size, null, null, varargsByVals[i], ','); - offset += size; + offset += Runtime.forceAlign(size, Runtime.STACK_ALIGN); } return ret; }).filter(function(arg) { @@ -1459,7 +1474,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && simpleIdent in Functions.libraryFunctions) { ret = asmCoercion(ret, returnType); if (simpleIdent == 'abort' && funcData.returnType != 'void') { - ret += '; return 0'; // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return + ret += '; return ' + asmCoercion('0', funcData.returnType); // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return } } @@ -1583,10 +1598,11 @@ function JSify(data, functionsOnly, givenFunctions) { sortGlobals(globalsData.globalVariables).forEach(function(g) { var ident = g.ident; if (!isIndexableGlobal(ident)) return; + assert(Variables.nextIndexedOffset % Runtime.STACK_ALIGN == 0); Variables.indexedGlobals[ident] = Variables.nextIndexedOffset; Variables.nextIndexedOffset += Runtime.alignMemory(calcAllocatedSize(Variables.globals[ident].type)); if (ident.substr(0, 5) == '__ZTV') { // leave room for null-terminating the vtable - Variables.nextIndexedOffset += Runtime.getNativeTypeSize('i32'); + Variables.nextIndexedOffset += Runtime.alignMemory(QUANTUM_SIZE); } }); } diff --git a/src/library.js b/src/library.js index 957f69bd..ade63d45 100644 --- a/src/library.js +++ b/src/library.js @@ -2499,7 +2499,7 @@ LibraryManager.library = { for (var formatIndex = 0; formatIndex < format.length;) { if (format[formatIndex] === '%' && format[formatIndex+1] == 'n') { var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getNativeFieldSize('void*'); + argIndex += Runtime.getAlignSize('void*', null, true); {{{ makeSetValue('argPtr', 0, 'soFar', 'i32') }}}; formatIndex += 2; continue; @@ -2508,7 +2508,7 @@ LibraryManager.library = { // TODO: Support strings like "%5c" etc. if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getNativeFieldSize('void*'); + argIndex += Runtime.getAlignSize('void*', null, true); fields++; next = get(); {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} @@ -2600,7 +2600,7 @@ LibraryManager.library = { var text = buffer.join(''); var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getNativeFieldSize('void*'); + argIndex += Runtime.getAlignSize('void*', null, true); switch (type) { case 'd': case 'u': case 'i': if (half) { @@ -2669,8 +2669,16 @@ LibraryManager.library = { ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}}; #if USE_TYPED_ARRAYS == 2 } else if (type == 'i64') { + +#if TARGET_LE32 + ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, + {{{ makeGetValue('varargs', 'argIndex+8', 'i32', undefined, undefined, true) }}}]; + argIndex += {{{ STACK_ALIGN }}}; // each 32-bit chunk is in a 64-bit block +#else ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, {{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true) }}}]; +#endif + #else } else if (type == 'i64') { ret = {{{ makeGetValue('varargs', 'argIndex', 'i64', undefined, undefined, true) }}}; @@ -2679,7 +2687,7 @@ LibraryManager.library = { type = 'i32'; // varargs are always i32, i64, or double ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}; } - argIndex += Runtime.getNativeFieldSize(type); + argIndex += Math.max(Runtime.getNativeFieldSize(type), Runtime.getAlignSize(type, null, true)); return ret; } @@ -3612,6 +3620,9 @@ LibraryManager.library = { asprintf: function(s, format, varargs) { return _sprintf(-s, format, varargs); }, + +#if TARGET_X86 + // va_arg is just like our varargs vfprintf: 'fprintf', vsnprintf: 'snprintf', vprintf: 'printf', @@ -3620,6 +3631,44 @@ LibraryManager.library = { vscanf: 'scanf', vfscanf: 'fscanf', vsscanf: 'sscanf', +#endif + +#if TARGET_LE32 + // convert va_arg into varargs + vfprintf__deps: ['fprintf'], + vfprintf: function(s, f, va_arg) { + return _fprintf(s, f, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vsnprintf__deps: ['snprintf'], + vsnprintf: function(s, n, format, va_arg) { + return _snprintf(s, n, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vprintf__deps: ['printf'], + vprintf: function(format, va_arg) { + return _printf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vsprintf__deps: ['sprintf'], + vsprintf: function(s, format, va_arg) { + return _sprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vasprintf__deps: ['asprintf'], + vasprintf: function(s, format, va_arg) { + return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vscanf__deps: ['scanf'], + vscanf: function(format, va_arg) { + return _scanf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vfscanf__deps: ['fscanf'], + vfscanf: function(s, format, va_arg) { + return _fscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, + vsscanf__deps: ['sscanf'], + vsscanf: function(s, format, va_arg) { + return _sscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, +#endif + fopen64: 'fopen', __01fopen64_: 'fopen', __01freopen64_: 'freopen', @@ -4825,7 +4874,13 @@ LibraryManager.library = { llvm_va_start__inline: function(ptr) { // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp' +#if TARGET_X86 return makeSetValue(ptr, 0, 'varrp', 'void*'); +#endif +#if TARGET_LE32 + // 4-word structure: start, current offset + return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, 4, 0, 'void*'); +#endif }, llvm_va_end: function() {}, @@ -7487,12 +7542,13 @@ LibraryManager.library = { } var i = 0; do { - var curr = {{{ makeGetValue('varargs', 'i*4', 'i8') }}}; + var curr = {{{ makeGetValue('varargs', '0', 'i8') }}}; + varargs += {{{ STACK_ALIGN }}}; {{{ makeSetValue('_emscripten_jcache_printf_.buffer', 'i', 'curr', 'i8') }}}; i++; - assert(i*4 < MAX); + assert(i*{{{ STACK_ALIGN }}} < MAX); } while (curr != 0); - Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs + i*4)).replace('\\n', '')); + Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs)).replace('\\n', '')); Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free }, @@ -7522,8 +7578,10 @@ LibraryManager.library = { l = (a + c)>>>0; h = (b + d)>>>0; overflow = ((h>>>0) < (b>>>0))|0; // Return whether addition overflowed even the high word. - h = (h + (((l>>>0) < (a>>>0))|0))>>>0; // Add carry from low word to high word on overflow. - overflow = overflow | (!h); // Check again for overflow. + if ((l>>>0) < (a>>>0)) { + h = (h + 1)>>>0; // Add carry from low word to high word on overflow. + overflow = overflow | (!h); // Check again for overflow. + } {{{ makeStructuralReturn(['l|0', 'h', 'overflow'], true) }}}; }, diff --git a/src/library_gl.js b/src/library_gl.js index 8923435f..33aadb4b 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1039,7 +1039,7 @@ var LibraryGL = { glGetShaderSource: function(shader, bufSize, length, source) { var result = Module.ctx.getShaderSource(GL.shaders[shader]); - result.slice(0, bufSize - 1); + result = result.slice(0, bufSize - 1); writeStringToMemory(result, source); if (length) { {{{ makeSetValue('length', '0', 'result.length', 'i32') }}}; diff --git a/src/modules.js b/src/modules.js index ce162ac1..9cbe88aa 100644 --- a/src/modules.js +++ b/src/modules.js @@ -2,6 +2,8 @@ // Various namespace-like modules +var STACK_ALIGN = TARGET_X86 ? 4 : 8; + var LLVM = { LINKAGES: set('private', 'linker_private', 'linker_private_weak', 'linker_private_weak_def_auto', 'internal', 'available_externally', 'linkonce', 'common', 'weak', 'appending', 'extern_weak', 'linkonce_odr', diff --git a/src/parseTools.js b/src/parseTools.js index f4ce12d5..39de4b7c 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -109,6 +109,10 @@ function isJSVar(ident) { } +function isLocalVar(ident) { + return ident[0] == '$'; +} + function isStructPointerType(type) { // This test is necessary for clang - in llvm-gcc, we // could check for %struct. The downside is that %1 can @@ -160,7 +164,8 @@ function isIntImplemented(type) { } // Note: works for iX types and structure types, not pointers (even though they are implemented as ints) -function getBits(type) { +function getBits(type, allowPointers) { + if (allowPointers && isPointerType(type)) return 32; if (!type) return 0; if (type[0] == 'i') { var left = type.substr(1); @@ -178,6 +183,17 @@ function getBits(type) { return 0; } +function getNumIntChunks(type) { + return Math.ceil(getBits(type, true)/32); +} + +function isIdenticallyImplemented(type1, type2) { + var floats = +(type1 in Runtime.FLOAT_TYPES) + +(type2 in Runtime.FLOAT_TYPES); + if (floats == 2) return true; + if (floats == 1) return false; + return getNumIntChunks(type1) == getNumIntChunks(type2); +} + function isIllegalType(type) { var bits = getBits(type); return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits)); @@ -229,7 +245,21 @@ function isFunctionType(type, out) { returnType = 'i8*'; // some pointer type, no point in analyzing further } if (out) out.returnType = returnType; - var argText = type.substr(lastOpen); + // find ( that starts the arguments + var depth = 0, i = type.length-1, argText = null; + while (i >= 0) { + var curr = type[i]; + if (curr == ')') depth++; + else if (curr == '(') { + depth--; + if (depth == 0) { + argText = type.substr(i); + break; + } + } + i--; + } + assert(argText); return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2), true) }, out); } @@ -256,8 +286,8 @@ function isVarArgsFunctionType(type) { return type.substr(-varArgsSuffix.length) == varArgsSuffix; } -function countNormalArgs(type) { - var out = {}; +function countNormalArgs(type, out) { + out = out || {}; if (!isFunctionType(type, out)) return -1; if (isVarArgsFunctionType(type)) out.numArgs--; return out.numArgs; @@ -571,7 +601,9 @@ function parseLLVMFunctionCall(segment) { function eatLLVMIdent(tokens) { var ret; if (tokens[0].text in PARSABLE_LLVM_FUNCTIONS) { - ret = parseLLVMFunctionCall([{text: 'i0'}].concat(tokens.slice(0,2))).ident; // TODO: Handle more cases, return a full object, process it later + var item = parseLLVMFunctionCall([{text: '?'}].concat(tokens.slice(0,2))); // TODO: Handle more cases, return a full object, process it later + if (item.intertype == 'bitcast') checkBitcast(item); + ret = item.ident; tokens.shift(); tokens.shift(); } else { @@ -992,7 +1024,9 @@ function getHeapOffset(offset, type, forceAsm) { } if (Runtime.getNativeFieldSize(type) > 4) { - type = 'i32'; // XXX we emulate 64-bit values as 32 + if (type == 'i64' || TARGET_X86) { + type = 'i32'; // XXX we emulate 64-bit values as 32 in x86, and also in le32 but only i64, not double + } } var sz = Runtime.getNativeTypeSize(type); @@ -1093,7 +1127,9 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa return '{ ' + ret.join(', ') + ' }'; } - if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') { + // In double mode 1, in x86 we always assume unaligned because we can't trust that; otherwise in le32 + // we need this code path if we are not fully aligned. + if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double' && (TARGET_X86 || align < 8)) { return '(' + makeSetTempDouble(0, 'i32', makeGetValue(ptr, pos, 'i32', noNeedFirst, unsigned, ignore, align)) + ',' + makeSetTempDouble(1, 'i32', makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, unsigned, ignore, align)) + ',' + makeGetTempDouble(0, 'double') + ')'; @@ -1102,6 +1138,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa if (USE_TYPED_ARRAYS == 2 && align) { // Alignment is important here. May need to split this up var bytes = Runtime.getNativeTypeSize(type); + if (DOUBLE_MODE == 0 && type == 'double') bytes = 4; // we will really only read 4 bytes here if (bytes > align) { var ret = '('; if (isIntImplemented(type)) { @@ -1109,7 +1146,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa // Special case that we can optimize ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '|' + '(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)'; - } else { // XXX we cannot truly handle > 4... + } else { // XXX we cannot truly handle > 4... (in x86) ret = ''; for (var i = 0; i < bytes; i++) { ret += '(' + makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore) + (i > 0 ? '<<' + (8*i) : '') + ')'; @@ -1198,6 +1235,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, return ret.join('; '); } + // TODO: optimize like get for le32 if (DOUBLE_MODE == 1 && USE_TYPED_ARRAYS == 2 && type == 'double') { return '(' + makeSetTempDouble(0, 'double', value) + ',' + makeSetValue(ptr, pos, makeGetTempDouble(0, 'i32'), 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' + @@ -1609,7 +1647,11 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) { case 'i1': case 'i8': return [unsigned ? 'HEAPU8' : 'HEAP8']; break; case 'i16': return [unsigned ? 'HEAPU16' : 'HEAP16']; break; case 'i32': case 'i64': return [unsigned ? 'HEAPU32' : 'HEAP32']; break; - case 'float': case 'double': return ['HEAPF32']; break; + case 'double': { + if (TARGET_LE32) return ['HEAPF64']; // in le32, we do have the ability to assume 64-bit alignment + // otherwise, fall through to float + } + case 'float': return ['HEAPF32']; default: { throw 'what, exactly, can we do for unknown types in TA2?! ' + new Error().stack; } @@ -1618,45 +1660,48 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) { return []; } -function finalizeLLVMFunctionCall(item, noIndexizeFunctions) { - if (item.intertype == 'getelementptr') { // TODO finalizeLLVMParameter on the ident and the indexes? - return makePointer(makeGetSlabs(item.ident, item.type)[0], getGetElementPtrIndexes(item), null, item.type); - } - if (item.intertype == 'bitcast') { - // Warn about some types of casts, then fall through to the handling code below - var oldType = item.params[0].type; - var newType = item.type; - if (isPossiblyFunctionType(oldType) && isPossiblyFunctionType(newType)) { - var oldCount = countNormalArgs(oldType); - var newCount = countNormalArgs(newType); - if (oldCount != newCount && oldCount && newCount) { - warnOnce('Casting a function pointer type to another with a different number of arguments. See more info in the compiler source'); - if (VERBOSE) { - warnOnce('Casting a function pointer type to another with a different number of arguments: ' + oldType + ' vs. ' + newType + ', on ' + item.params[0].ident); +function checkBitcast(item) { + // Warn about some types of casts, then fall through to the handling code below + var oldType = item.params[0].type; + var newType = item.type; + if (isPossiblyFunctionType(oldType) && isPossiblyFunctionType(newType)) { + var oldInfo = {}, newInfo = {}; + var oldCount = countNormalArgs(oldType, oldInfo); + var newCount = countNormalArgs(newType, newInfo); + var warned = false; + function showWarning() { + if (warned) return; + warned = true; + if (VERBOSE || ASM_JS) { + warnOnce('Casting potentially incompatible function pointer ' + oldType + ' to ' + newType + ', for ' + item.params[0].ident.slice(1)); + } else { + warnOnce('Casting a function pointer type to a potentially incompatible one (use VERBOSE=1 to see more)'); + } + warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidlinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts'); + if (ASM_JS) warnOnce('Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these'); + } + if (oldCount != newCount && oldCount && newCount) showWarning(); + if (ASM_JS) { + if (oldCount != newCount) showWarning(); + else if (!isIdenticallyImplemented(oldInfo.returnType, newInfo.returnType)) { + showWarning(); + } else { + for (var i = 0; i < oldCount; i++) { + if (!isIdenticallyImplemented(oldInfo.segments[i][0].text, newInfo.segments[i][0].text)) { + showWarning(); + break; + } } - // This may be dangerous as clang generates different code for C and C++ calling conventions. The only problem - // case appears to be passing a structure by value, C will have (field1, field2) as function args, and the - // function will internally create a structure with that data, while C++ will have (struct* byVal) and it - // will create a copy before calling the function, then call it with a pointer to the copy. Mixing the two - // first of all leads to two copies being made, so this is a bad idea even regardless of Emscripten. But, - // what is a problem for Emscr ipten is that mixing these two calling conventions (say, calling a C one from - // C++) will then assume that (struct* byVal) is actually the same as (field1, field2). In native code, this - // is easily possible, you place the two fields on the stack and call the function (you know to place the - // values since there is 'byVal'). In Emscripten, though, this means we would need to always do one or the - // other of the two possibilities, for example, always passing by-value structs as (field1, field2). This - // would slow down everything, just to handle this corner case. (Which, just to point out how much of a - // corner case it is, does not appear to happen with nested structures!) - // - // The recommended solution for this problem is not to mix C and C++ calling conventions when passing structs - // by value. Either always pass structs by value within C code or C++ code, but not mixing the two by - // defining a function in one and calling it from the other (so, just changing .c to .cpp, or moving code - // from one file to another, would be enough to fix this), or, do not pass structs by value (which in general - // is inefficient, and worth avoiding if you can). - // - // Note that removing all arguments is acceptable, as a vast to void ()*. } } } +} + +function finalizeLLVMFunctionCall(item, noIndexizeFunctions) { + if (item.intertype == 'getelementptr') { // TODO finalizeLLVMParameter on the ident and the indexes? + return makePointer(makeGetSlabs(item.ident, item.type)[0], getGetElementPtrIndexes(item), null, item.type); + } + if (item.intertype == 'bitcast') checkBitcast(item); var temp = { op: item.intertype, variant: item.variant, diff --git a/src/preamble.js b/src/preamble.js index 92305ca0..cac0be4c 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -35,8 +35,8 @@ function SAFE_HEAP_CLEAR(dest) { var SAFE_HEAP_ERRORS = 0; var ACCEPTABLE_SAFE_HEAP_ERRORS = 0; -function SAFE_HEAP_ACCESS(dest, type, store, ignore) { - //if (dest === A_NUMBER) Module.print ([dest, type, store] + ' ' + new Error().stack); // Something like this may be useful, in debugging +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 assert(dest >= STACK_ROOT, 'segmentation fault: null pointer, or below normal memory'); @@ -97,7 +97,7 @@ function SAFE_HEAP_STORE(dest, value, type, ignore) { } //if (!ignore && (value === Infinity || value === -Infinity || isNaN(value))) throw [value, typeof value, new Error().stack]; - SAFE_HEAP_ACCESS(dest, type, true, ignore); + SAFE_HEAP_ACCESS(dest, type, true, ignore, value); if (dest in HEAP_WATCHED) { Module.print((new Error()).stack); throw "Bad store!" + dest; diff --git a/src/runtime.js b/src/runtime.js index 5269301c..9daab820 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -14,8 +14,8 @@ var RuntimeGenerator = { ret += sep + '_memset(' + type + 'TOP, 0, ' + size + ')'; } ret += sep + type + 'TOP = (' + type + 'TOP + ' + size + ')|0'; - if ({{{ QUANTUM_SIZE }}} > 1 && !ignoreAlign) { - ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ QUANTUM_SIZE }}}); + if ({{{ STACK_ALIGN }}} > 1 && !ignoreAlign) { + ret += sep + RuntimeGenerator.alignMemory(type + 'TOP', {{{ STACK_ALIGN }}}); } return ret; }, @@ -23,9 +23,7 @@ var RuntimeGenerator = { // An allocation that lives as long as the current function call stackAlloc: function(size, sep) { sep = sep || ';'; - if (USE_TYPED_ARRAYS === 2) 'STACKTOP = (STACKTOP + STACKTOP|0 % ' + ({{{ QUANTUM_SIZE }}} - (isNumber(size) ? Math.min(size, {{{ QUANTUM_SIZE }}}) : {{{ QUANTUM_SIZE }}})) + ')' + sep; - // The stack is always QUANTUM SIZE aligned, so we may not need to force alignment here - var ret = RuntimeGenerator.alloc(size, 'STACK', false, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ QUANTUM_SIZE }}} == 0)); + var ret = RuntimeGenerator.alloc(size, 'STACK', false, sep, USE_TYPED_ARRAYS != 2 || (isNumber(size) && parseInt(size) % {{{ STACK_ALIGN }}} == 0)); if (ASSERTIONS) { ret += sep + 'assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')'; } @@ -37,8 +35,8 @@ var RuntimeGenerator = { var ret = 'var __stackBase__ = ' + (ASM_JS ? '0; __stackBase__ = ' : '') + 'STACKTOP'; if (initial > 0) ret += '; STACKTOP = (STACKTOP + ' + initial + ')|0'; if (USE_TYPED_ARRAYS == 2) { - assert(initial % QUANTUM_SIZE == 0); - if (ASSERTIONS && QUANTUM_SIZE == 4) { + assert(initial % Runtime.STACK_ALIGN == 0); + if (ASSERTIONS && Runtime.STACK_ALIGN == 4) { ret += '; assert(' + asmCoercion('!(STACKTOP&3)', 'i32') + ')'; } } @@ -70,7 +68,7 @@ var RuntimeGenerator = { alignMemory: function(target, quantum) { if (typeof quantum !== 'number') { - quantum = '(quantum ? quantum : {{{ QUANTUM_SIZE }}})'; + quantum = '(quantum ? quantum : {{{ STACK_ALIGN }}})'; } return target + ' = ' + Runtime.forceAlign(target, quantum); }, @@ -175,6 +173,18 @@ var Runtime = { set: set, + STACK_ALIGN: {{{ STACK_ALIGN }}}, + + // type can be a native type or a struct (or null, for structs we only look at size here) + getAlignSize: function(type, size, vararg) { + // we align i64s and doubles on 64-bit boundaries, unlike x86 +#if TARGET_LE32 + if (type == 'i64' || type == 'double' || vararg) return 8; + if (!type) return Math.min(size, 8); // align structures internally to 64 bits +#endif + return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE); + }, + // Calculate aligned size, just like C structs should be. TODO: Consider // requesting that compilation be done with #pragma pack(push) /n #pragma pack(1), // which would remove much of the complexity here. @@ -187,10 +197,10 @@ var Runtime = { var size, alignSize; if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) { size = Runtime.getNativeTypeSize(field); // pack char; char; in structs, also char[X]s. - alignSize = size; + alignSize = Runtime.getAlignSize(field, size); } else if (Runtime.isStructType(field)) { size = Types.types[field].flatSize; - alignSize = Types.types[field].alignSize; + alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize); } else if (field[0] == 'b') { // bN, large number field, like a [N x i8] size = field.substr(1)|0; @@ -198,7 +208,7 @@ var Runtime = { } else { throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]); } - alignSize = type.packed ? 1 : Math.min(alignSize, Runtime.QUANTUM_SIZE); + if (type.packed) alignSize = 1; type.alignSize = Math.max(type.alignSize, alignSize); var curr = Runtime.alignMemory(type.flatSize, alignSize); // if necessary, place this on aligned memory type.flatSize = curr + size; diff --git a/src/settings.js b/src/settings.js index c878be92..2e4c2550 100644 --- a/src/settings.js +++ b/src/settings.js @@ -17,6 +17,9 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure. // // Changing this from the default of 4 is deprecated. +var TARGET_X86 = 1; // For le32-unknown-nacl +var TARGET_LE32 = 0; // For i386-pc-linux-gnu + var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values. // Decreases performance with additional runtime checks. Might not be // needed in some kinds of code. @@ -317,6 +320,9 @@ var WARN_ON_UNDEFINED_SYMBOLS = 0; // If set to 1, we will warn on any undefined // the existing buildsystem), and (2) functions might be // implemented l |