aboutsummaryrefslogtreecommitdiff
path: root/src/modules.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules.js')
-rw-r--r--src/modules.js184
1 files changed, 130 insertions, 54 deletions
diff --git a/src/modules.js b/src/modules.js
index e4093078..2d2a75d0 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -18,8 +18,9 @@ var LLVM = {
PHI_REACHERS: set('branch', 'switch', 'invoke', 'indirectbr'),
EXTENDS: set('sext', 'zext'),
COMPS: set('icmp', 'fcmp'),
- CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui'),
+ CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui', 'fpext', 'fptrunc'),
INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
+ MATHOP_IGNORABLES: set('exact', 'nnan', 'ninf', 'nsz', 'arcp', 'fast'),
};
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
@@ -46,7 +47,9 @@ var Debugging = {
var form3ab = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:metadata !\d+|i32 \d+|null), metadata !(\d+).*$/);
var form3ac = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:metadata !\d+|null), metadata !"[^"]*", metadata !(\d+)[^\[]*.*$/);
var form3ad = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:i32 \d+|null), (?:i32 \d+|null), metadata !"[^"]*", metadata !"[^"]*", metadata !"[^"]*", metadata !(\d+),.*$/);
- var form3b = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !"([^"]+)", metadata !"([^"]*)", (metadata !\d+|null)}.*$/);
+ var form3ae = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !(\d+).*$/);
+ // LLVM 3.3 drops the first and last parameters.
+ var form3b = new RegExp(/^!(\d+) = metadata !{(?:i32 \d+, )?metadata !"([^"]+)", metadata !"([^"]*)"(?:, (metadata !\d+|null))?}.*$/);
var form3c = new RegExp(/^!(\d+) = metadata !{\w+\d* !?(\d+)[^\d].*$/);
var form4 = new RegExp(/^!llvm.dbg.[\w\.]+ = .*$/);
var form5 = new RegExp(/^!(\d+) = metadata !{.*$/);
@@ -75,7 +78,7 @@ var Debugging = {
lines[i] = ';'; // return an empty line, to keep line numbers of subsequent lines the same
continue;
}
- calc = form3a.exec(line) || form3ab.exec(line) || form3ac.exec(line) || form3ad.exec(line);
+ calc = form3a.exec(line) || form3ab.exec(line) || form3ac.exec(line) || form3ad.exec(line) || form3ae.exec(line);
if (calc) {
metadataToParentMetadata[calc[1]] = calc[2];
lines[i] = ';';
@@ -114,7 +117,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;
@@ -224,12 +228,16 @@ var Types = {
needAnalysis: {}, // Types noticed during parsing, that need analysis
+ hasInlineJS: false, // whether the program has inline JS anywhere
+
+ usesSIMD: false,
+
// Set to true if we actually use precise i64 math: If PRECISE_I64_MATH is set, and also such math is actually
// needed (+,-,*,/,% - we do not need it for bitops), or PRECISE_I64_MATH is 2 (forced)
preciseI64MathUsed: (PRECISE_I64_MATH == 2)
};
-var firstTableIndex = (ASM_JS ? 2*RESERVED_FUNCTION_POINTERS : 0) + 2;
+var firstTableIndex = FUNCTION_POINTER_ALIGNMENT * ((ASM_JS ? RESERVED_FUNCTION_POINTERS : 0) + 1);
var Functions = {
// All functions that will be implemented in this file. Maps id to signature
@@ -244,37 +252,66 @@ var Functions = {
blockAddresses: {}, // maps functions to a map of block labels to label ids
+ aliases: {}, // in shared modules (MAIN_MODULE or SHARED_MODULE), a list of aliases for functions that have them
+
+ getSignatureLetter: function(type) {
+ switch(type) {
+ case 'float': return 'f';
+ case 'double': return 'd';
+ case 'void': return 'v';
+ default: return 'i';
+ }
+ },
+
+ getSignatureType: function(letter) {
+ switch(letter) {
+ case 'v': return 'void';
+ case 'i': return 'i32';
+ case 'f': return 'float';
+ case 'd': return 'double';
+ default: throw 'what is this sig? ' + sig;
+ }
+ },
+
getSignature: function(returnType, argTypes, hasVarArgs) {
- var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f');
+ var sig = Functions.getSignatureLetter(returnType);
for (var i = 0; i < argTypes.length; i++) {
var type = argTypes[i];
if (!type) break; // varargs
- sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s
+ if (type in Runtime.FLOAT_TYPES) {
+ sig += Functions.getSignatureLetter(type);
+ } else {
+ var chunks = getNumIntChunks(type);
+ if (chunks > 0) {
+ for (var j = 0; j < chunks; j++) sig += 'i';
+ } else if (type !== '...') {
+ // some special type like a SIMD vector (anything but varargs, which we handle below)
+ sig += Functions.getSignatureLetter(type);
+ }
+ }
}
if (hasVarArgs) sig += 'i';
return sig;
},
// Mark a function as needing indexing. Python will coordinate them all
- getIndex: function(ident, doNotCreate) {
- if (doNotCreate && !(ident in this.indexedFunctions)) {
- if (!Functions.getIndex.tentative) Functions.getIndex.tentative = {}; // only used by GL emulation; TODO: generalize when needed
- Functions.getIndex.tentative[ident] = 0;
- }
+ getIndex: function(ident, sig) {
+ var ret;
if (phase != 'post' && singlePhase) {
- if (!doNotCreate) this.indexedFunctions[ident] = 0; // tell python we need this indexized
- return "'{{ FI_" + toNiceIdent(ident) + " }}'"; // something python will replace later
+ ret = "'{{ FI_" + toNiceIdent(ident) + " }}'"; // something python will replace later
+ this.indexedFunctions[ident] = 0;
} else {
if (!singlePhase) return 'NO_INDEX'; // Should not index functions in post
- var ret = this.indexedFunctions[ident];
- if (!ret) {
- if (doNotCreate) return '0';
- ret = this.nextIndex;
- this.nextIndex += 2; // Need to have indexes be even numbers, see |polymorph| test
- this.indexedFunctions[ident] = ret;
- }
- return ret.toString();
+ ret = this.indexedFunctions[ident];
+ assert(ret);
+ ret = ret.toString();
}
+ if (SIDE_MODULE && sig) { // sig can be undefined for the GL library functions
+ ret = '((F_BASE_' + sig + ' + ' + ret + ')|0)';
+ } else if (BUILD_AS_SHARED_LIB) {
+ ret = '(FUNCTION_TABLE_OFFSET + ' + ret + ')';
+ }
+ return ret;
},
getTable: function(sig) {
@@ -300,7 +337,7 @@ var Functions = {
tables[sig][index] = ident;
}
var generated = false;
- var wrapped = {};
+ var wrapped = {}; // whether we wrapped a lib func
var maxTable = 0;
for (var t in tables) {
if (t == 'pre') continue;
@@ -310,37 +347,37 @@ var Functions = {
// Resolve multi-level aliases all the way down
while (1) {
var varData = Variables.globals[table[i]];
- if (!(varData && varData.resolvedAlias && varData.resolvedAlias.indexOf('FUNCTION_TABLE_OFFSET') < 0)) break;
+ if (!(varData && varData.resolvedAlias && !/(FUNCTION_TABLE_OFFSET|F_BASE_)/.test(varData.resolvedAlias))) break;
table[i] = table[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6
}
// Resolve library aliases
if (table[i]) {
var libName = LibraryManager.getRootIdent(table[i].substr(1));
if (libName && typeof libName == 'string') {
- table[i] = (libName.indexOf('.') < 0 ? '_' : '') + libName;
+ table[i] = (libName.indexOf('Math_') < 0 ? '_' : '') + libName;
}
}
if (ASM_JS) {
var curr = table[i];
if (curr && curr != '0' && !Functions.implementedFunctions[curr]) {
- curr = toNiceIdent(curr); // fix Math.* to Math_*
+ var short = toNiceIdent(curr); // fix Math.* to Math_*
+ curr = t + '_' + short; // libfuncs can alias with different sigs, wrap each separately
// This is a library function, we can't just put it in the function table, need a wrapper
if (!wrapped[curr]) {
- var args = '', arg_coercions = '', call = curr + '(', retPre = '', retPost = '';
+ var args = '', arg_coercions = '', call = short + '(', retPre = '', retPost = '';
if (t[0] != 'v') {
- if (t[0] == 'i') {
- retPre = 'return ';
- retPost = '|0';
- } else {
- retPre = 'return +';
- }
+ var temp = asmFFICoercion('X', Functions.getSignatureType(t[0])).split('X');
+ retPre = 'return ' + temp[0];
+ retPost = temp[1];
}
for (var j = 1; j < t.length; j++) {
args += (j > 1 ? ',' : '') + 'a' + j;
- arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32') + ';';
- call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32');
+ var type = Functions.getSignatureType(t[j]);
+ arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, type) + ';';
+ call += (j > 1 ? ',' : '') + asmCoercion('a' + j, type === 'float' ? 'double' : type); // ffi arguments must be doubles if they are floats
}
call += ')';
+ if (short == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things');
tables.pre += 'function ' + curr + '__wrapper(' + args + ') { ' + arg_coercions + ' ; ' + retPre + call + retPost + ' }\n';
wrapped[curr] = 1;
}
@@ -348,27 +385,17 @@ var Functions = {
}
}
}
- if (table.length > 20) {
- // add some newlines in the table, for readability
- var j = 10;
- while (j+10 < table.length) {
- table[j] += '\n';
- j += 10;
- }
- }
maxTable = Math.max(maxTable, table.length);
}
if (ASM_JS) maxTable = ceilPowerOfTwo(maxTable);
for (var t in tables) {
if (t == 'pre') continue;
var table = tables[t];
- if (ASM_JS) {
- // asm function table mask must be power of two
- // if nonaliasing, then standardize function table size, to avoid aliasing pointers through the &M mask (in a small table using a big index)
- var fullSize = ALIASING_FUNCTION_POINTERS ? ceilPowerOfTwo(table.length) : maxTable;
- for (var i = table.length; i < fullSize; i++) {
- table[i] = 0;
- }
+ // asm function table mask must be power of two, and non-asm must be aligned
+ // if nonaliasing, then standardize function table size, to avoid aliasing pointers through the &M mask (in a small table using a big index)
+ var fullSize = ASM_JS ? (ALIASING_FUNCTION_POINTERS ? ceilPowerOfTwo(table.length) : maxTable) : ((table.length+FUNCTION_POINTER_ALIGNMENT-1)&-FUNCTION_POINTER_ALIGNMENT);
+ for (var i = table.length; i < fullSize; i++) {
+ table[i] = 0;
}
// finalize table
var indices = table.toString().replace('"', '');
@@ -397,10 +424,43 @@ var LibraryManager = {
load: function() {
if (this.library) return;
- var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries);
+ var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_html5.js'].concat(additionalLibraries);
for (var i = 0; i < libraries.length; i++) {
- eval(processMacros(preprocess(read(libraries[i]))));
+ var filename = libraries[i];
+ var src = read(filename);
+ try {
+ var processed = processMacros(preprocess(src));
+ eval(processed);
+ } catch(e) {
+ var details = [e, e.lineNumber ? 'line number: ' + e.lineNumber : '', (e.stack || "").toString().replace('Object.<anonymous>', filename)];
+ if (processed) {
+ error('failure to execute js library "' + filename + '": ' + details + '\npreprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n' + processed + '\n=============\n');
+ } else {
+ error('failure to process js library "' + filename + '": ' + details + '\noriginal source:\n=============\n' + src + '\n=============\n');
+ }
+ throw e;
+ }
+ }
+
+ /*
+ // export code for CallHandlers.h
+ printErr('============================');
+ for (var x in this.library) {
+ var y = this.library[x];
+ if (typeof y === 'string' && x.indexOf('__sig') < 0 && x.indexOf('__postset') < 0 && y.indexOf(' ') < 0) {
+ printErr('DEF_REDIRECT_HANDLER(' + x + ', ' + y + ');');
+ }
}
+ printErr('============================');
+ for (var x in this.library) {
+ var y = this.library[x];
+ if (typeof y === 'string' && x.indexOf('__sig') < 0 && x.indexOf('__postset') < 0 && y.indexOf(' ') < 0) {
+ printErr(' SETUP_CALL_HANDLER(' + x + ');');
+ }
+ }
+ printErr('============================');
+ // end export code for CallHandlers.h
+ */
this.loaded = true;
},
@@ -420,6 +480,7 @@ var LibraryManager = {
},
isStubFunction: function(ident) {
+ if (SIDE_MODULE == 1) return false; // cannot eliminate these, as may be implement in the main module and imported by us
var libCall = LibraryManager.library[ident.substr(1)];
return typeof libCall === 'function' && libCall.toString().replace(/\s/g, '') === 'function(){}'
&& !(ident in Functions.implementedFunctions);
@@ -438,11 +499,16 @@ var PassManager = {
Types: Types,
Variables: Variables,
Functions: Functions,
- EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors)
+ EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS, // needed for asm.js global constructors (ctors)
+ Runtime: { GLOBAL_BASE: Runtime.GLOBAL_BASE }
}));
} else if (phase == 'funcs') {
print('\n//FORWARDED_DATA:' + JSON.stringify({
- Types: { preciseI64MathUsed: Types.preciseI64MathUsed },
+ Types: {
+ hasInlineJS: Types.hasInlineJS,
+ usesSIMD: Types.usesSIMD,
+ preciseI64MathUsed: Types.preciseI64MathUsed
+ },
Functions: {
blockAddresses: Functions.blockAddresses,
indexedFunctions: Functions.indexedFunctions,
@@ -455,6 +521,11 @@ var PassManager = {
print('\n//FORWARDED_DATA:' + JSON.stringify({
Functions: { tables: Functions.tables }
}));
+ } else if (phase == 'glue') {
+ print('\n//FORWARDED_DATA:' + JSON.stringify({
+ Functions: Functions,
+ EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS
+ }));
}
},
load: function(json) {
@@ -468,6 +539,7 @@ var PassManager = {
for (var i in data.Functions) {
Functions[i] = data.Functions[i];
}
+ EXPORTED_FUNCTIONS = data.EXPORTED_FUNCTIONS;
/*
print('\n//LOADED_DATA:' + phase + ':' + JSON.stringify({
Types: Types,
@@ -478,3 +550,7 @@ var PassManager = {
}
};
+var Framework = {
+ currItem: null
+};
+