aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc8
-rwxr-xr-xemscripten.py36
-rw-r--r--src/jsifier.js1
-rw-r--r--src/library.js3
-rw-r--r--src/library_gl.js11
-rw-r--r--src/modules.js41
-rw-r--r--src/settings.js4
-rwxr-xr-xtests/runner.py50
-rw-r--r--tools/shared.py1
9 files changed, 121 insertions, 34 deletions
diff --git a/emcc b/emcc
index 4315219a..d368a4e5 100755
--- a/emcc
+++ b/emcc
@@ -163,7 +163,10 @@ Options that are modified or new in %s include:
EMCC_OPTIMIZE_NORMALLY=1 (not recommended
unless you know what you are doing!)
-O2 As -O1, plus the relooper (loop recreation),
- plus LLVM -O2 optimizations
+ LLVM -O2 optimizations, and
+
+ -s ALIASING_FUNCTION_POINTERS=1
+
-O3 As -O2, plus dangerous optimizations that may
break the generated code! This adds
@@ -1032,6 +1035,9 @@ try:
shared.Settings.CORRECT_OVERFLOWS = 1
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
+ if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
+ logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
+
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
keep_llvm_debug = True # must keep debug info to do line-by-line operations
diff --git a/emscripten.py b/emscripten.py
index 35edc515..629bbe5f 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -288,6 +288,8 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
exported_implemented_functions.add(key)
for key, value in curr_forwarded_json['Functions']['unimplementedFunctions'].iteritems():
forwarded_json['Functions']['unimplementedFunctions'][key] = value
+ for key, value in curr_forwarded_json['Functions']['neededTables'].iteritems():
+ forwarded_json['Functions']['neededTables'][key] = value
if settings.get('ASM_JS'):
parts = pre.split('// ASM_LIBRARY FUNCTIONS\n')
@@ -302,18 +304,24 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
# calculations on merged forwarded data
forwarded_json['Functions']['indexedFunctions'] = {}
- i = 2
+ i = 2 # universal counter
if settings['ASM_JS']: i += 2*settings['RESERVED_FUNCTION_POINTERS']
+ table_counters = {} # table-specific counters
+ alias = settings['ASM_JS'] and settings['ALIASING_FUNCTION_POINTERS']
+ sig = None
for indexed in indexed_functions:
- #print >> sys.stderr, 'function indexing', indexed, i
- forwarded_json['Functions']['indexedFunctions'][indexed] = i # make sure not to modify this python object later - we use it in indexize
- i += 2
- forwarded_json['Functions']['nextIndex'] = i
- function_table_size = forwarded_json['Functions']['nextIndex']
- i = 1
- while i < function_table_size:
- i *= 2
- function_table_size = i
+ if alias:
+ sig = forwarded_json['Functions']['implementedFunctions'].get(indexed) or forwarded_json['Functions']['unimplementedFunctions'].get(indexed)
+ assert sig, indexed
+ if sig not in table_counters:
+ table_counters[sig] = 2 + 2*settings['RESERVED_FUNCTION_POINTERS']
+ curr = table_counters[sig]
+ table_counters[sig] += 2
+ else:
+ curr = i
+ i += 2
+ #print >> sys.stderr, 'function indexing', indexed, curr, sig
+ forwarded_json['Functions']['indexedFunctions'][indexed] = curr # make sure not to modify this python object later - we use it in indexize
def split_32(x):
x = int(x)
@@ -370,14 +378,6 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
pre_tables = last_forwarded_json['Functions']['tables']['pre']
del last_forwarded_json['Functions']['tables']['pre']
- # Find function table calls without function tables generated for them
- for funcs_js_item in funcs_js:
- for use in set(re.findall(r'{{{ FTM_[\w\d_$]+ }}}', funcs_js_item)):
- sig = use[8:len(use)-4]
- if sig not in last_forwarded_json['Functions']['tables']:
- if DEBUG: print >> sys.stderr, 'add empty function table', sig
- last_forwarded_json['Functions']['tables'][sig] = 'var FUNCTION_TABLE_' + sig + ' = [' + ','.join(['0']*function_table_size) + '];\n'
-
def make_table(sig, raw):
i = Counter.i
Counter.i += 1
diff --git a/src/jsifier.js b/src/jsifier.js
index 8270b443..3f52337f 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1463,6 +1463,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (!byPointerForced && !funcData.setjmpTable) {
// normal asm function pointer call
callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
+ Functions.neededTables[sig] = 1;
} else {
// This is a call through an invoke_*, either a forced one, or a setjmp-required one
// note: no need to update argsTypes at this point
diff --git a/src/library.js b/src/library.js
index 84071b68..d19fd531 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3482,7 +3482,8 @@ LibraryManager.library = {
} else if (oldObj.isRoot || oldObj.path == FS.currentPath) {
___setErrNo(ERRNO_CODES.EBUSY);
return -1;
- } else if (newObj.path && newObj.path.indexOf(oldObj.path) == 0) {
+ } else if (newObj.parentPath &&
+ newObj.parentPath.indexOf(oldObj.path) == 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
} else if (newObj.exists && newObj.object.isFolder) {
diff --git a/src/library_gl.js b/src/library_gl.js
index 0f6fb670..2e59f0d0 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -2880,7 +2880,7 @@ var LibraryGL = {
// Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos
glGenVertexArrays__deps: ['$GLEMulation'],
- glGenVertexArrays__sig: ['vii'],
+ glGenVertexArrays__sig: 'vii',
glGenVertexArrays: function(n, vaos) {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GLEmulation.vaos);
@@ -2895,7 +2895,7 @@ var LibraryGL = {
{{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
}
},
- glDeleteVertexArrays__sig: ['vii'],
+ glDeleteVertexArrays__sig: 'vii',
glDeleteVertexArrays: function(n, vaos) {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
@@ -2903,7 +2903,7 @@ var LibraryGL = {
if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
}
},
- glBindVertexArray__sig: ['vi'],
+ glBindVertexArray__sig: 'vi',
glBindVertexArray: function(vao) {
// undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
GLEmulation.currentVao = null; // make sure the commands we run here are not recorded
@@ -3331,7 +3331,10 @@ for (var item in LibraryGL) {
}
LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.concat(glFuncs);
LibraryGL.$GLEmulation__deps.push(function() {
- for (var func in Functions.getIndex.tentative) Functions.getIndex(func);
+ for (var func in Functions.getIndex.tentative) {
+ Functions.getIndex(func);
+ Functions.unimplementedFunctions[func] = LibraryGL[func.substr(1) + '__sig'];
+ }
});
if (FORCE_GL_EMULATION) {
diff --git a/src/modules.js b/src/modules.js
index 2775bfbd..e4093078 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -229,6 +229,8 @@ var Types = {
preciseI64MathUsed: (PRECISE_I64_MATH == 2)
};
+var firstTableIndex = (ASM_JS ? 2*RESERVED_FUNCTION_POINTERS : 0) + 2;
+
var Functions = {
// All functions that will be implemented in this file. Maps id to signature
implementedFunctions: {},
@@ -236,7 +238,9 @@ var Functions = {
unimplementedFunctions: {}, // library etc. functions that we need to index, maps id to signature
indexedFunctions: {},
- nextIndex: (ASM_JS ? 2*RESERVED_FUNCTION_POINTERS : 0) + 2, // Start at a non-0 (even, see below) value
+ nextIndex: firstTableIndex, // Start at a non-0 (even, see below) value
+ neededTables: set('v', 'vi', 'ii', 'iii'), // signatures that appeared (initialized with library stuff
+ // we always use), and we will need a function table for
blockAddresses: {}, // maps functions to a map of block labels to label ids
@@ -261,6 +265,7 @@ var Functions = {
if (!doNotCreate) this.indexedFunctions[ident] = 0; // tell python we need this indexized
return "'{{ FI_" + toNiceIdent(ident) + " }}'"; // something python will replace later
} else {
+ if (!singlePhase) return 'NO_INDEX'; // Should not index functions in post
var ret = this.indexedFunctions[ident];
if (!ret) {
if (doNotCreate) return '0';
@@ -278,25 +283,25 @@ var Functions = {
// Generate code for function indexing
generateIndexing: function() {
- var total = this.nextIndex;
- if (ASM_JS) total = ceilPowerOfTwo(total); // must be power of 2 for mask
- function emptyTable(sig) {
- return zeros(total);
- }
var tables = { pre: '' };
if (ASM_JS) {
- ['v', 'vi', 'ii', 'iii'].forEach(function(sig) { // add some default signatures that are used in the library
- tables[sig] = emptyTable(sig); // TODO: make them compact
+ keys(Functions.neededTables).forEach(function(sig) { // add some default signatures that are used in the library
+ tables[sig] = zeros(firstTableIndex);
});
}
for (var ident in this.indexedFunctions) {
var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] || LibraryManager.library[ident.substr(1) + '__sig'] : 'x';
assert(sig, ident);
- if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact
- tables[sig][this.indexedFunctions[ident]] = ident;
+ if (!tables[sig]) tables[sig] = zeros(firstTableIndex);
+ var index = this.indexedFunctions[ident];
+ for (var i = tables[sig].length; i < index; i++) {
+ tables[sig][i] = 0; // keep flat
+ }
+ tables[sig][index] = ident;
}
var generated = false;
var wrapped = {};
+ var maxTable = 0;
for (var t in tables) {
if (t == 'pre') continue;
generated = true;
@@ -351,6 +356,21 @@ var Functions = {
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;
+ }
+ }
+ // finalize table
var indices = table.toString().replace('"', '');
if (BUILD_AS_SHARED_LIB) {
// Shared libraries reuse the parent's function table.
@@ -428,6 +448,7 @@ var PassManager = {
indexedFunctions: Functions.indexedFunctions,
implementedFunctions: ASM_JS ? Functions.implementedFunctions : [],
unimplementedFunctions: Functions.unimplementedFunctions,
+ neededTables: Functions.neededTables
}
}));
} else if (phase == 'post') {
diff --git a/src/settings.js b/src/settings.js
index 8766277b..d3abb06e 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -142,6 +142,10 @@ var SAFE_DYNCALLS = 0; // Show stack traces on missing function pointer/virtual
var RESERVED_FUNCTION_POINTERS = 0; // In asm.js mode, we cannot simply add function pointers to
// function tables, so we reserve some slots for them.
+var ALIASING_FUNCTION_POINTERS = 0; // Whether to allow function pointers to alias if they have
+ // a different type. This can greatly decrease table sizes
+ // in asm.js, but can break code that compares function
+ // pointers across different types.
var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js
diff --git a/tests/runner.py b/tests/runner.py
index b2b1ff43..87eab8c6 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -3404,6 +3404,35 @@ Exiting setjmp function, level: 0, prev_jmp: -1
'''
self.do_run(src, 'z:1*', force_c=True)
+ def test_rename(self):
+ src = '''
+ #include <stdio.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <assert.h>
+
+ int main() {
+ int err;
+ FILE* fid;
+
+ err = mkdir("/foo", 0777);
+ err = mkdir("/bar", 0777);
+ fid = fopen("/foo/bar", "w+");
+ fclose(fid);
+
+ err = rename("/foo/bar", "/foo/bar2");
+ printf("%d\\n", err);
+
+ err = rename("/foo", "/foo/foo");
+ printf("%d\\n", err);
+
+ err = rename("/foo", "/bar/foo");
+ printf("%d\\n", err);
+ return 0;
+ }
+ '''
+ self.do_run(src, '0\n-1\n0\n', force_c=True)
+
def test_alloca_stack(self):
if self.emcc_args is None: return # too slow in other modes
@@ -7727,6 +7756,23 @@ void*:16
self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
+ assert 'asm2g' in test_modes
+ if self.run_name == 'asm2g':
+ results = {}
+ original = open('src.cpp.o.js').read()
+ results[Settings.ALIASING_FUNCTION_POINTERS] = len(original)
+ Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS
+ self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
+ final = open('src.cpp.o.js').read()
+ results[Settings.ALIASING_FUNCTION_POINTERS] = len(final)
+ open('original.js', 'w').write(original)
+ print results
+ assert results[1] < 0.99*results[0]
+ assert ' & 3]()' in original, 'small function table exists'
+ assert ' & 3]()' not in final, 'small function table does not exist'
+ assert ' & 255]()' not in original, 'big function table does not exist'
+ assert ' & 255]()' in final, 'big function table exists'
+
def test_gcc_unmangler(self):
Settings.NAMED_GLOBALS = 1 # test coverage for this
@@ -8513,6 +8559,10 @@ def process(filename):
self.do_run(src, '''Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.''')
generated = open('src.cpp.o.js').read()
assert 'jsCall' not in generated
+ Settings.RESERVED_FUNCTION_POINTERS = 1
+
+ Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip the test
+ self.do_run(src, '''Hello 7 from JS!''')
def test_scriptaclass(self):
if self.emcc_args is None: return self.skip('requires emcc')
diff --git a/tools/shared.py b/tools/shared.py
index cc056074..228f1253 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -676,6 +676,7 @@ class Settings:
Settings.EMIT_GENERATED_FUNCTIONS = 1
if opt_level >= 2:
Settings.RELOOP = 1
+ Settings.ALIASING_FUNCTION_POINTERS = 1
if opt_level >= 3:
# Aside from these, -O3 also runs closure compiler and llvm lto
Settings.FORCE_ALIGNED_MEMORY = 1