summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-02-25 19:23:56 -0500
committerAlon Zakai <alonzakai@gmail.com>2013-02-25 19:23:56 -0500
commit230c0e80dfcd44870bec3254c399db430f6e1d98 (patch)
tree745ec3b082adc222050b4d48a416d7a969dd148d /src
parent5a99d2567e76f257309cfd225876f3a5402e5f46 (diff)
parent9d4ef477a511ae4136c2d63e0150a4768cbd53ea (diff)
Merge branch 'incoming'
Conflicts: AUTHORS
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js7
-rw-r--r--src/compiler.js8
-rw-r--r--src/corruptionCheck.js98
-rw-r--r--src/jsifier.js26
-rw-r--r--src/library.js225
-rw-r--r--src/library_browser.js19
-rw-r--r--src/library_egl.js41
-rw-r--r--src/library_gl.js311
-rw-r--r--src/library_glut.js6
-rw-r--r--src/library_sdl.js14
-rw-r--r--src/parseTools.js63
-rw-r--r--src/preamble.js170
-rw-r--r--src/runtime.js31
-rw-r--r--src/settings.js40
14 files changed, 672 insertions, 387 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 1c53b76c..c930231f 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -223,6 +223,9 @@ function analyzer(data, sidePass) {
for (var i = 0; i < item.params.length; i++) {
if (item.params[i].type == 'i64') item.params[i].type = 'i32';
}
+ } else if (item.intertype == 'inttoptr') {
+ var input = item.params[0];
+ if (input.type == 'i64') input.type = 'i32'; // inttoptr can only care about 32 bits anyhow since pointers are 32-bit
}
if (isIllegalType(item.valueType) || isIllegalType(item.type)) {
isIllegal = true;
@@ -681,9 +684,9 @@ function analyzer(data, sidePass) {
params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null],
type: 'i32',
};
- if (j == 0 && isUnsignedOp(value.op) && sourceBits < 32) {
+ if (j == 0 && sourceBits < 32) {
// zext sign correction
- result.ident = makeSignOp(result.ident, 'i' + sourceBits, 'un', 1, 1);
+ result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1);
}
if (fraction != 0) {
var other = {
diff --git a/src/compiler.js b/src/compiler.js
index 25c306cf..0b43842e 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -160,12 +160,6 @@ if (SAFE_HEAP >= 2) {
SAFE_HEAP_LINES = set(SAFE_HEAP_LINES); // for fast checking
}
-if (PGO) { // by default, correct everything during PGO
- CORRECT_SIGNS = CORRECT_SIGNS || 1;
- CORRECT_OVERFLOWS = CORRECT_OVERFLOWS || 1;
- CORRECT_ROUNDINGS = CORRECT_ROUNDINGS || 1;
-}
-
EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS);
EXPORTED_GLOBALS = set(EXPORTED_GLOBALS);
EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST);
@@ -185,7 +179,7 @@ assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have
if (phase == 'pre') {
if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP ||
- !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !DISABLE_EXCEPTION_CATCHING) {
+ !SKIP_STACK_IN_SMALL || SAFE_HEAP || !DISABLE_EXCEPTION_CATCHING) {
print('// Note: Some Emscripten settings will significantly limit the speed of the generated code.');
} else {
print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code');
diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js
new file mode 100644
index 00000000..315f5cf0
--- /dev/null
+++ b/src/corruptionCheck.js
@@ -0,0 +1,98 @@
+
+// See settings.js, CORRUPTION_CHECK
+
+var CorruptionChecker = {
+ BUFFER_FACTOR: Math.round({{{ CORRUPTION_CHECK }}}),
+
+ ptrs: {},
+ checks: 0,
+ checkFrequency: 1,
+
+ init: function() {
+ this.realMalloc = _malloc;
+ _malloc = Module['_malloc'] = this.malloc;
+
+ this.realFree = _free;
+ _free = Module['_free'] = this.free;
+
+ if (typeof _realloc != 'undefined') {
+ this.realRealloc = _realloc;
+ _realloc = Module['_realloc'] = this.realloc;
+ }
+
+ __ATEXIT__.push({ func: function() {
+ Module.printErr('No corruption detected, ran ' + CorruptionChecker.checks + ' checks.');
+ } });
+ },
+ malloc: function(size) {
+ if (size <= 0) size = 1; // malloc(0) sometimes happens - just allocate a larger area, no harm
+ CorruptionChecker.checkAll();
+ size = (size+7)&(~7);
+ var allocation = CorruptionChecker.realMalloc(size*(1+2*CorruptionChecker.BUFFER_FACTOR));
+ var ptr = allocation + size*CorruptionChecker.BUFFER_FACTOR;
+ assert(!CorruptionChecker.ptrs[ptr]);
+ CorruptionChecker.ptrs[ptr] = size;
+ CorruptionChecker.fillBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR);
+ CorruptionChecker.fillBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR);
+ //Module.printErr('malloc ' + size + ' ==> ' + [ptr, allocation]);
+ return ptr;
+ },
+ free: function(ptr) {
+ if (!ptr) return; // ok to free(NULL), does nothing
+ CorruptionChecker.checkAll();
+ var size = CorruptionChecker.ptrs[ptr];
+ //Module.printErr('free ' + ptr + ' of size ' + size);
+ assert(size);
+ var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
+ //Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation);
+ delete CorruptionChecker.ptrs[ptr];
+ CorruptionChecker.realFree(allocation);
+ },
+ realloc: function(ptr, newSize) {
+ //Module.printErr('realloc ' + ptr + ' to size ' + newSize);
+ if (newSize <= 0) newSize = 1; // like in malloc
+ if (!ptr) return CorruptionChecker.malloc(newSize); // realloc(NULL, size) forwards to malloc according to the spec
+ var size = CorruptionChecker.ptrs[ptr];
+ assert(size);
+ var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
+ var newPtr = CorruptionChecker.malloc(newSize);
+ //Module.printErr('realloc ' + ptr + ' to size ' + newSize + ' is now ' + newPtr);
+ var newAllocation = newPtr + newSize*CorruptionChecker.BUFFER_FACTOR;
+ HEAPU8.set(HEAPU8.subarray(ptr, ptr + Math.min(size, newSize)), newPtr);
+ CorruptionChecker.free(ptr);
+ return newPtr;
+ },
+ canary: function(x) {
+ return (x&127) + 10;
+ },
+ fillBuffer: function(buffer, size) {
+ for (var x = buffer; x < buffer + size; x++) {
+ {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}};
+ }
+ },
+ checkBuffer: function(buffer, size) {
+ for (var x = buffer; x < buffer + size; x++) {
+ if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) {
+ assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]);
+ }
+ }
+ },
+ checkPtr: function(ptr) {
+ var size = CorruptionChecker.ptrs[ptr];
+ assert(size);
+ var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
+ CorruptionChecker.checkBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR);
+ CorruptionChecker.checkBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR);
+ },
+ checkAll: function(force) {
+ CorruptionChecker.checks++;
+ if (!force && CorruptionChecker.checks % CorruptionChecker.checkFrequency != 0) return;
+ //Module.printErr('checking for corruption ' + (CorruptionChecker.checks/CorruptionChecker.checkFrequency));
+ for (var ptr in CorruptionChecker.ptrs) {
+ CorruptionChecker.checkPtr(ptr, false);
+ }
+ },
+};
+
+CorruptionChecker.init();
+
diff --git a/src/jsifier.js b/src/jsifier.js
index 761a5fec..4af522b4 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -478,8 +478,7 @@ function JSify(data, functionsOnly, givenFunctions) {
ident = '_' + ident;
}
var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
- // redirected idents just need a var, but no value assigned to them - it would be unused
- var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';');
+ var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';');
if (ASM_JS) {
var sig = LibraryManager.library[ident.substr(1) + '__sig'];
if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) {
@@ -508,7 +507,7 @@ function JSify(data, functionsOnly, givenFunctions) {
item.JS = addFromLibrary(shortident);
} else {
item.JS = 'var ' + item.ident + '; // stub for ' + item.ident;
- if (WARN_ON_UNDEFINED_SYMBOLS) {
+ if (WARN_ON_UNDEFINED_SYMBOLS || ASM_JS) { // always warn on undefs in asm, since it breaks validation
warn('Unresolved symbol: ' + item.ident);
}
}
@@ -631,15 +630,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
- if (PROFILE) {
- func.JS += ' if (PROFILING) { '
- + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; '
- + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };'
- + 'PROFILING_NODE.calls++; '
- + 'var __profilingStartTime__ = Date.now() '
- + '}\n';
- }
-
if (true) { // TODO: optimize away when not needed
if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */';
func.JS += ' var label = 0;\n';
@@ -1145,12 +1135,6 @@ function JSify(data, functionsOnly, givenFunctions) {
});
makeFuncLineActor('return', function(item) {
var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n';
- if (PROFILE) {
- ret += 'if (PROFILING) { '
- + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; '
- + 'PROFILING_NODE = __parentProfilingNode__ '
- + '}\n';
- }
if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) {
ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n"
+ "INDENT = INDENT.substr(0, INDENT.length-2);\n";
@@ -1527,7 +1511,7 @@ function JSify(data, functionsOnly, givenFunctions) {
print('// ASM_LIBRARY FUNCTIONS');
function fix(f) { // fix indenting to not confuse js optimizer
f = f.substr(f.indexOf('f')); // remove initial spaces before 'function'
- f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last }
+ f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } XXX assumes function has multiple lines
return f + '}'; // add unindented } to match function
}
print(asmLibraryFunctions.map(fix).join('\n'));
@@ -1547,6 +1531,10 @@ function JSify(data, functionsOnly, givenFunctions) {
// This is the main 'post' pass. Print out the generated code that we have here, together with the
// rest of the output that we started to print out earlier (see comment on the
// "Final shape that will be created").
+ if (CORRUPTION_CHECK) {
+ assert(!ASM_JS); // cannot monkeypatch asm!
+ print(processMacros(read('corruptionCheck.js')));
+ }
if (PRECISE_I64_MATH && Types.preciseI64MathUsed) {
print(read('long.js'));
} else {
diff --git a/src/library.js b/src/library.js
index 1e026937..d0f73fdd 100644
--- a/src/library.js
+++ b/src/library.js
@@ -624,7 +624,12 @@ LibraryManager.library = {
// dirent.h
// ==========================================================================
- __dirent_struct_layout: Runtime.generateStructInfo(['d_ino', 'd_name', 'd_off', 'd_reclen', 'd_type'], '%struct.dirent'),
+ __dirent_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'd_ino'],
+ ['b1024', 'd_name'],
+ ['i32', 'd_off'],
+ ['i32', 'd_reclen'],
+ ['i32', 'd_type']]),
opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
opendir: function(dirname) {
// DIR *opendir(const char *dirname);
@@ -786,7 +791,9 @@ LibraryManager.library = {
// utime.h
// ==========================================================================
- __utimbuf_struct_layout: Runtime.generateStructInfo(['actime', 'modtime'], '%struct.utimbuf'),
+ __utimbuf_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'actime'],
+ ['i32', 'modtime']]),
utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__utimbuf_struct_layout'],
utime: function(path, times) {
// int utime(const char *path, const struct utimbuf *times);
@@ -877,23 +884,23 @@ LibraryManager.library = {
// ==========================================================================
__stat_struct_layout: Runtime.generateStructInfo([
- 'st_dev',
- 'st_ino',
- 'st_mode',
- 'st_nlink',
- 'st_uid',
- 'st_gid',
- 'st_rdev',
- 'st_size',
- 'st_atime',
- 'st_spare1',
- 'st_mtime',
- 'st_spare2',
- 'st_ctime',
- 'st_spare3',
- 'st_blksize',
- 'st_blocks',
- 'st_spare4'], '%struct.stat'),
+ ['i32', 'st_dev'],
+ ['i32', 'st_ino'],
+ ['i32', 'st_mode'],
+ ['i32', 'st_nlink'],
+ ['i32', 'st_uid'],
+ ['i32', 'st_gid'],
+ ['i32', 'st_rdev'],
+ ['i32', 'st_size'],
+ ['i32', 'st_atime'],
+ ['i32', 'st_spare1'],
+ ['i32', 'st_mtime'],
+ ['i32', 'st_spare2'],
+ ['i32', 'st_ctime'],
+ ['i32', 'st_spare3'],
+ ['i32', 'st_blksize'],
+ ['i32', 'st_blocks'],
+ ['i32', 'st_spare4']]),
stat__deps: ['$FS', '__stat_struct_layout'],
stat: function(path, buf, dontResolveLastLink) {
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
@@ -1065,17 +1072,17 @@ LibraryManager.library = {
// ==========================================================================
__statvfs_struct_layout: Runtime.generateStructInfo([
- 'f_bsize',
- 'f_frsize',
- 'f_blocks',
- 'f_bfree',
- 'f_bavail',
- 'f_files',
- 'f_ffree',
- 'f_favail',
- 'f_fsid',
- 'f_flag',
- 'f_namemax'], '%struct.statvfs'),
+ ['i32', 'f_bsize'],
+ ['i32', 'f_frsize'],
+ ['i32', 'f_blocks'],
+ ['i32', 'f_bfree'],
+ ['i32', 'f_bavail'],
+ ['i32', 'f_files'],
+ ['i32', 'f_ffree'],
+ ['i32', 'f_favail'],
+ ['i32', 'f_fsid'],
+ ['i32', 'f_flag'],
+ ['i32', 'f_namemax']]),
statvfs__deps: ['$FS', '__statvfs_struct_layout'],
statvfs: function(path, buf) {
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
@@ -1110,12 +1117,12 @@ LibraryManager.library = {
// ==========================================================================
__flock_struct_layout: Runtime.generateStructInfo([
- 'l_type',
- 'l_whence',
- 'l_start',
- 'l_len',
- 'l_pid',
- 'l_xxx'], '%struct.flock'),
+ ['i16', 'l_type'],
+ ['i16', 'l_whence'],
+ ['i32', 'l_start'],
+ ['i32', 'l_len'],
+ ['i16', 'l_pid'],
+ ['i16', 'l_xxx']]),
open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
open: function(path, oflag, varargs) {
// int open(const char *path, int oflag, ...);
@@ -1336,7 +1343,10 @@ LibraryManager.library = {
// poll.h
// ==========================================================================
- __pollfd_struct_layout: Runtime.generateStructInfo(['fd', 'events', 'revents'], '%struct.pollfd'),
+ __pollfd_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'fd'],
+ ['i16', 'events'],
+ ['i16', 'revents']]),
poll__deps: ['$FS', '__pollfd_struct_layout'],
poll: function(fds, nfds, timeout) {
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
@@ -2481,6 +2491,17 @@ LibraryManager.library = {
continue;
}
+ // TODO: Support strings like "%5c" etc.
+ if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') {
+ var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
+ argIndex += Runtime.getNativeFieldSize('void*');
+ fields++;
+ next = get();
+ {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}}
+ formatIndex += 2;
+ continue;
+ }
+
// remove whitespace
while (1) {
next = get();
@@ -4202,6 +4223,8 @@ LibraryManager.library = {
return 1;
},
+ arc4random: 'rand',
+
// ==========================================================================
// string.h
// ==========================================================================
@@ -4502,11 +4525,16 @@ LibraryManager.library = {
return 0;
},
+ memcmp__asm: 'true',
+ memcmp__sig: 'iiii',
memcmp: function(p1, p2, num) {
- for (var i = 0; i < num; i++) {
- var v1 = {{{ makeGetValue('p1', 'i', 'i8', 0, 1) }}};
- var v2 = {{{ makeGetValue('p2', 'i', 'i8', 0, 1) }}};
- if (v1 != v2) return v1 > v2 ? 1 : -1;
+ p1 = p1|0; p2 = p2|0; num = num|0;
+ var i = 0, v1 = 0, v2 = 0;
+ while ((i|0) < (num|0)) {
+ var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}};
+ var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}};
+ if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0;
+ i = (i+1)|0;
}
return 0;
},
@@ -4607,10 +4635,8 @@ LibraryManager.library = {
__strtok_state: 0,
strtok__deps: ['__strtok_state', 'strtok_r'],
+ strtok__postset: '___strtok_state = Runtime.staticAlloc(4);',
strtok: function(s, delim) {
- if (!___strtok_state) {
- ___strtok_state = _malloc(4);
- }
return _strtok_r(s, delim, ___strtok_state);
},
@@ -5070,6 +5096,7 @@ LibraryManager.library = {
},
__cxa_call_unexpected: function(exception) {
+ Module.printErr('Unexpected exception thrown, this is not properly supported - aborting');
ABORT = true;
throw exception;
},
@@ -5619,11 +5646,11 @@ LibraryManager.library = {
// ==========================================================================
__utsname_struct_layout: Runtime.generateStructInfo([
- 'sysname',
- 'nodename',
- 'release',
- 'version',
- 'machine'], '%struct.utsname'),
+ ['b32', 'sysname'],
+ ['b32', 'nodename'],
+ ['b32', 'release'],
+ ['b32', 'version'],
+ ['b32', 'machine']]),
uname__deps: ['__utsname_struct_layout'],
uname: function(name) {
// int uname(struct utsname *name);
@@ -5823,17 +5850,17 @@ LibraryManager.library = {
},
__tm_struct_layout: Runtime.generateStructInfo([
- 'tm_sec',
- 'tm_min',
- 'tm_hour',
- 'tm_mday',
- 'tm_mon',
- 'tm_year',
- 'tm_wday',
- 'tm_yday',
- 'tm_isdst',
- 'tm_gmtoff',
- 'tm_zone'], '%struct.tm'),
+ ['i32', 'tm_sec'],
+ ['i32', 'tm_min'],
+ ['i32', 'tm_hour'],
+ ['i32', 'tm_mday'],
+ ['i32', 'tm_mon'],
+ ['i32', 'tm_year'],
+ ['i32', 'tm_wday'],
+ ['i32', 'tm_yday'],
+ ['i32', 'tm_isdst'],
+ ['i32', 'tm_gmtoff'],
+ ['i32', 'tm_zone']]),
// Statically allocated time struct.
__tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
// Statically allocated timezone strings.
@@ -6032,7 +6059,9 @@ LibraryManager.library = {
// sys/time.h
// ==========================================================================
- __timespec_struct_layout: Runtime.generateStructInfo(['tv_sec', 'tv_nsec'], '%struct.timespec'),
+ __timespec_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'tv_sec'],
+ ['i32', 'tv_nsec']]),
// TODO: Implement these for real.
clock_gettime__deps: ['__timespec_struct_layout'],
clock_gettime: function(clk_id, tp) {
@@ -6089,10 +6118,10 @@ LibraryManager.library = {
// ==========================================================================
__tms_struct_layout: Runtime.generateStructInfo([
- 'tms_utime',
- 'tms_stime',
- 'tms_cutime',
- 'tms_cstime'], '%struct.tms'),
+ ['i32', 'tms_utime'],
+ ['i32', 'tms_stime'],
+ ['i32', 'tms_cutime'],
+ ['i32', 'tms_cstime']]),
times__deps: ['__tms_struct_layout', 'memset'],
times: function(buffer) {
// clock_t times(struct tms *buffer);
@@ -6614,7 +6643,9 @@ LibraryManager.library = {
// ==========================================================================
// TODO: Implement for real.
- __rlimit_struct_layout: Runtime.generateStructInfo(['rlim_cur', 'rlim_max'], '%struct.rlimit'),
+ __rlimit_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'rlim_cur'],
+ ['i32', 'rlim_max']]),
getrlimit__deps: ['__rlimit_struct_layout'],
getrlimit: function(resource, rlp) {
// int getrlimit(int resource, struct rlimit *rlp);
@@ -6630,22 +6661,22 @@ LibraryManager.library = {
// TODO: Implement for real. We just do time used, and no useful data
__rusage_struct_layout: Runtime.generateStructInfo([
- 'ru_utime',
- 'ru_stime',
- 'ru_maxrss',
- 'ru_ixrss',
- 'ru_idrss',
- 'ru_isrss',
- 'ru_minflt',
- 'ru_majflt',
- 'ru_nswap',
- 'ru_inblock',
- 'ru_oublock',
- 'ru_msgsnd',
- 'ru_msgrcv',
- 'ru_nsignals',
- 'ru_nvcsw',
- 'ru_nivcsw'], '%struct.rusage'),
+ ['i64', 'ru_utime'],
+ ['i64', 'ru_stime'],
+ ['i32', 'ru_maxrss'],
+ ['i32', 'ru_ixrss'],
+ ['i32', 'ru_idrss'],
+ ['i32', 'ru_isrss'],
+ ['i32', 'ru_minflt'],
+ ['i32', 'ru_majflt'],
+ ['i32', 'ru_nswap'],
+ ['i32', 'ru_inblock'],
+ ['i32', 'ru_oublock'],
+ ['i32', 'ru_msgsnd'],
+ ['i32', 'ru_msgrcv'],
+ ['i32', 'ru_nsignals'],
+ ['i32', 'ru_nvcsw'],
+ ['i32', 'ru_nivcsw']]),
getrusage__deps: ['__rusage_struct_layout'],
getrusage: function(resource, rlp) {
// %struct.timeval = type { i32, i32 }
@@ -7289,36 +7320,6 @@ LibraryManager.library = {
emscripten_random: function() {
return Math.random();
},
-
- $Profiling: {
- max_: 0,
- times: null,
- invalid: 0,
- dump: function() {
- if (Profiling.invalid) {
- Module.printErr('Invalid # of calls to Profiling begin and end!');
- return;
- }
- Module.printErr('Profiling data:')
- for (var i = 0; i < Profiling.max_; i++) {
- Module.printErr('Block ' + i + ': ' + Profiling.times[i]);
- }
- }
- },
- EMSCRIPTEN_PROFILE_INIT__deps: ['$Profiling'],
- EMSCRIPTEN_PROFILE_INIT: function(max_) {
- Profiling.max_ = max_;
- Profiling.times = new Array(max_);
- for (var i = 0; i < max_; i++) Profiling.times[i] = 0;
- },
- EMSCRIPTEN_PROFILE_BEGIN__inline: function(id) {
- return 'Profiling.times[' + id + '] -= Date.now();'
- + 'Profiling.invalid++;'
- },
- EMSCRIPTEN_PROFILE_END__inline: function(id) {
- return 'Profiling.times[' + id + '] += Date.now();'
- + 'Profiling.invalid--;'
- }
};
function autoAddDeps(object, name) {
diff --git a/src/library_browser.js b/src/library_browser.js
index d16fbc0b..5b19a360 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -68,6 +68,7 @@ mergeInto(LibraryManager.library, {
function getMimetype(name) {
return {
'jpg': 'image/jpeg',
+ 'jpeg': 'image/jpeg',
'png': 'image/png',
'bmp': 'image/bmp',
'ogg': 'audio/ogg',
@@ -81,7 +82,7 @@ mergeInto(LibraryManager.library, {
var imagePlugin = {};
imagePlugin['canHandle'] = function(name) {
- return name.substr(-4) in { '.jpg': 1, '.png': 1, '.bmp': 1 };
+ return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
};
imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
var b = null;
@@ -123,7 +124,7 @@ mergeInto(LibraryManager.library, {
var audioPlugin = {};
audioPlugin['canHandle'] = function(name) {
- return name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
+ return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
};
audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
var done = false;
@@ -200,8 +201,18 @@ mergeInto(LibraryManager.library, {
return null;
}
#endif
+ var ctx;
try {
- var ctx = canvas.getContext(useWebGL ? 'experimental-webgl' : '2d');
+ if (useWebGL) {
+ ctx = canvas.getContext('experimental-webgl', {
+ alpha: false,
+#if GL_TESTING
+ preserveDrawingBuffer: true
+#endif
+ });
+ } else {
+ ctx = canvas.getContext('2d');
+ }
if (!ctx) throw ':(';
} catch (e) {
Module.print('Could not create canvas - ' + e);
@@ -262,7 +273,7 @@ mergeInto(LibraryManager.library, {
}
return ctx;
},
-
+ destroyContext: function(canvas, useWebGL, setInModule) {},
requestFullScreen: function() {
var canvas = Module['canvas'];
function fullScreenChange() {
diff --git a/src/library_egl.js b/src/library_egl.js
index a9eb37dd..271ea29e 100644
--- a/src/library_egl.js
+++ b/src/library_egl.js
@@ -83,6 +83,17 @@ var LibraryEGL = {
return 1;
},
+// EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy);
+ eglTerminate: function(display) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ // TODO: Tear down EGL here. Currently a no-op since we don't need to actually do anything here for the browser.
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1;
+ },
+
// EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
eglGetConfigs: function(display, configs, config_size, numConfigs) {
return EGL.chooseConfig(display, 0, configs, config_size, numConfigs);
@@ -225,6 +236,20 @@ var LibraryEGL = {
return 62006; /* Magic ID for Emscripten 'default surface' */
},
+ // EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface);
+ eglDestroySurface: function(display, surface) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+ if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) {
+ EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);
+ return 1;
+ }
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 1; /* Magic ID for Emscripten 'default surface' */
+ },
+
eglCreateContext__deps: ['glutCreateWindow', '$GL'],
// EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
@@ -234,7 +259,21 @@ var LibraryEGL = {
return 0;
}
- _glutCreateWindow();
+ EGL.windowID = _glutCreateWindow();
+ EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ return 62004; // Magic ID for Emscripten EGLContext
+ },
+
+ eglDestroyContext__deps: ['glutDestroyWindow', '$GL'],
+
+ // EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext context);
+ eglDestroyContext: function(display, context) {
+ if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
+ EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
+ return 0;
+ }
+
+ _glutDestroyWindow(EGL.windowID);
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
return 62004; // Magic ID for Emscripten EGLContext
},
diff --git a/src/library_gl.js b/src/library_gl.js
index f7966dab..027b2841 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -408,7 +408,19 @@ var LibraryGL = {
},
glReadPixels: function(x, y, width, height, format, type, pixels) {
- Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels));
+ assert(type == 0x1401 /* GL_UNSIGNED_BYTE */);
+ var sizePerPixel;
+ switch (format) {
+ case 0x1907 /* GL_RGB */:
+ sizePerPixel = 3;
+ break;
+ case 0x1908 /* GL_RGBA */:
+ sizePerPixel = 4;
+ break;
+ default: throw 'unsupported glReadPixels format';
+ }
+ var totalSize = width*height*sizePerPixel;
+ Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels, pixels + totalSize));
},
glBindTexture: function(target, texture) {
@@ -908,11 +920,9 @@ var LibraryGL = {
},
glIsProgram: function(program) {
- var fb = GL.programs[program];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isProgram(fb);
+ var program = GL.programs[program];
+ if (!program) return 0;
+ return Module.ctx.isProgram(program);
},
glBindAttribLocation__sig: 'viii',
@@ -983,6 +993,11 @@ var LibraryGL = {
fogMode: 0x0800, // GL_EXP
fogEnabled: false,
+ // VAO support
+ vaos: [],
+ currentVao: null,
+ enabledVertexAttribArrays: {}, // helps with vao cleanups
+
init: function() {
GLEmulation.fogColor = new Float32Array(4);
@@ -1008,6 +1023,11 @@ var LibraryGL = {
if (cap == 0x0B60 /* GL_FOG */) {
GLEmulation.fogEnabled = true;
return;
+ } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
+ // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
+ // it by forwarding to glEnableClientState
+ _glEnableClientState(cap);
+ return;
} else if (!(cap in validCapabilities)) {
return;
}
@@ -1018,6 +1038,11 @@ var LibraryGL = {
if (cap == 0x0B60 /* GL_FOG */) {
GLEmulation.fogEnabled = false;
return;
+ } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
+ // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
+ // it by forwarding to glDisableClientState
+ _glDisableClientState(cap);
+ return;
} else if (!(cap in validCapabilities)) {
return;
}
@@ -1052,6 +1077,51 @@ var LibraryGL = {
return;
}
case 0x8871: pname = Module.ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS
+ case 0x807A: { // GL_VERTEX_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x807B: { // GL_VERTEX_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x807C: { // GL_VERTEX_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8081: { // GL_COLOR_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8082: { // GL_COLOR_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x8083: { // GL_COLOR_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
}
glGetIntegerv(pname, params);
};
@@ -1267,8 +1337,13 @@ var LibraryGL = {
glBindBuffer(target, buffer);
if (target == Module.ctx.ARRAY_BUFFER) {
GL.currArrayBuffer = buffer;
+ if (GLEmulation.currentVao) {
+ assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
+ GLEmulation.currentVao.arrayBuffer = buffer;
+ }
} else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) {
GL.currElementArrayBuffer = buffer;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
}
};
@@ -1312,6 +1387,28 @@ var LibraryGL = {
}
glHint(target, mode);
};
+
+ var glEnableVertexAttribArray = _glEnableVertexAttribArray;
+ _glEnableVertexAttribArray = function(index) {
+ glEnableVertexAttribArray(index);
+ GLEmulation.enabledVertexAttribArrays[index] = 1;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
+ };
+
+ var glDisableVertexAttribArray = _glDisableVertexAttribArray;
+ _glDisableVertexAttribArray = function(index) {
+ glDisableVertexAttribArray(index);
+ delete GLEmulation.enabledVertexAttribArrays[index];
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
+ };
+
+ var glVertexAttribPointer = _glVertexAttribPointer;
+ _glVertexAttribPointer = function(index, size, type, normalized, stride, pointer) {
+ glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
+ GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
+ }
+ };
},
getProcAddress: function(name) {
@@ -1375,6 +1472,9 @@ var LibraryGL = {
case 'glIsFramebuffer': ret = {{{ Functions.getIndex('_glIsFramebuffer', true) }}}; break;
case 'glCheckFramebufferStatus': ret = {{{ Functions.getIndex('_glCheckFramebufferStatus', true) }}}; break;
case 'glRenderbufferStorage': ret = {{{ Functions.getIndex('_glRenderbufferStorage', true) }}}; break;
+ case 'glGenVertexArrays': ret = {{{ Functions.getIndex('_glGenVertexArrays', true) }}}; break;
+ case 'glDeleteVertexArrays': ret = {{{ Functions.getIndex('_glDeleteVertexArrays', true) }}}; break;
+ case 'glBindVertexArray': ret = {{{ Functions.getIndex('_glBindVertexArray', true) }}}; break;
}
if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name);
return ret;
@@ -1427,6 +1527,20 @@ var LibraryGL = {
assert(id == 0);
},
+ glGetPointerv: function(name, p) {
+ var attribute;
+ switch(name) {
+ case 0x808E: // GL_VERTEX_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
+ case 0x8090: // GL_COLOR_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
+ case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; break;
+ default: throw 'TODO: glGetPointerv for ' + name;
+ }
+ {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
+ },
+
// GL Immediate mode
$GLImmediate__postset: 'GL.immediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });',
@@ -1501,7 +1615,7 @@ var LibraryGL = {
},
// Temporary buffers
- MAX_TEMP_BUFFER_SIZE: 2*1024*1024,
+ MAX_TEMP_BUFFER_SIZE: {{{ GL_MAX_TEMP_BUFFER_SIZE }}},
tempBufferIndexLookup: null,
tempVertexBuffers: null,
tempIndexBuffers: null,
@@ -1784,27 +1898,35 @@ var LibraryGL = {
}
// If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
- // XXX We use some heuristics here, and this may not work in all cases. Try disabling this if you
- // have odd glitches (by setting canSkip always to 0, or even cleaning up the renderer right
- // after rendering)
+ // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
+ // have odd glitches
+#if GL_UNSAFE_OPTS
var lastRenderer = GL.immediate.lastRenderer;
var canSkip = this == lastRenderer &&
arrayBuffer == GL.immediate.lastArrayBuffer &&
(GL.currProgram || this.program) == GL.immediate.lastProgram &&
!GL.immediate.matricesModified;
if (!canSkip && lastRenderer) lastRenderer.cleanup();
+#endif
if (!GL.currArrayBuffer) {
// Bind the array buffer and upload data after cleaning up the previous renderer
+#if GL_UNSAFE_OPTS
+ // Potentially unsafe, since lastArrayBuffer might not reflect the true array buffer in code that mixes immediate/non-immediate
if (arrayBuffer != GL.immediate.lastArrayBuffer) {
+#endif
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, arrayBuffer);
+#if GL_UNSAFE_OPTS
}
+#endif
Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, start, GL.immediate.vertexData.subarray(start >> 2, end >> 2));
}
+#if GL_UNSAFE_OPTS
if (canSkip) return;
GL.immediate.lastRenderer = this;
GL.immediate.lastArrayBuffer = arrayBuffer;
GL.immediate.lastProgram = GL.currProgram || this.program;
GL.immediate.matricesModified = false;
+#endif
if (!GL.currProgram) {
Module.ctx.useProgram(this.program);
@@ -1880,9 +2002,11 @@ var LibraryGL = {
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
}
+#if GL_UNSAFE_OPTS
GL.immediate.lastRenderer = null;
GL.immediate.lastArrayBuffer = null;
GL.immediate.lastProgram = null;
+#endif
GL.immediate.matricesModified = true;
}
};
@@ -1997,22 +2121,61 @@ var LibraryGL = {
#endif
if (attribute.stride) stride = attribute.stride;
}
-
- var bytes = 0;
- for (var i = 0; i < attributes.length; i++) {
- var attribute = attributes[i];
- if (!attribute) break;
- attribute.offset = attribute.pointer - start;
- if (attribute.offset > bytes) { // ensure we start where we should
- assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
- bytes += attribute.offset - bytes;
+ var bytes = 0; // total size in bytes
+ if (!stride && !beginEnd) {
+ // beginEnd can not have stride in the attributes, that is fine. otherwise,
+ // no stride means that all attributes are in fact packed. to keep the rest of
+ // our emulation code simple, we perform unpacking/restriding here. this adds overhead, so
+ // it is a good idea to not hit this!
+#if ASSERTIONS
+ Runtime.warnOnce('Unpacking/restriding attributes, this is not fast');
+#endif
+ if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.immediate.MAX_TEMP_BUFFER_SIZE);
+ start = GL.immediate.restrideBuffer;
+#if ASSERTIONS
+ assert(start % 4 == 0);
+#endif
+ // calculate restrided offsets and total size
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ var size = attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
+ if (size % 4 != 0) size += 4 - (size % 4); // align everything
+ attribute.offset = bytes;
+ bytes += size;
+ }
+#if ASSERTIONS
+ assert(count*bytes <= GL.immediate.MAX_TEMP_BUFFER_SIZE);
+#endif
+ // copy out the data (we need to know the stride for that, and define attribute.pointer
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ var size4 = Math.floor((attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot])/4);
+ for (var j = 0; j < count; j++) {
+ for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible
+ HEAP32[((start + attribute.offset + bytes*j)>>2) + k] = HEAP32[(attribute.pointer>>2) + j*size4 + k];
+ }
+ }
+ attribute.pointer = start + attribute.offset;
+ }
+ } else {
+ // normal situation, everything is strided and in the same buffer
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ attribute.offset = attribute.pointer - start;
+ if (attribute.offset > bytes) { // ensure we start where we should
+ assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
+ bytes += attribute.offset - bytes;
+ }
+ bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
+ if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
+ }
+ assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size
+ if (bytes < stride) { // ensure the size is that of the stride
+ bytes = stride;
}
- bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
- if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
- }
- assert(stride == 0 || bytes <= stride);
- if (bytes < stride) { // ensure the size is that of the stride
- bytes = stride;
}
GL.immediate.stride = bytes;
@@ -2026,6 +2189,9 @@ var LibraryGL = {
},
flush: function(numProvidedIndexes, startIndex, ptr) {
+#if ASSERTIONS
+ assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
+#endif
startIndex = startIndex || 0;
ptr = ptr || 0;
@@ -2085,6 +2251,10 @@ var LibraryGL = {
if (emulatedElementArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
}
+
+#if GL_UNSAFE_OPTS == 0
+ renderer.cleanup();
+#endif
}
},
@@ -2302,6 +2472,7 @@ var LibraryGL = {
var attrib;
switch(cap) {
case 0x8078: // GL_TEXTURE_COORD_ARRAY
+ case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break;
case 0x8074: // GL_VERTEX_ARRAY
attrib = GL.immediate.VERTEX; break;
@@ -2310,14 +2481,19 @@ var LibraryGL = {
case 0x8076: // GL_COLOR_ARRAY
attrib = GL.immediate.COLOR; break;
default:
- throw 'unhandled clientstate: ' + cap;
+#if ASSERTIONS
+ Module.printErr('WARNING: unhandled clientstate: ' + cap);
+#endif
+ return;
}
if (disable && GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = false;
GL.immediate.totalEnabledClientAttributes--;
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
} else if (!disable && !GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = true;
GL.immediate.totalEnabledClientAttributes++;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
}
GL.immediate.modifiedClientAttributes = true;
},
@@ -2344,6 +2520,63 @@ var LibraryGL = {
GL.immediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
},
+ // 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: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = GL.getNewId(GLEmulation.vaos);
+ GLEmulation.vaos[id] = {
+ id: id,
+ arrayBuffer: 0,
+ elementArrayBuffer: 0,
+ enabledVertexAttribArrays: {},
+ vertexAttribPointers: {},
+ enabledClientStates: {},
+ };
+ {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
+ }
+ },
+ glDeleteVertexArrays__sig: ['vii'],
+ glDeleteVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
+ GLEmulation.vaos[id] = null;
+ if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
+ }
+ },
+ 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
+ if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup();
+ _glBindBuffer(Module.ctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound?
+ _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, 0);
+ for (var vaa in GLEmulation.enabledVertexAttribArrays) {
+ Module.ctx.disableVertexAttribArray(vaa);
+ }
+ GLEmulation.enabledVertexAttribArrays = {};
+ GL.immediate.enabledClientAttributes = [0, 0];
+ GL.immediate.totalEnabledClientAttributes = 0;
+ GL.immediate.modifiedClientAttributes = true;
+ if (vao) {
+ // replay vao
+ var info = GLEmulation.vaos[vao];
+ _glBindBuffer(Module.ctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding?
+ _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
+ for (var vaa in info.enabledVertexAttribArrays) {
+ _glEnableVertexAttribArray(vaa);
+ }
+ for (var vaa in info.vertexAttribPointers) {
+ _glVertexAttribPointer.apply(null, info.vertexAttribPointers[vaa]);
+ }
+ for (var attrib in info.enabledClientStates) {
+ _glEnableClientState(attrib|0);
+ }
+ GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded
+ }
+ },
+
// OpenGL Immediate Mode matrix routines.
// Note that in the future we might make these available only in certain modes.
glMatrixMode__deps: ['$GL', '$GLImmediateSetup', '$GLEmulation'], // emulation is not strictly needed, this is a workaround
@@ -2434,6 +2667,7 @@ var LibraryGL = {
GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix],
GL.immediate.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal));
},
+ glFrustumf: 'glFrustum',
glOrtho: function(left, right, bottom, top_, nearVal, farVal) {
GL.immediate.matricesModified = true;
@@ -2464,8 +2698,9 @@ var LibraryGL = {
gluPerspective: function(fov, aspect, near, far) {
GL.immediate.matricesModified = true;
- GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix],
- GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far, GL.immediate.currentMatrix));
+ GL.immediate.matrix[GL.immediate.currentMatrix] =
+ GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far,
+ GL.immediate.matrix[GL.immediate.currentMatrix]);
},
gluLookAt: function(ex, ey, ez, cx, cy, cz, ux, uy, uz) {
@@ -2534,8 +2769,8 @@ var LibraryGL = {
glTexGeni: function() { throw 'glTexGeni: TODO' },
glTexGenfv: function() { throw 'glTexGenfv: TODO' },
- glTexEnvi: function() { throw 'glTexEnvi: TODO' },
- glTexEnvfv: function() { throw 'glTexEnvfv: TODO' },
+ glTexEnvi: function() { Runtime.warnOnce('glTexEnvi: TODO') },
+ glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') },
glTexImage1D: function() { throw 'glTexImage1D: TODO' },
glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
@@ -2548,6 +2783,22 @@ var LibraryGL = {
glVertexAttribPointer__sig: 'viiiiii',
glCheckFramebufferStatus__sig: 'ii',
glRenderbufferStorage__sig: 'viiii',
+
+ // Open GLES1.1 compatibility
+ glGenFramebuffersOES : 'glGenFramebuffers',
+ glGenRenderbuffersOES : 'glGenRenderbuffers',
+ glBindFramebufferOES : 'glBindFramebuffer',
+ glBindRenderbufferOES : 'glBindRenderbuffer',
+ glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv',
+ glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer',
+ glRenderbufferStorageOES : 'glRenderbufferStorage',
+ glCheckFramebufferStatusOES : 'glCheckFramebufferStatus',
+ glDeleteFramebuffersOES : 'glDeleteFramebuffers',
+ glDeleteRenderbuffersOES : 'glDeleteRenderbuffers',
+ glGenVertexArraysOES: 'glGenVertexArrays',
+ glDeleteVertexArraysOES: 'glDeleteVertexArrays',
+ glBindVertexArrayOES: 'glBindVertexArray',
+ glFramebufferTexture2DOES: 'glFramebufferTexture2D'
};
// Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name
diff --git a/src/library_glut.js b/src/library_glut.js
index 2e662698..bb4dfefa 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -381,6 +381,12 @@ var LibraryGLUT = {
return 1;
},
+ glutDestroyWindow__deps: ['$Browser'],
+ glutDestroyWindow: function(name) {
+ Module.ctx = Browser.destroyContext(Module['canvas'], true, true);
+ return 1;
+ },
+
glutReshapeWindow__deps: ['$GLUT', 'glutPostRedisplay'],
glutReshapeWindow: function(width, height) {
GLUT.cancelFullScreen();
diff --git a/src/library_sdl.js b/src/library_sdl.js
index cfab6410..e02e1e62 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -607,9 +607,11 @@ var LibrarySDL = {
SDL_Init: function(what) {
SDL.startTime = Date.now();
// capture all key events. we just keep down and up, but also capture press to prevent default actions
- document.onkeydown = SDL.receiveEvent;
- document.onkeyup = SDL.receiveEvent;
- document.onkeypress = SDL.receiveEvent;
+ if (!Module['doNotCaptureKeyboard']) {
+ document.onkeydown = SDL.receiveEvent;
+ document.onkeyup = SDL.receiveEvent;
+ document.onkeypress = SDL.receiveEvent;
+ }
window.onunload = SDL.receiveEvent;
SDL.keyboardState = _malloc(0x10000);
_memset(SDL.keyboardState, 0, 0x10000);
@@ -1077,7 +1079,9 @@ var LibrarySDL = {
}
var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
var surfData = SDL.surfaces[surf];
+ surfData.ctx.globalCompositeOperation = "copy";
surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height);
+ surfData.ctx.globalCompositeOperation = "source-over";
// XXX SDL does not specify that loaded images must have available pixel data, in fact
// there are cases where you just want to blit them, so you just need the hardware
// accelerated version. However, code everywhere seems to assume that the pixels
@@ -1176,6 +1180,10 @@ var LibrarySDL = {
// SDL Mixer
+ Mix_Init: function(flags) {
+ return 8; /* MIX_INIT_OGG */
+ },
+
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
SDL.allocateChannels(32);
// Just record the values for a later call to Mix_QuickLoad_RAW
diff --git a/src/parseTools.js b/src/parseTools.js
index e081d0de..357d33a1 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -103,6 +103,11 @@ function isNiceIdent(ident, loose) {
}
}
+function isJSVar(ident) {
+ return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident);
+
+}
+
function isStructPointerType(type) {
// This test is necessary for clang - in llvm-gcc, we
// could check for %struct. The downside is that %1 can
@@ -976,12 +981,6 @@ function checkSafeHeap() {
return SAFE_HEAP === 1 || checkSpecificSafeHeap();
}
-if (ASM_JS) {
- var hexMemoryMask = '0x' + (TOTAL_MEMORY-1).toString(16);
- var decMemoryMask = (TOTAL_MEMORY-1).toString();
- var memoryMask = hexMemoryMask.length <= decMemoryMask.length ? hexMemoryMask : decMemoryMask;
-}
-
function getHeapOffset(offset, type, forceAsm) {
if (USE_TYPED_ARRAYS !== 2) {
return offset;
@@ -991,11 +990,11 @@ function getHeapOffset(offset, type, forceAsm) {
}
var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2;
offset = '(' + offset + ')';
- if (ASM_JS && (phase == 'funcs' || forceAsm)) offset = '(' + offset + '&' + memoryMask + ')';
if (shifts != 0) {
return '(' + offset + '>>' + shifts + ')';
} else {
- return offset;
+ // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int
+ return isJSVar(offset) ? offset : '(' + offset + '|0)';
}
}
}
@@ -1045,20 +1044,6 @@ function asmCoercion(value, type, signedness) {
}
}
-var TWO_TWENTY = Math.pow(2, 20);
-
-function asmMultiplyI32(a, b) {
- // special-case: there is no integer multiply in asm, because there is no true integer
- // multiply in JS. While we wait for Math.imul, do double multiply
- if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY)) {
- return '(((' + a + ')*(' + b + '))&-1)'; // small enough to emit directly as a multiply
- }
- if (USE_MATH_IMUL) {
- return 'Math.imul(' + a + ',' + b + ')';
- }
- return '(~~(+((' + a + ')|0) * +((' + b + ')|0)))';
-}
-
function asmFloatToInt(x) {
return '(~~(' + x + '))';
}
@@ -1070,7 +1055,6 @@ function makeGetTempDouble(i, type, forSet) { // get an aliased part of the temp
var ptr = getFastValue('tempDoublePtr', '+', Runtime.getNativeTypeSize(type)*i);
var offset;
if (type == 'double') {
- if (ASM_JS) ptr = '(' + ptr + ')&' + memoryMask;
offset = '(' + ptr + ')>>3';
} else {
offset = getHeapOffset(ptr, type);
@@ -1153,8 +1137,8 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
}
-function makeGetValueAsm(ptr, pos, type) {
- return makeGetValue(ptr, pos, type, null, null, null, null, null, true);
+function makeGetValueAsm(ptr, pos, type, unsigned) {
+ return makeGetValue(ptr, pos, type, null, unsigned, null, null, null, true);
}
function indexizeFunctions(value, type) {
@@ -1372,9 +1356,11 @@ function makeHEAPView(which, start, end) {
var PLUS_MUL = set('+', '*');
var MUL_DIV = set('*', '/');
var PLUS_MINUS = set('+', '-');
+var TWO_TWENTY = Math.pow(2, 20);
// Given two values and an operation, returns the result of that operation.
// Tries to do as much as possible at compile time.
+// Leaves overflows etc. unhandled, *except* for integer multiply, in order to be efficient with Math.imul
function getFastValue(a, op, b, type) {
a = a.toString();
b = b.toString();
@@ -1410,8 +1396,14 @@ function getFastValue(a, op, b, type) {
return '(' + a + '<<' + shifts + ')';
}
}
- if (ASM_JS && !(type in Runtime.FLOAT_TYPES)) {
- return asmMultiplyI32(a, b); // unoptimized multiply, do it using asm.js's special multiply operation
+ if (!(type in Runtime.FLOAT_TYPES)) {
+ // if guaranteed small enough to not overflow into a double, do a normal multiply
+ var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes
+ // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there
+ if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
+ return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this
+ }
+ return 'Math.imul(' + a + ',' + b + ')';
}
} else {
if (a == '0') {
@@ -1717,9 +1709,7 @@ function handleOverflow(text, bits) {
if (!bits) return text;
var correct = correctOverflows();
warnOnce(!correct || bits <= 32, 'Cannot correct overflows of this many bits: ' + bits);
- if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow() && !PGO) + (
- PGO ? ', "' + Debugging.getIdentifier() + '"' : ''
- ) + ')';
+ if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow()) + ')';
if (!correct) return text;
if (bits == 32) {
return '((' + text + ')|0)';
@@ -1827,9 +1817,7 @@ function makeSignOp(value, type, op, force, ignore) {
var bits, full;
if (type in Runtime.INT_TYPES) {
bits = parseInt(type.substr(1));
- full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign() && !PGO)) + (
- PGO ? ', "' + (ignore ? '' : Debugging.getIdentifier()) + '"' : ''
- ) + ')';
+ full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign())) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
if (isNumber(value)) {
return eval(full).toString();
@@ -2142,14 +2130,7 @@ function processMathop(item) {
case 'add': return handleOverflow(getFastValue(idents[0], '+', idents[1], item.type), bits);
case 'sub': return handleOverflow(getFastValue(idents[0], '-', idents[1], item.type), bits);
case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, op[0] === 's');
- case 'mul': {
- if (bits == 32 && PRECISE_I32_MUL) {
- Types.preciseI64MathUsed = true;
- return '(i64Math' + (ASM_JS ? '_' : '.') + 'multiply(' + asmCoercion(idents[0], 'i32') + ',0,' + asmCoercion(idents[1], 'i32') + ',0),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')';
- } else {
- return '((' +getFastValue(idents[0], '*', idents[1], item.type) + ')&-1)'; // force a non-eliminatable coercion here, to prevent a double result from leaking
- }
- }
+ case 'mul': return getFastValue(idents[0], '*', idents[1], item.type); // overflow handling is already done in getFastValue for '*'
case 'urem': case 'srem': return getFastValue(idents[0], '%', idents[1], item.type);
case 'or': {
if (bits > 32) {
diff --git a/src/preamble.js b/src/preamble.js
index aab50e9a..a7731e7f 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -150,52 +150,6 @@ function SAFE_HEAP_COPY_HISTORY(dest, src) {
//==========================================
#endif
-var CorrectionsMonitor = {
-#if PGO
- MAX_ALLOWED: Infinity,
-#else
- MAX_ALLOWED: 0, // XXX
-#endif
- corrections: 0,
- sigs: {},
-
- note: function(type, succeed, sig) {
- if (!succeed) {
- this.corrections++;
- if (this.corrections >= this.MAX_ALLOWED) abort('\n\nToo many corrections!');
- }
-#if PGO
- if (!sig)
- sig = (new Error().stack).toString().split('\n')[2].split(':').slice(-1)[0]; // Spidermonkey-specific FIXME
- sig = type + '|' + sig;
- if (!this.sigs[sig]) {
- //Module.print('Correction: ' + sig);
- this.sigs[sig] = [0, 0]; // fail, succeed
- }
- this.sigs[sig][succeed ? 1 : 0]++;
-#endif
- },
-
- print: function() {
-#if PGO
- var items = [];
- for (var sig in this.sigs) {
- items.push({
- sig: sig,
- fails: this.sigs[sig][0],
- succeeds: this.sigs[sig][1],
- total: this.sigs[sig][0] + this.sigs[sig][1]
- });
- }
- items.sort(function(x, y) { return y.total - x.total; });
- for (var i = 0; i < items.length; i++) {
- var item = items[i];
- Module.print(item.sig + ' : ' + item.total + ' hits, %' + (Math.ceil(100*item.fails/item.total)) + ' failures');
- }
-#endif
- }
-};
-
#if CHECK_OVERFLOWS
//========================================
// Debugging tools - Mathop overflows
@@ -207,24 +161,20 @@ function CHECK_OVERFLOW(value, bits, ignore, sig) {
// For signedness issue here, see settings.js, CHECK_SIGNED_OVERFLOWS
#if CHECK_SIGNED_OVERFLOWS
if (value === Infinity || value === -Infinity || value >= twopbits1 || value < -twopbits1) {
- CorrectionsMonitor.note('SignedOverflow', 0, sig);
- if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) CorrectionsMonitor.note('Overflow');
+ throw 'SignedOverflow';
+ if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) throw 'Overflow';
+ }
#else
if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) {
- CorrectionsMonitor.note('Overflow', 0, sig);
+ throw 'Overflow';
+ }
#endif
#if CORRECT_OVERFLOWS
- // Fail on >32 bits - we warned at compile time
- if (bits <= 32) {
- value = value & (twopbits - 1);
- }
-#endif
- } else {
-#if CHECK_SIGNED_OVERFLOWS
- CorrectionsMonitor.note('SignedOverflow', 1, sig);
-#endif
- CorrectionsMonitor.note('Overflow', 1, sig);
+ // Fail on >32 bits - we warned at compile time
+ if (bits <= 32) {
+ value = value & (twopbits - 1);
}
+#endif
return value;
}
#endif
@@ -243,39 +193,6 @@ var INDENT = '';
var START_TIME = Date.now();
#endif
-#if PROFILE
-var PROFILING = 0;
-var PROFILING_ROOT = { time: 0, children: {}, calls: 0 };
-var PROFILING_NODE;
-
-function startProfiling() {
- PROFILING_NODE = PROFILING_ROOT;
- PROFILING = 1;
-}
-Module['startProfiling'] = startProfiling;
-
-function stopProfiling() {
- PROFILING = 0;
- assert(PROFILING_NODE === PROFILING_ROOT, 'Must have popped all the profiling call stack');
-}
-Module['stopProfiling'] = stopProfiling;
-
-function printProfiling() {
- function dumpData(name_, node, indent) {
- Module.print(indent + ('________' + node.time).substr(-8) + ': ' + name_ + ' (' + node.calls + ')');
- var children = [];
- for (var child in node.children) {
- children.push(node.children[child]);
- children[children.length-1].name_ = child;
- }
- children.sort(function(x, y) { return y.time - x.time });
- children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') });
- }
- dumpData('root', PROFILING_ROOT, ' ');
-}
-Module['printProfiling'] = printProfiling;
-#endif
-
//========================================
// Runtime essentials
//========================================
@@ -655,47 +572,43 @@ function enlargeMemory() {
#endif
var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}};
-#if ASM_JS == 0
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}};
-#else
-var TOTAL_MEMORY = {{{ TOTAL_MEMORY }}}; // in asm, we hardcode the mask, so cannot adjust memory at runtime
-#endif
var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
// Initialize the runtime's memory
#if USE_TYPED_ARRAYS
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
- assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
- 'Cannot fallback to non-typed array case: Code is too specialized');
+assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
+ 'Cannot fallback to non-typed array case: Code is too specialized');
#if USE_TYPED_ARRAYS == 1
- HEAP = IHEAP = new Int32Array(TOTAL_MEMORY);
- IHEAPU = new Uint32Array(IHEAP.buffer);
+HEAP = IHEAP = new Int32Array(TOTAL_MEMORY);
+IHEAPU = new Uint32Array(IHEAP.buffer);
#if USE_FHEAP
- FHEAP = new Float64Array(TOTAL_MEMORY);
+FHEAP = new Float64Array(TOTAL_MEMORY);
#endif
#endif
#if USE_TYPED_ARRAYS == 2
- var buffer = new ArrayBuffer(TOTAL_MEMORY);
- HEAP8 = new Int8Array(buffer);
- HEAP16 = new Int16Array(buffer);
- HEAP32 = new Int32Array(buffer);
- HEAPU8 = new Uint8Array(buffer);
- HEAPU16 = new Uint16Array(buffer);
- HEAPU32 = new Uint32Array(buffer);
- HEAPF32 = new Float32Array(buffer);
- HEAPF64 = new Float64Array(buffer);
-
- // Endianness check (note: assumes compiler arch was little-endian)
- HEAP32[0] = 255;
- assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system');
+var buffer = new ArrayBuffer(TOTAL_MEMORY);
+HEAP8 = new Int8Array(buffer);
+HEAP16 = new Int16Array(buffer);
+HEAP32 = new Int32Array(buffer);
+HEAPU8 = new Uint8Array(buffer);
+HEAPU16 = new Uint16Array(buffer);
+HEAPU32 = new Uint32Array(buffer);
+HEAPF32 = new Float32Array(buffer);
+HEAPF64 = new Float64Array(buffer);
+
+// Endianness check (note: assumes compiler arch was little-endian)
+HEAP32[0] = 255;
+assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system');
#endif
#else
- // Make sure that our HEAP is implemented as a flat array.
- HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower
- for (var i = 0; i < FAST_MEMORY; i++) {
- HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed
- }
+// Make sure that our HEAP is implemented as a flat array.
+HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower
+for (var i = 0; i < FAST_MEMORY; i++) {
+ HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed
+}
#endif
Module['HEAP'] = HEAP;
@@ -722,7 +635,7 @@ STACK_MAX = TOTAL_STACK; // we lose a little stack here, but TOTAL_STACK is nice
#if USE_TYPED_ARRAYS == 2
var tempDoublePtr = Runtime.alignMemory(allocate(12, 'i8', ALLOC_STACK), 8);
assert(tempDoublePtr % 8 == 0);
-function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much
+function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much
HEAP8[tempDoublePtr] = HEAP8[ptr];
HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];
HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];
@@ -773,9 +686,6 @@ function preMain() {
}
function exitRuntime() {
callRuntimeCallbacks(__ATEXIT__);
-
- // Print summary of correction activity
- CorrectionsMonitor.print();
}
// Tools
@@ -832,6 +742,20 @@ Module['writeArrayToMemory'] = writeArrayToMemory;
{{{ unSign }}}
{{{ reSign }}}
+#if PRECISE_I32_MUL
+if (!Math.imul) Math.imul = function(a, b) {
+ var ah = a >>> 16;
+ var al = a & 0xffff;
+ var bh = b >>> 16;
+ var bl = b & 0xffff;
+ return (al*bl + ((ah*bl + al*bh) << 16))|0;
+};
+#else
+Math.imul = function(a, b) {
+ return (a*b)|0; // fast but imprecise
+};
+#endif
+
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
// decrement it. Incrementing must happen in a place like
diff --git a/src/runtime.js b/src/runtime.js
index e5b86065..e902d27b 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -242,6 +242,10 @@ var Runtime = {
} else if (Runtime.isStructType(field)) {
size = Types.types[field].flatSize;
alignSize = Types.types[field].alignSize;
+ } else if (field[0] == 'b') {
+ // bN, large number field, like a [N x i8]
+ size = field.substr(1)|0;
+ alignSize = 1;
} else {
throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]);
}
@@ -353,7 +357,7 @@ var Runtime = {
},
addFunction: function(func, sig) {
- assert(sig);
+ //assert(sig); // TODO: support asm
var table = FUNCTION_TABLE; // TODO: support asm
var ret = table.length;
table.push(func);
@@ -505,26 +509,19 @@ function getRuntime() {
// example, -1 in int32 would be a very large number as unsigned.
function unSign(value, bits, ignore, sig) {
if (value >= 0) {
-#if CHECK_SIGNS
- if (!ignore) CorrectionsMonitor.note('UnSign', 1, sig);
-#endif
return value;
}
#if CHECK_SIGNS
- if (!ignore) CorrectionsMonitor.note('UnSign', 0, sig);
+ if (!ignore) throw 'UnSign';
#endif
return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts
: Math.pow(2, bits) + value;
- // TODO: clean up previous line
}
// Converts a value we have as unsigned, into a signed value. For
// example, 200 in a uint8 would be a negative number.
function reSign(value, bits, ignore, sig) {
if (value <= 0) {
-#if CHECK_SIGNS
- if (!ignore) CorrectionsMonitor.note('ReSign', 1, sig);
-#endif
return value;
}
var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32
@@ -536,10 +533,7 @@ function reSign(value, bits, ignore, sig) {
// but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors
// TODO: In i64 mode 1, resign the two parts separately and safely
#if CHECK_SIGNS
- if (!ignore) {
- CorrectionsMonitor.note('ReSign', 0, sig);
- noted = true;
- }
+ if (!ignore) throw 'ReSign';
#endif
value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
}
@@ -548,18 +542,9 @@ function reSign(value, bits, ignore, sig) {
// without CHECK_SIGNS, we would just do the |0 shortcut, so check that that
// would indeed give the exact same result.
if (bits === 32 && (value|0) !== value && typeof value !== 'boolean') {
- if (!ignore) {
- CorrectionsMonitor.note('ReSign', 0, sig);
- noted = true;
- }
+ if (!ignore) throw 'ReSign';
}
- if (!noted) CorrectionsMonitor.note('ReSign', 1, sig);
#endif
return value;
}
-// Just a stub. We don't care about noting compile-time corrections. But they are called.
-var CorrectionsMonitor = {
- note: function(){}
-};
-
diff --git a/src/settings.js b/src/settings.js
index 0234d0ca..7caa5b92 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -92,13 +92,9 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i
// that we can't know at compile time that 64-bit math is needed. For example, if you
// print 64-bit values with printf, but never add them, we can't know at compile time
// and you need to set this to 2.
-var PRECISE_I32_MUL = 0; // If enabled, i64 math is done in i32 multiplication. This is necessary if the values
- // exceed the JS double-integer limit of ~52 bits. This option can normally be disabled
- // because generally i32 multiplication works ok without it, and enabling it has a big
- // impact on performance.
- // Note that you can hand-optimize your code to avoid the need for this: If you do
- // multiplications that actually need 64-bit precision inside 64-bit values, things
- // will work properly. (Unless the LLVM optimizer turns them into 32-bit values?)
+var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is
+ // correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise,
+ // rounding will occur above that range).
var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
// compiler. This potentially lets closure optimize the code better.
@@ -134,6 +130,16 @@ var SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations
var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js
+var CORRUPTION_CHECK = 0; // When enabled, will emit a buffer area at the beginning and
+ // end of each allocation on the heap, filled with canary
+ // values that can be checked later. Corruption is checked for
+ // at the end of each at each free() (see jsifier to add more, and you
+ // can add more manual checks by calling CorruptionChecker.checkAll).
+ // 0 means not enabled, higher values mean the size of the
+ // buffer areas as a multiple of the allocated area (so
+ // 1 means 100%, or buffer areas equal to allocated area,
+ // both before and after). This must be an integer.
+
var LABEL_DEBUG = 0; // 1: Print out functions as we enter them
// 2: Also print out each label as we enter it
var LABEL_FUNCTION_FILTERS = []; // Filters for function label debug.
@@ -148,11 +154,13 @@ var LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js).
// Runtime.debug at runtime for logging to cease, and can set it when you
// want it back. A simple way to set it in C++ is
// emscripten_run_script("Runtime.debug = ...;");
-var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime
- // option, in this case GL.debug.
var SOCKET_DEBUG = 0; // Log out socket/network data transfer.
-var PROFILE_MAIN_LOOP = 0; // Profile the function called in set_main_loop
+var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime
+ // option, in this case GL.debug.
+var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, to allow tests to work (but adds overhead)
+var GL_MAX_TEMP_BUFFER_SIZE = 2097152; // How large GL emulation temp buffers are
+var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL emulation code
var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you
// are compiling does not actually rely on catching exceptions (but the
@@ -203,21 +211,10 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r
// a new project and want to see a list of file system operations happening
// so that you can create a virtual file system with all of the required files.
-var PGO = 0; // Profile-guided optimization.
- // When run with the CHECK_* options, will not fail on errors. Instead, will
- // keep a record of which checks succeeded and which failed. On shutdown, will
- // print out that information. This is useful for knowing which lines need
- // checking enabled and which do not, that is, this is a way to automate the
- // generation of line data for CORRECT_*_LINES options.
- // All CORRECT_* options default to 1 with PGO builds.
- // See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info
-
var NAMED_GLOBALS = 0; // If 1, we use global variables for globals. Otherwise
// they are referred to by a base plus an offset (called an indexed global),
// saving global variables but adding runtime overhead.
-var PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example.
-
var EXPORT_ALL = 0; // If true, we export all the symbols
var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported. These functions are kept alive
// through LLVM dead code elimination, and also made accessible outside of
@@ -325,7 +322,6 @@ var BENCHMARK = 0; // If 1, will just time how long main() takes to execute, and
var ASM_JS = 0; // If 1, generate code in asm.js format. XXX This is highly experimental,
// and will not work on most codebases yet. It is NOT recommended that you
// try this yet.
-var USE_MATH_IMUL = 0; // If 1, use Math.imul when useful
var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i32, using ?: