diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 184 | ||||
-rw-r--r-- | src/include/emscripten.h | 38 | ||||
-rw-r--r-- | src/intertyper.js | 4 | ||||
-rw-r--r-- | src/jsifier.js | 7 | ||||
-rw-r--r-- | src/library.js | 11 | ||||
-rw-r--r-- | src/modules.js | 22 | ||||
-rw-r--r-- | src/parseTools.js | 2 |
7 files changed, 212 insertions, 56 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index db223c36..7f67e7e1 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -38,6 +38,9 @@ function analyzer(data) { item.items = temp.leftIn; temp.splitOut.forEach(function(type) { Types.types[type.name_] = type; + if (QUANTUM_SIZE === 1) { + Types.fatTypes[type.name_] = copy(type); + } }); // Functions & labels @@ -85,7 +88,7 @@ function analyzer(data) { } }); - function addType(type, data) { + function addTypeInternal(type, data) { if (type.length == 1) return; if (Types.types[type]) return; if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return; @@ -131,11 +134,20 @@ function analyzer(data) { }; } + function addType(type, data) { + addTypeInternal(type, data); + if (QUANTUM_SIZE === 1) { + Types.flipTypes(); + addTypeInternal(type, data); + Types.flipTypes(); + } + } + // Typevestigator substrate.addActor('Typevestigator', { processItem: function(data) { for (type in Types.needAnalysis) { - if (type) addType(type, data); + if (type) addType(type, data, Types.types); } Types.needAnalysis = []; this.forwardItem(data, 'Typeanalyzer'); @@ -144,7 +156,9 @@ function analyzer(data) { // Type analyzer substrate.addActor('Typeanalyzer', { - processItem: function(item) { + processItem: function analyzeTypes(item, fatTypes) { + var types = Types.types; + // 'fields' is the raw list of LLVM fields. However, we embed // child structures into parent structures, basically like C. // So { int, { int, int }, int } would be represented as @@ -164,16 +178,16 @@ function analyzer(data) { var more = true; while (more) { more = false; - values(Types.types).forEach(function(type) { + values(types).forEach(function(type) { if (type.flatIndexes) return; var ready = true; type.fields.forEach(function(field) { if (isStructType(field)) { - if (!Types.types[field]) { - addType(field, item); + if (!types[field]) { + addType(field, item, types); ready = false; } else { - if (!Types.types[field].flatIndexes) { + if (!types[field].flatIndexes) { ready = false; } } @@ -186,12 +200,24 @@ function analyzer(data) { Runtime.calculateStructAlignment(type); - dprint('types', 'type: ' + type.name_ + ' : ' + JSON.stringify(type.fields)); + dprint('types', 'type (fat=' + !!fatTypes + '): ' + type.name_ + ' : ' + JSON.stringify(type.fields)); dprint('types', ' has final size of ' + type.flatSize + ', flatting: ' + type.needsFlattening + ' ? ' + (type.flatFactor ? type.flatFactor : JSON.stringify(type.flatIndexes))); }); } - this.forwardItem(item, 'VariableAnalyzer'); + if (QUANTUM_SIZE === 1 && !fatTypes) { + Types.flipTypes(); + // Fake a quantum size of 4 for fat types. TODO: Might want non-4 for some reason? + var trueQuantumSize = QUANTUM_SIZE; + QUANTUM_SIZE = 4; + analyzeTypes(item, true); + QUANTUM_SIZE = trueQuantumSize; + Types.flipTypes(); + } + + if (!fatTypes) { + this.forwardItem(item, 'VariableAnalyzer'); + } } }); @@ -244,6 +270,26 @@ function analyzer(data) { } }); + // Second pass over variables - notice when types are crossed by bitcast + + func.lines.forEach(function(item) { + if (item.intertype === 'assign' && item.value.intertype === 'bitcast') { + // bitcasts are unique in that they convert one pointer to another. We + // sometimes need to know the original type of a pointer, so we save that. + // + // originalType is the type this variable is created from + // derivedTypes are the types that this variable is cast into + func.variables[item.ident].originalType = item.value.type2; + + if (!isNumber(item.value.ident)) { + if (!func.variables[item.value.ident].derivedTypes) { + func.variables[item.value.ident].derivedTypes = []; + } + func.variables[item.value.ident].derivedTypes.push(item.value.type); + } + } + }); + for (vname in func.variables) { var variable = func.variables[vname]; @@ -304,7 +350,127 @@ function analyzer(data) { dprint('vars', '// var ' + vname + ': ' + JSON.stringify(variable)); } }); + this.forwardItem(item, 'QuantumFixer'); + } + }); + + // Quantum fixer + // + // See settings.js for the meaning of QUANTUM_SIZE. The issue we fix here is, + // to correct the .ll assembly code so that things work with QUANTUM_SIZE=1. + // + substrate.addActor('QuantumFixer', { + processItem: function(item) { this.forwardItem(item, 'LabelAnalyzer'); + if (QUANTUM_SIZE !== 1) return; + + // ptrs: the indexes of parameters that are pointers, whose originalType is what we want + // bytes: the index of the 'bytes' parameter + // TODO: malloc, realloc? + var FIXABLE_CALLS = { + 'memcpy': { ptrs: [0,1], bytes: 2 }, + 'memmove': { ptrs: [0,1], bytes: 2 }, + 'memset': { ptrs: [0], bytes: 2 }, + 'qsort': { ptrs: [0], bytes: 2 } + }; + + function getSize(types, type, fat) { + if (types[type]) return types[type].flatSize; + if (fat) { + QUANTUM_SIZE = 4; + } + var ret = getNativeFieldSize(type, true); + if (fat) { + QUANTUM_SIZE = 1; + } + return ret; + } + + item.functions.forEach(function(func) { + function getOriginalType(param) { + function get() { + if (param.intertype === 'value' && !isNumber(param.ident)) { + if (func.variables[param.ident]) { + return func.variables[param.ident].originalType; + } else { + return item.globalVariables[param.ident].originalType; + } + } else if (param.intertype === 'bitcast') { + return param.params[0].type; + } else if (param.intertype === 'getelementptr') { + if (param.params[0].type[0] === '[') return param.params[0].type; + } + return null; + } + var ret = get(); + if (ret && ret[0] === '[') { + var check = new RegExp(/^\[(\d+)\ x\ (.*)\]\*$/g).exec(ret); + assert(check); + ret = check[2] + '*'; + } + return ret; + } + + func.lines.forEach(function(line) { + // Call + if (line.intertype === 'call') { + var funcIdent = LibraryManager.getRootIdent(line.ident.substr(1)); + var fixData = FIXABLE_CALLS[funcIdent]; + if (!fixData) return; + var ptrs = fixData.ptrs.map(function(ptr) { return line.params[ptr] }); + var bytes = line.params[fixData.bytes].ident; + + // Only consider original types. This assumes memcpy always has pointers bitcast to i8* + var originalTypes = ptrs.map(getOriginalType); + if (!originalTypes[0]) return; + originalTypes = originalTypes.map(function(type) { return removePointing(type) }); + var sizes = originalTypes.map(function(type) { return getSize(Types.types, type) }); + var fatSizes = originalTypes.map(function(type) { return getSize(Types.fatTypes, type, true) }); + // The sizes may not be identical, if we copy a descendant class into a parent class. We use + // the smaller size in that case. However, this may also be a bug, it is hard to tell, hence a warning + warn(dedup(sizes).length === 1, 'All sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' + + line.lineNum); + warn(dedup(fatSizes).length === 1, 'All fat sizes should probably be identical here: ' + dump(originalTypes) + ':' + dump(sizes) + ':' + + line.lineNum); + var size = Math.min.apply(null, sizes); + var fatSize = Math.min.apply(null, fatSizes); + if (isNumber(bytes)) { + assert(bytes % fatSize === 0, + 'The bytes copied must be a multiple of the full size, or else we are wrong about the type! ' + + [bytes, size, fatSize, originalTypes, line.lineNum]); + line.params[fixData.bytes].ident = size*(bytes/fatSize); + } else { + line.params[fixData.bytes].intertype = 'jsvalue'; + // We have an assertion in library::memcpy() that this is round + line.params[fixData.bytes].ident = size + '*(' + bytes + '/' + fatSize + ')'; + } + } + }); + }); + + // 2nd part - fix hardcoded constant offsets in global constants + values(item.globalVariables).forEach(function(variable) { + function recurse(item) { + if (item.contents) { + item.contents.forEach(recurse); + } else if (item.intertype === 'getelementptr' && item.params[0].intertype === 'bitcast' && item.params[0].type === 'i8*') { + var originalType = removePointing(item.params[0].params[0].type); + var fatSize = getSize(Types.fatTypes, originalType, true); + var slimSize = getSize(Types.types, originalType, false); + assert(fatSize % slimSize === 0); + item.params.slice(1).forEach(function(param) { + if (param.intertype === 'value' && isNumber(param.ident)) { + var corrected = parseInt(param.ident)/(fatSize/slimSize); + assert(corrected % 1 === 0); + param.ident = param.value.text = corrected.toString(); + } + }); + } else if (item.params) { + item.params.forEach(recurse); + } + } + if (!variable.external && variable.value) recurse(variable.value); + }); } }); diff --git a/src/include/emscripten.h b/src/include/emscripten.h index 958bef98..b06f3781 100644 --- a/src/include/emscripten.h +++ b/src/include/emscripten.h @@ -7,42 +7,8 @@ * http://emscripten.org */ -// ES_SIZEOF -// -// NOTE: As of now, ES_SIZEOF is not needed when using QUANTUM_SIZE -// of 4. We will use the same offsets as C/C++ does in that case. -// ES_SIZEOF is useful if QUANTUM_SIZE is 1. -// -// A 'safe' sizeof operator. Sadly llvm-gcc calculates sizeof's -// and stores them hardcoded as raw values, unlike say offsets -// within a structure which it nicely details using getelementptr. -// -// You should always use ES_SIZEOF|V instead of sizeof when using -// Emscripten. Use ES_SIZEOF for types, ES_SIZEOV for values. -// -// Note that there is no way for Emscripten to know if you used -// ES_SIZEOF properly, or if you did and and you used sizeof. -// No warning will be shown if you do not use it. -// -// Sadly -// #define ES_SIZEOF(x) int(&((x*)(NULL))[1]) -// does not work, since the compiler parses and hard-codes the -// value. So we need to trick it with a function call. -#ifdef EMSCRIPTEN - template<class T> - int es_sizeof(T* x) { return int(&x[1]); } - #define ES_SIZEOF(T) es_sizeof((T*)0) - template<class T> - int es_sizeov(T* x) { return es_sizeof((T*)0); } - #define ES_SIZEOV(V) es_sizeof(V) - - // Undefine normal archs, so asm is not attempted - #define __EMSCRIPTEN__ - #undef __i386__ - #undef __x86_64__ -#else - #define ES_SIZEOF(T) sizeof(T) -#endif +#undef __i386__ +#undef __x86_64__ // Interface to the underlying JS engine. This function will // eval() the given script. diff --git a/src/intertyper.js b/src/intertyper.js index db94b8af..8b9e4ebf 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -544,10 +544,10 @@ function intertyper(data, parseFunctions, baseLineNum) { substrate.addActor('Bitcast', { processItem: function(item) { item.intertype = 'bitcast'; - item.type = item.tokens[1].text; + item.type = item.tokens[4].text; // The final type Types.needAnalysis[item.type] = 0; item.ident = toNiceIdent(item.tokens[2].text); - item.type2 = item.tokens[4].text; + item.type2 = item.tokens[1].text; // The original type Types.needAnalysis[item.type2] = 0; this.forwardItem(item, 'Reintegrator'); } diff --git a/src/jsifier.js b/src/jsifier.js index 1901409e..29da592f 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -683,12 +683,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var varargs = []; params.forEach(function(param, i) { - var val; - if (param.intertype in PARSABLE_LLVM_FUNCTIONS) { - val = finalizeLLVMFunctionCall(param); - } else { - val = toNiceIdent(param.ident); - } + var val = finalizeParam(param); if (!func || !func.hasVarArgs || i < func.numParams-1) { // unrecognized functions (like library ones) cannot have varargs args.push(val); } else { diff --git a/src/library.js b/src/library.js index 7653d446..c7fe551b 100644 --- a/src/library.js +++ b/src/library.js @@ -87,7 +87,7 @@ var Library = { } else { ret = {{{ makeGetValue(0, 'argIndex', 'i32') }}}; } - argIndex += type === 'l'.charCodeAt(0) ? 8 : 4; // XXX hardcoded native sizes + argIndex += {{{ QUANTUM_SIZE === 1 ? 1 : "type === 'l'.charCodeAt(0) ? 8 : 4" }}}; } return ret; } @@ -463,7 +463,11 @@ var Library = { fstat: function(stream, ptr) { var info = _STDIO.streams[stream]; if (!info) return -1; - {{{ makeSetValue('ptr', '$struct_stat___FLATTENER[9]', 'info.data.length', 'i32') }}} // st_size. XXX: hardcoded index 9 into the structure. + try { + {{{ makeSetValue('ptr', '$struct_stat___FLATTENER[9]', 'info.data.length', 'i32') }}} // st_size. XXX: hardcoded index 9 into the structure. + } catch(e) { + {{{ makeSetValue('ptr', '9', 'info.data.length', 'i32') }}} // no FLATTENER + } // TODO: other fields return 0; }, @@ -592,6 +596,9 @@ var Library = { // string.h memcpy: function (dest, src, num, idunno) { +#if ASSERTIONS + assert(num % 1 === 0, 'memcpy given ' + num + ' bytes to copy. Problem with QUANTUM_SIZE=1 corrections perhaps?'); +#endif var curr; for (var i = 0; i < num; i++) { // TODO: optimize for the typed arrays case diff --git a/src/modules.js b/src/modules.js index d21fe599..54b92755 100644 --- a/src/modules.js +++ b/src/modules.js @@ -111,6 +111,13 @@ var Debugging = { var Types = { types: {}, + fatTypes: {}, // With QUANTUM_SIZE=1, we store the full-size type data here + flipTypes: function() { + var temp = this.fatTypes; + this.fatTypes = this.types; + this.types = temp; + }, + needAnalysis: {} // Types noticed during parsing, that need analysis }; @@ -137,3 +144,18 @@ var Functions = { } }; +var LibraryManager = { + // Given an ident, see if it is an alias for something, and so forth, returning + // the earliest ancestor (the root) + getRootIdent: function(ident) { + var ret = Library[ident]; + if (!ret) return null; + var last = ident; + while (typeof ret === 'string') { + last = ret; + ret = Library[ret]; + } + return last; + } +}; + diff --git a/src/parseTools.js b/src/parseTools.js index fd008910..41bb97f8 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -520,7 +520,7 @@ function getNativeFieldSize(field, alone) { "_float": 4, "_double": 8 }['_'+field]; // add '_' since float&double confuse closure compiler as keys - if (!size) { + if (!size && field[field.length-1] == '*') { size = QUANTUM_SIZE; // A pointer } if (!alone) size = Math.max(size, QUANTUM_SIZE); |