aboutsummaryrefslogtreecommitdiff
path: root/src/parseTools.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/parseTools.js')
-rw-r--r--src/parseTools.js133
1 files changed, 89 insertions, 44 deletions
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,