refactor assertions and parsing code
This commit is contained in:
Родитель
f89d9e2be6
Коммит
46e3b2a787
255
src/jsifier.js
255
src/jsifier.js
|
@ -385,7 +385,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
|
|||
first = false;
|
||||
});
|
||||
}
|
||||
if (GUARD_LABELS) {
|
||||
if (ASSERTIONS) {
|
||||
ret += indent + multipleIdent + 'else { throw "Bad multiple branching: " + __label__ + " : " + (new Error().stack); }\n';
|
||||
}
|
||||
if (!block.loopless) {
|
||||
|
@ -654,258 +654,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
|
|||
return makeOne(0);
|
||||
});
|
||||
|
||||
function makeSignOp(value, type, op) { // TODO: If value isNumber, do this at compile time
|
||||
if (!value) return value;
|
||||
if (!correctSigns() && !CHECK_SIGNS) return value;
|
||||
if (type in Runtime.INT_TYPES) {
|
||||
var bits = parseInt(type.substr(1));
|
||||
if (isNumber(value)) {
|
||||
// Sign/unsign constants at compile time
|
||||
return eval(op + 'Sign(' + value + ', ' + bits + ', 1)').toString();
|
||||
}
|
||||
// shortcuts for 32-bit case
|
||||
if (bits === 32 && !CHECK_SIGNS) {
|
||||
if (op === 're') {
|
||||
return '((' + value + ')|0)';
|
||||
} else {
|
||||
// TODO: figure out something here along the lines of return '(' + Math.pow(2, 32) + '+((' + value + ')|0))';
|
||||
}
|
||||
}
|
||||
return op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(correctSpecificSign()) + ')'; // If we are correcting a specific sign here, do not check for it
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function handleOverflow(text, bits) {
|
||||
if (!bits) return text;
|
||||
if (bits <= 32 && correctOverflows()) text = '(' + text + ')&' + (Math.pow(2, bits) - 1);
|
||||
if (!CHECK_OVERFLOWS || correctSpecificOverflow()) return text; // If we are correcting a specific overflow here, do not check for it
|
||||
return 'CHECK_OVERFLOW(' + text + ', ' + bits + ')';
|
||||
}
|
||||
|
||||
function makeRounding(value, bits, signed) {
|
||||
// C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
|
||||
// With 32 bits and less, and a signed value, |0 will round it like C does.
|
||||
if (bits && bits <= 32 && signed) return '('+value+'|0)';
|
||||
// If the value may be negative, and we care about proper rounding, then use a slow but correct function
|
||||
if (signed && correctRoundings()) return 'cRound(' + value + ')';
|
||||
// Either this must be positive, so Math.Floor is correct, or we don't care
|
||||
return 'Math.floor(' + value + ')';
|
||||
}
|
||||
|
||||
var mathop = makeFuncLineActor('mathop', function(item) { with(item) {
|
||||
for (var i = 1; i <= 4; i++) {
|
||||
if (item['param'+i]) {
|
||||
item['ident'+i] = indexizeFunctions(finalizeLLVMParameter(item['param'+i]));
|
||||
if (!isNumber(item['ident'+i])) {
|
||||
item['ident'+i] = '(' + item['ident'+i] + ')'; // we may have nested expressions. So enforce the order of operations we want
|
||||
}
|
||||
} else {
|
||||
item['ident'+i] = null; // just so it exists for purposes of reading ident2 etc. later on, and no exception is thrown
|
||||
}
|
||||
}
|
||||
if (op in set('udiv', 'urem', 'uitofp', 'zext', 'lshr') || (variant && variant[0] == 'u')) {
|
||||
ident1 = makeSignOp(ident1, type, 'un');
|
||||
ident2 = makeSignOp(ident2, type, 'un');
|
||||
} else if (op in set('sdiv', 'srem', 'sitofp', 'sext', 'ashr') || (variant && variant[0] == 's')) {
|
||||
ident1 = makeSignOp(ident1, type, 're');
|
||||
ident2 = makeSignOp(ident2, type, 're');
|
||||
}
|
||||
var bits = null;
|
||||
if (item.type[0] === 'i') {
|
||||
bits = parseInt(item.type.substr(1));
|
||||
}
|
||||
var bitsLeft = ident2 ? ident2.substr(2, ident2.length-3) : null; // remove (i and ), to leave number. This value is important in float ops
|
||||
|
||||
switch (op) {
|
||||
// basic integer ops
|
||||
case 'add': return handleOverflow(ident1 + ' + ' + ident2, bits);
|
||||
case 'sub': return handleOverflow(ident1 + ' - ' + ident2, bits);
|
||||
case 'sdiv': case 'udiv': return makeRounding(ident1 + '/' + ident2, bits, op[0] === 's');
|
||||
case 'mul': return handleOverflow(ident1 + ' * ' + ident2, bits);
|
||||
case 'urem': case 'srem': return ident1 + ' % ' + ident2;
|
||||
case 'or': return ident1 + ' | ' + ident2; // TODO this forces into a 32-bit int - add overflow-style checks? also other bitops below us
|
||||
case 'and': return ident1 + ' & ' + ident2;
|
||||
case 'xor': return ident1 + ' ^ ' + ident2;
|
||||
case 'shl': {
|
||||
// Note: Increases in size may reach the 32-bit limit... where our sign can flip. But this may be expected by the code...
|
||||
/*
|
||||
if (bits >= 32) {
|
||||
if (CHECK_SIGNS && !CORRECT_SIGNS) return 'shlSignCheck(' + ident1 + ', ' + ident2 + ')';
|
||||
if (CORRECT_SIGNS) {
|
||||
var mul = 'Math.pow(2, ' + ident2 + ')';
|
||||
if (isNumber(ident2)) mul = eval(mul);
|
||||
return ident1 + ' * ' + mul;
|
||||
}
|
||||
}
|
||||
*/
|
||||
return ident1 + ' << ' + ident2;
|
||||
}
|
||||
case 'ashr': return ident1 + ' >> ' + ident2;
|
||||
case 'lshr': return ident1 + ' >>> ' + ident2;
|
||||
// basic float ops
|
||||
case 'fadd': return ident1 + ' + ' + ident2;
|
||||
case 'fsub': return ident1 + ' - ' + ident2;
|
||||
case 'fdiv': return ident1 + ' / ' + ident2;
|
||||
case 'fmul': return ident1 + ' * ' + ident2;
|
||||
case 'uitofp': case 'sitofp': return ident1;
|
||||
case 'fptoui': case 'fptosi': return makeRounding(ident1, bitsLeft, op === 'fptosi');
|
||||
|
||||
// TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking
|
||||
// Note that with typed arrays, these become 0 when written. So that is a potential difference with non-typed array runs.
|
||||
case 'icmp': {
|
||||
switch (variant) {
|
||||
case 'uge': case 'sge': return ident1 + ' >= ' + ident2;
|
||||
case 'ule': case 'sle': return ident1 + ' <= ' + ident2;
|
||||
case 'ugt': case 'sgt': return ident1 + ' > ' + ident2;
|
||||
case 'ult': case 'slt': return ident1 + ' < ' + ident2;
|
||||
// We use loose comparisons, which allows false == 0 to be true, etc. Ditto in fcmp
|
||||
case 'ne': case 'eq': {
|
||||
// We must sign them, so we do not compare -1 to 255 (could have unsigned them both too)
|
||||
// since LLVM tells us if <=, >= etc. comparisons are signed, but not == and !=.
|
||||
ident1 = makeSignOp(ident1, type, 're');
|
||||
ident2 = makeSignOp(ident2, type, 're');
|
||||
return ident1 + (variant === 'eq' ? '==' : '!=') + ident2;
|
||||
}
|
||||
default: throw 'Unknown icmp variant: ' + variant;
|
||||
}
|
||||
}
|
||||
case 'fcmp': {
|
||||
switch (variant) {
|
||||
// TODO 'o' ones should be 'ordered (no NaN) and',
|
||||
// 'u' ones should be 'unordered or'.
|
||||
case 'uge': case 'oge': return ident1 + ' >= ' + ident2;
|
||||
case 'ule': case 'ole': return ident1 + ' <= ' + ident2;
|
||||
case 'ugt': case 'ogt': return ident1 + ' > ' + ident2;
|
||||
case 'ult': case 'olt': return ident1 + ' < ' + ident2;
|
||||
case 'une': case 'one': return ident1 + ' != ' + ident2;
|
||||
case 'ueq': case 'oeq': return ident1 + ' == ' + ident2;
|
||||
case 'ord': return '!isNaN(' + ident1 + ') && !isNaN(' + ident2 + ')';
|
||||
case 'uno': return 'isNaN(' + ident1 + ') || isNaN(' + ident2 + ')';
|
||||
case 'true': return '1';
|
||||
default: throw 'Unknown fcmp variant: ' + variant;
|
||||
}
|
||||
}
|
||||
// Note that zext has sign checking, see above. We must guard against -33 in i8 turning into -33 in i32
|
||||
// then unsigning that i32... which would give something huge.
|
||||
case 'zext': case 'fpext': case 'sext': return ident1;
|
||||
case 'fptrunc': return ident1;
|
||||
case 'trunc': {
|
||||
// Unlike extending, which we just 'do' (by doing nothing),
|
||||
// truncating can change the number, e.g. by truncating to an i1
|
||||
// in order to get the first bit
|
||||
assert(ident2[1] == 'i');
|
||||
assert(bitsLeft <= 32, 'Cannot truncate to more than 32 bits, since we use a native & op');
|
||||
return '((' + ident1 + ') & ' + (Math.pow(2, bitsLeft)-1) + ')';
|
||||
}
|
||||
case 'select': return ident1 + ' ? ' + ident2 + ' : ' + ident3;
|
||||
case 'ptrtoint': return ident1;
|
||||
case 'inttoptr': return ident1;
|
||||
default: throw 'Unknown mathcmp op: ' + item.op;
|
||||
}
|
||||
} });
|
||||
|
||||
function getGetElementPtrIndexes(item) {
|
||||
var type = item.params[0].type;
|
||||
item.params = item.params.map(finalizeLLVMParameter);
|
||||
var ident = item.params[0];
|
||||
|
||||
// struct pointer, struct*, and getting a ptr to an element in that struct. Param 1 is which struct, then we have items in that
|
||||
// struct, and possibly further substructures, all embedded
|
||||
// can also be to 'blocks': [8 x i32]*, not just structs
|
||||
type = removePointing(type);
|
||||
var indexes = [makeGetPos(ident)];
|
||||
var offset = item.params[1];
|
||||
if (offset != 0) {
|
||||
if (isStructType(type)) {
|
||||
indexes.push(getFastValue(Types.types[type].flatSize, '*', offset));
|
||||
} else {
|
||||
indexes.push(getFastValue(getNativeFieldSize(type, true), '*', offset));
|
||||
}
|
||||
}
|
||||
item.params.slice(2, item.params.length).forEach(function(arg) {
|
||||
var curr = arg;
|
||||
// TODO: If index is constant, optimize
|
||||
var typeData = Types.types[type];
|
||||
if (isStructType(type) && typeData.needsFlattening) {
|
||||
if (typeData.flatFactor) {
|
||||
indexes.push(getFastValue(curr, '*', typeData.flatFactor));
|
||||
} else {
|
||||
if (isNumber(curr)) {
|
||||
indexes.push(typeData.flatIndexes[curr]);
|
||||
} else {
|
||||
indexes.push(toNiceIdent(type) + '___FLATTENER[' + curr + ']'); // TODO: If curr is constant, optimize out the flattener struct
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (curr != 0) {
|
||||
indexes.push(curr);
|
||||
}
|
||||
}
|
||||
if (!isNumber(curr) || parseInt(curr) < 0) {
|
||||
// We have a *variable* to index with, or a negative number. In both
|
||||
// cases, in theory we might need to do something dynamic here. FIXME?
|
||||
// But, most likely all the possible types are the same, so do that case here now...
|
||||
for (var i = 1; i < typeData.fields.length; i++) {
|
||||
assert(typeData.fields[0] === typeData.fields[i]);
|
||||
}
|
||||
curr = 0;
|
||||
}
|
||||
type = typeData ? typeData.fields[curr] : '';
|
||||
});
|
||||
var ret = indexes[0];
|
||||
for (var i = 1; i < indexes.length; i++) {
|
||||
ret = getFastValue(ret, '+', indexes[i]);
|
||||
}
|
||||
|
||||
ret = handleOverflow(ret, 32); // XXX - we assume a 32-bit arch here. If you fail on this, change to 64
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function finalizeLLVMFunctionCall(item) {
|
||||
switch(item.intertype) {
|
||||
case 'getelementptr':
|
||||
return makePointer(makeGetSlabs(item.ident, item.type)[0], getGetElementPtrIndexes(item), null, item.type);
|
||||
case 'bitcast':
|
||||
case 'inttoptr':
|
||||
case 'ptrtoint':
|
||||
return finalizeLLVMParameter(item.params[0]);
|
||||
case 'icmp': case 'mul': case 'zext': case 'add': case 'sub': case 'div':
|
||||
var temp = {
|
||||
op: item.intertype,
|
||||
variant: item.variant,
|
||||
type: item.type
|
||||
};
|
||||
for (var i = 1; i <= 4; i++) {
|
||||
if (item.params[i-1]) {
|
||||
temp['param' + i] = finalizeLLVMParameter(item.params[i-1]);
|
||||
}
|
||||
}
|
||||
mathop.processItem(temp);
|
||||
return temp.JS;
|
||||
default:
|
||||
throw 'Invalid function to finalize: ' + dump(item.intertype);
|
||||
}
|
||||
}
|
||||
|
||||
// From parseLLVMSegment
|
||||
function finalizeLLVMParameter(param) {
|
||||
if (isNumber(param)) {
|
||||
return param;
|
||||
} else if (typeof param === 'string') {
|
||||
return toNiceIdentCarefully(param);
|
||||
} else if (param.intertype in PARSABLE_LLVM_FUNCTIONS) {
|
||||
return finalizeLLVMFunctionCall(param);
|
||||
} else if (param.intertype == 'value') {
|
||||
return parseNumerical(param.ident);
|
||||
} else if (param.intertype == 'structvalue') {
|
||||
return param.values.map(finalizeLLVMParameter);
|
||||
} else {
|
||||
throw 'invalid llvm parameter: ' + param.intertype;
|
||||
}
|
||||
}
|
||||
makeFuncLineActor('mathop', processMathop);
|
||||
|
||||
makeFuncLineActor('bitcast', function(item) {
|
||||
var ident = toNiceIdent(item.ident);
|
||||
|
|
|
@ -307,6 +307,16 @@ function parseParamTokens(params) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
function finalizeParam(param) {
|
||||
if (param.intertype in PARSABLE_LLVM_FUNCTIONS) {
|
||||
return finalizeLLVMFunctionCall(param);
|
||||
} else if (param.intertype === 'jsvalue') {
|
||||
return param.ident;
|
||||
} else {
|
||||
return toNiceIdent(param.ident);
|
||||
}
|
||||
}
|
||||
|
||||
// Segment ==> Parameter
|
||||
function parseLLVMSegment(segment) {
|
||||
var type;
|
||||
|
@ -715,3 +725,255 @@ function makeGetSlabs(ptr, type, allowMultiple) {
|
|||
}
|
||||
}
|
||||
|
||||
function finalizeLLVMFunctionCall(item) {
|
||||
switch(item.intertype) {
|
||||
case 'getelementptr': // TODO finalizeLLVMParameter on the ident and the indexes?
|
||||
return makePointer(makeGetSlabs(item.ident, item.type)[0], getGetElementPtrIndexes(item), null, item.type);
|
||||
case 'bitcast':
|
||||
case 'inttoptr':
|
||||
case 'ptrtoint':
|
||||
return finalizeLLVMParameter(item.params[0]);
|
||||
case 'icmp': case 'mul': case 'zext': case 'add': case 'sub': case 'div':
|
||||
var temp = {
|
||||
op: item.intertype,
|
||||
variant: item.variant,
|
||||
type: item.type
|
||||
};
|
||||
for (var i = 1; i <= 4; i++) {
|
||||
if (item.params[i-1]) {
|
||||
temp['param' + i] = finalizeLLVMParameter(item.params[i-1]);
|
||||
}
|
||||
}
|
||||
return processMathop(temp);
|
||||
default:
|
||||
throw 'Invalid function to finalize: ' + dump(item.intertype);
|
||||
}
|
||||
}
|
||||
|
||||
function getGetElementPtrIndexes(item) {
|
||||
var type = item.params[0].type;
|
||||
item.params = item.params.map(finalizeLLVMParameter);
|
||||
var ident = item.params[0];
|
||||
|
||||
// struct pointer, struct*, and getting a ptr to an element in that struct. Param 1 is which struct, then we have items in that
|
||||
// struct, and possibly further substructures, all embedded
|
||||
// can also be to 'blocks': [8 x i32]*, not just structs
|
||||
type = removePointing(type);
|
||||
var indexes = [makeGetPos(ident)];
|
||||
var offset = item.params[1];
|
||||
if (offset != 0) {
|
||||
if (isStructType(type)) {
|
||||
indexes.push(getFastValue(Types.types[type].flatSize, '*', offset));
|
||||
} else {
|
||||
indexes.push(getFastValue(getNativeFieldSize(type, true), '*', offset));
|
||||
}
|
||||
}
|
||||
item.params.slice(2, item.params.length).forEach(function(arg) {
|
||||
var curr = arg;
|
||||
// TODO: If index is constant, optimize
|
||||
var typeData = Types.types[type];
|
||||
if (isStructType(type) && typeData.needsFlattening) {
|
||||
if (typeData.flatFactor) {
|
||||
indexes.push(getFastValue(curr, '*', typeData.flatFactor));
|
||||
} else {
|
||||
if (isNumber(curr)) {
|
||||
indexes.push(typeData.flatIndexes[curr]);
|
||||
} else {
|
||||
indexes.push(toNiceIdent(type) + '___FLATTENER[' + curr + ']'); // TODO: If curr is constant, optimize out the flattener struct
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (curr != 0) {
|
||||
indexes.push(curr);
|
||||
}
|
||||
}
|
||||
if (!isNumber(curr) || parseInt(curr) < 0) {
|
||||
// We have a *variable* to index with, or a negative number. In both
|
||||
// cases, in theory we might need to do something dynamic here. FIXME?
|
||||
// But, most likely all the possible types are the same, so do that case here now...
|
||||
for (var i = 1; i < typeData.fields.length; i++) {
|
||||
assert(typeData.fields[0] === typeData.fields[i]);
|
||||
}
|
||||
curr = 0;
|
||||
}
|
||||
type = typeData ? typeData.fields[curr] : '';
|
||||
});
|
||||
var ret = indexes[0];
|
||||
for (var i = 1; i < indexes.length; i++) {
|
||||
ret = getFastValue(ret, '+', indexes[i]);
|
||||
}
|
||||
|
||||
ret = handleOverflow(ret, 32); // XXX - we assume a 32-bit arch here. If you fail on this, change to 64
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function handleOverflow(text, bits) {
|
||||
if (!bits) return text;
|
||||
if (bits <= 32 && correctOverflows()) text = '(' + text + ')&' + (Math.pow(2, bits) - 1);
|
||||
if (!CHECK_OVERFLOWS || correctSpecificOverflow()) return text; // If we are correcting a specific overflow here, do not check for it
|
||||
return 'CHECK_OVERFLOW(' + text + ', ' + bits + ')';
|
||||
}
|
||||
|
||||
// From parseLLVMSegment
|
||||
function finalizeLLVMParameter(param) {
|
||||
if (isNumber(param)) {
|
||||
return param;
|
||||
} else if (typeof param === 'string') {
|
||||
return toNiceIdentCarefully(param);
|
||||
} else if (param.intertype in PARSABLE_LLVM_FUNCTIONS) {
|
||||
return finalizeLLVMFunctionCall(param);
|
||||
} else if (param.intertype == 'value') {
|
||||
return parseNumerical(param.ident);
|
||||
} else if (param.intertype == 'structvalue') {
|
||||
return param.values.map(finalizeLLVMParameter);
|
||||
} else {
|
||||
throw 'invalid llvm parameter: ' + param.intertype;
|
||||
}
|
||||
}
|
||||
|
||||
function makeSignOp(value, type, op) { // TODO: If value isNumber, do this at compile time
|
||||
if (!value) return value;
|
||||
if (!correctSigns() && !CHECK_SIGNS) return value;
|
||||
if (type in Runtime.INT_TYPES) {
|
||||
var bits = parseInt(type.substr(1));
|
||||
if (isNumber(value)) {
|
||||
// Sign/unsign constants at compile time
|
||||
return eval(op + 'Sign(' + value + ', ' + bits + ', 1)').toString();
|
||||
}
|
||||
// shortcuts for 32-bit case
|
||||
if (bits === 32 && !CHECK_SIGNS) {
|
||||
if (op === 're') {
|
||||
return '((' + value + ')|0)';
|
||||
} else {
|
||||
// TODO: figure out something here along the lines of return '(' + Math.pow(2, 32) + '+((' + value + ')|0))';
|
||||
}
|
||||
}
|
||||
return op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(correctSpecificSign()) + ')'; // If we are correcting a specific sign here, do not check for it
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function makeRounding(value, bits, signed) {
|
||||
// C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
|
||||
// With 32 bits and less, and a signed value, |0 will round it like C does.
|
||||
if (bits && bits <= 32 && signed) return '('+value+'|0)';
|
||||
// If the value may be negative, and we care about proper rounding, then use a slow but correct function
|
||||
if (signed && correctRoundings()) return 'cRound(' + value + ')';
|
||||
// Either this must be positive, so Math.Floor is correct, or we don't care
|
||||
return 'Math.floor(' + value + ')';
|
||||
}
|
||||
|
||||
function processMathop(item) { with(item) {
|
||||
for (var i = 1; i <= 4; i++) {
|
||||
if (item['param'+i]) {
|
||||
item['ident'+i] = indexizeFunctions(finalizeLLVMParameter(item['param'+i]));
|
||||
if (!isNumber(item['ident'+i])) {
|
||||
item['ident'+i] = '(' + item['ident'+i] + ')'; // we may have nested expressions. So enforce the order of operations we want
|
||||
}
|
||||
} else {
|
||||
item['ident'+i] = null; // just so it exists for purposes of reading ident2 etc. later on, and no exception is thrown
|
||||
}
|
||||
}
|
||||
if (op in set('udiv', 'urem', 'uitofp', 'zext', 'lshr') || (variant && variant[0] == 'u')) {
|
||||
ident1 = makeSignOp(ident1, type, 'un');
|
||||
ident2 = makeSignOp(ident2, type, 'un');
|
||||
} else if (op in set('sdiv', 'srem', 'sitofp', 'sext', 'ashr') || (variant && variant[0] == 's')) {
|
||||
ident1 = makeSignOp(ident1, type, 're');
|
||||
ident2 = makeSignOp(ident2, type, 're');
|
||||
}
|
||||
var bits = null;
|
||||
if (item.type[0] === 'i') {
|
||||
bits = parseInt(item.type.substr(1));
|
||||
}
|
||||
var bitsLeft = ident2 ? ident2.substr(2, ident2.length-3) : null; // remove (i and ), to leave number. This value is important in float ops
|
||||
|
||||
switch (op) {
|
||||
// basic integer ops
|
||||
case 'add': return handleOverflow(ident1 + ' + ' + ident2, bits);
|
||||
case 'sub': return handleOverflow(ident1 + ' - ' + ident2, bits);
|
||||
case 'sdiv': case 'udiv': return makeRounding(ident1 + '/' + ident2, bits, op[0] === 's');
|
||||
case 'mul': return handleOverflow(ident1 + ' * ' + ident2, bits);
|
||||
case 'urem': case 'srem': return ident1 + ' % ' + ident2;
|
||||
case 'or': return ident1 + ' | ' + ident2; // TODO this forces into a 32-bit int - add overflow-style checks? also other bitops below us
|
||||
case 'and': return ident1 + ' & ' + ident2;
|
||||
case 'xor': return ident1 + ' ^ ' + ident2;
|
||||
case 'shl': {
|
||||
// Note: Increases in size may reach the 32-bit limit... where our sign can flip. But this may be expected by the code...
|
||||
/*
|
||||
if (bits >= 32) {
|
||||
if (CHECK_SIGNS && !CORRECT_SIGNS) return 'shlSignCheck(' + ident1 + ', ' + ident2 + ')';
|
||||
if (CORRECT_SIGNS) {
|
||||
var mul = 'Math.pow(2, ' + ident2 + ')';
|
||||
if (isNumber(ident2)) mul = eval(mul);
|
||||
return ident1 + ' * ' + mul;
|
||||
}
|
||||
}
|
||||
*/
|
||||
return ident1 + ' << ' + ident2;
|
||||
}
|
||||
case 'ashr': return ident1 + ' >> ' + ident2;
|
||||
case 'lshr': return ident1 + ' >>> ' + ident2;
|
||||
// basic float ops
|
||||
case 'fadd': return ident1 + ' + ' + ident2;
|
||||
case 'fsub': return ident1 + ' - ' + ident2;
|
||||
case 'fdiv': return ident1 + ' / ' + ident2;
|
||||
case 'fmul': return ident1 + ' * ' + ident2;
|
||||
case 'uitofp': case 'sitofp': return ident1;
|
||||
case 'fptoui': case 'fptosi': return makeRounding(ident1, bitsLeft, op === 'fptosi');
|
||||
|
||||
// TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking
|
||||
// Note that with typed arrays, these become 0 when written. So that is a potential difference with non-typed array runs.
|
||||
case 'icmp': {
|
||||
switch (variant) {
|
||||
case 'uge': case 'sge': return ident1 + ' >= ' + ident2;
|
||||
case 'ule': case 'sle': return ident1 + ' <= ' + ident2;
|
||||
case 'ugt': case 'sgt': return ident1 + ' > ' + ident2;
|
||||
case 'ult': case 'slt': return ident1 + ' < ' + ident2;
|
||||
// We use loose comparisons, which allows false == 0 to be true, etc. Ditto in fcmp
|
||||
case 'ne': case 'eq': {
|
||||
// We must sign them, so we do not compare -1 to 255 (could have unsigned them both too)
|
||||
// since LLVM tells us if <=, >= etc. comparisons are signed, but not == and !=.
|
||||
ident1 = makeSignOp(ident1, type, 're');
|
||||
ident2 = makeSignOp(ident2, type, 're');
|
||||
return ident1 + (variant === 'eq' ? '==' : '!=') + ident2;
|
||||
}
|
||||
default: throw 'Unknown icmp variant: ' + variant;
|
||||
}
|
||||
}
|
||||
case 'fcmp': {
|
||||
switch (variant) {
|
||||
// TODO 'o' ones should be 'ordered (no NaN) and',
|
||||
// 'u' ones should be 'unordered or'.
|
||||
case 'uge': case 'oge': return ident1 + ' >= ' + ident2;
|
||||
case 'ule': case 'ole': return ident1 + ' <= ' + ident2;
|
||||
case 'ugt': case 'ogt': return ident1 + ' > ' + ident2;
|
||||
case 'ult': case 'olt': return ident1 + ' < ' + ident2;
|
||||
case 'une': case 'one': return ident1 + ' != ' + ident2;
|
||||
case 'ueq': case 'oeq': return ident1 + ' == ' + ident2;
|
||||
case 'ord': return '!isNaN(' + ident1 + ') && !isNaN(' + ident2 + ')';
|
||||
case 'uno': return 'isNaN(' + ident1 + ') || isNaN(' + ident2 + ')';
|
||||
case 'true': return '1';
|
||||
default: throw 'Unknown fcmp variant: ' + variant;
|
||||
}
|
||||
}
|
||||
// Note that zext has sign checking, see above. We must guard against -33 in i8 turning into -33 in i32
|
||||
// then unsigning that i32... which would give something huge.
|
||||
case 'zext': case 'fpext': case 'sext': return ident1;
|
||||
case 'fptrunc': return ident1;
|
||||
case 'trunc': {
|
||||
// Unlike extending, which we just 'do' (by doing nothing),
|
||||
// truncating can change the number, e.g. by truncating to an i1
|
||||
// in order to get the first bit
|
||||
assert(ident2[1] == 'i');
|
||||
assert(bitsLeft <= 32, 'Cannot truncate to more than 32 bits, since we use a native & op');
|
||||
return '((' + ident1 + ') & ' + (Math.pow(2, bitsLeft)-1) + ')';
|
||||
}
|
||||
case 'select': return ident1 + ' ? ' + ident2 + ' : ' + ident3;
|
||||
case 'ptrtoint': return ident1;
|
||||
case 'inttoptr': return ident1;
|
||||
default: throw 'Unknown mathcmp op: ' + item.op;
|
||||
}
|
||||
} }
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
RuntimeGenerator = {
|
||||
alloc: function(size, type, init) {
|
||||
var ret = type + 'TOP';
|
||||
if (GUARD_MEMORY) {
|
||||
if (ASSERTIONS) {
|
||||
ret += '; assert(' + size + ' > 0, "Trying to allocate 0")';
|
||||
}
|
||||
if (init) {
|
||||
|
@ -23,7 +23,7 @@ RuntimeGenerator = {
|
|||
// An allocation that lives as long as the current function call
|
||||
stackAlloc: function(size) {
|
||||
var ret = RuntimeGenerator.alloc(size, 'STACK', INIT_STACK);
|
||||
if (GUARD_MEMORY) {
|
||||
if (ASSERTIONS) {
|
||||
ret += '; assert(STACKTOP < STACK_ROOT + STACK_MAX, "Ran out of stack")';
|
||||
}
|
||||
return ret;
|
||||
|
@ -35,7 +35,7 @@ RuntimeGenerator = {
|
|||
// until the parent unwinds its stack. So potentially if we are in
|
||||
// a loop, we can use a lot of memory.
|
||||
var ret = 'var __stackBase__ = STACKTOP; STACKTOP += ' + initial;
|
||||
if (GUARD_MEMORY) {
|
||||
if (ASSERTIONS) {
|
||||
ret += '; assert(STACKTOP < STACK_MAX)';
|
||||
}
|
||||
if (INIT_STACK) {
|
||||
|
|
|
@ -23,11 +23,12 @@ CHECK_SIGNS = 0; // Runtime errors for signing issues that need correcting.
|
|||
// order to find if your code needs CORRECT_SIGNS. If you can get your
|
||||
// code to run without CORRECT_SIGNS, it will run much faster
|
||||
|
||||
GUARD_LABELS = 0; // Whether we should throw if we encounter a bad __label__, i.e.,
|
||||
// if code flow runs into a fault
|
||||
GUARD_MEMORY = 1; // Whether we should check that each allocation to the stack does not
|
||||
// exceed it's size, whether all allocations (stack and static) are
|
||||
// of positive size, etc.
|
||||
ASSERTIONS = 1; // Whether we should add runtime assertions, for example to
|
||||
// check that each allocation to the stack does not
|
||||
// exceed it's size, whether all allocations (stack and static) are
|
||||
// of positive size, etc., whether we should throw if we encounter a bad __label__, i.e.,
|
||||
// if code flow runs into a fault
|
||||
|
||||
INVOKE_RUN = 1; // Whether we will call run(). Disable if you embed the generated
|
||||
// code in your own, and will call run() yourself at the right time
|
||||
INIT_STACK = 1; // Whether to initialize memory on the stack to 0.
|
||||
|
|
|
@ -62,6 +62,12 @@ function assertTrue(a, msg) {
|
|||
}
|
||||
assert = assertTrue;
|
||||
|
||||
function warn(a, msg) {
|
||||
if (!a) {
|
||||
dprint('Warning: ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
function dedup(items, ident) {
|
||||
var seen = {};
|
||||
if (ident) {
|
||||
|
@ -226,3 +232,7 @@ function setIntersect(x, y) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
function copy(x) {
|
||||
return JSON.parse(JSON.stringify(x));
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ class RunnerCore(unittest.TestCase):
|
|||
def do_emscripten(self, filename, output_processor=None):
|
||||
# Run Emscripten
|
||||
exported_settings = {}
|
||||
for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'GUARD_MEMORY', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN']:
|
||||
for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN']:
|
||||
value = eval(setting)
|
||||
exported_settings[setting] = value
|
||||
compiler_output = timeout_run(Popen([EMSCRIPTEN, filename + '.o.ll', COMPILER_ENGINE[0], str(exported_settings).replace("'", '"'), filename + '.o.js'], stdout=PIPE, stderr=STDOUT), TIMEOUT, 'Compiling')
|
||||
|
@ -2174,7 +2174,7 @@ if 'benchmark' not in sys.argv:
|
|||
exec('''
|
||||
class %s(T):
|
||||
def setUp(self):
|
||||
global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, GUARD_MEMORY, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN
|
||||
global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, ASSERTIONS, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN
|
||||
|
||||
COMPILER = '%s'
|
||||
QUANTUM_SIZE = 4 # See settings.js
|
||||
|
@ -2182,7 +2182,7 @@ class %s(T):
|
|||
embetter = %d
|
||||
INVOKE_RUN = 1
|
||||
RELOOP = OPTIMIZE = USE_TYPED_ARRAYS = embetter
|
||||
GUARD_MEMORY = 1-embetter
|
||||
ASSERTIONS = 1-embetter
|
||||
SAFE_HEAP = 1-(embetter and llvm_opts)
|
||||
LLVM_OPTS = llvm_opts
|
||||
CHECK_OVERFLOWS = 1-(embetter or llvm_opts)
|
||||
|
@ -2225,7 +2225,7 @@ else:
|
|||
QUANTUM_SIZE = 4
|
||||
RELOOP = OPTIMIZE = 1
|
||||
USE_TYPED_ARRAYS = 0
|
||||
GUARD_MEMORY = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = 0
|
||||
ASSERTIONS = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = 0
|
||||
INVOKE_RUN = 1
|
||||
CORRECT_SIGNS = 0
|
||||
CORRECT_ROUNDINGS = 0
|
||||
|
|
Загрузка…
Ссылка в новой задаче