diff options
author | Alon Zakai <alonzakai@gmail.com> | 2011-06-05 19:17:53 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2011-06-05 19:17:53 -0700 |
commit | e068f02137c1de07480886c0f6181150d77816dd (patch) | |
tree | ffc61c76fe4c0bf9fd58e6c7105c3c4378501698 | |
parent | 5b2f181ab4662dca21f3605aeca8c2e0cd212b20 (diff) |
fix alignment issues with USE_TYPED_ARRAYS == 2
-rw-r--r-- | src/analyzer.js | 1 | ||||
-rw-r--r-- | src/parseTools.js | 15 | ||||
-rw-r--r-- | src/preamble.js | 66 | ||||
-rw-r--r-- | src/runtime.js | 19 | ||||
-rw-r--r-- | src/settings.js | 9 | ||||
-rw-r--r-- | tests/runner.py | 7 |
6 files changed, 94 insertions, 23 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index caeedb56..ca021e45 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -534,6 +534,7 @@ function analyzer(data) { for (var i = 0; i < lines.length; i++) { var item = lines[i].value; if (!item || item.intertype != 'alloca') break; + if (USE_TYPED_ARRAYS === 2) index = Runtime.forceAlign(index, Math.min(item.allocatedSize, QUANTUM_SIZE)); item.allocatedIndex = index; index += item.allocatedSize; delete item.allocatedSize; diff --git a/src/parseTools.js b/src/parseTools.js index 5813c908..fab3eccd 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -665,8 +665,7 @@ function getHeapOffset(offset, type) { return offset; } else { if (getNativeFieldSize(type) > 4) { - dprint(type + ' has size > 4, which means we cannot be guaranteed to load it aligned! For now, USE_TYPED_ARRAYS==2 cannot handle that.'); - return 'abort("size > 4, alignment issues with USE_TYPED_ARRAYS==2")'; + type = 'i32'; // XXX we emulate 64-bit values as 32 } return '((' + offset + ')>>' + (Math.log(getNativeFieldSize(type, true))/Math.LN2) + ')'; } @@ -876,13 +875,21 @@ function makeGetSlabs(ptr, type, allowMultiple) { } } else { // USE_TYPED_ARRAYS == 2) if (isPointerType(type)) type = 'i32'; // Hardcoded 32-bit + function warn64() { + if (!Debugging.shownUTA2_64Warning) { + dprint('WARNING: .ll contains i64 or double values. These 64-bit values are dangerous in USE_TYPED_ARRAYS == 2.'); + dprint(' We store i64 as i32, and double as float. This can cause serious problems!'); + Debugging.shownUTA2_64Warning = true; + } + } switch(type) { + case 'i1': return ['HEAP8']; break; case 'i8': return ['HEAP8']; break; case 'i16': return ['HEAP16']; break; case 'i32': return ['HEAP32']; break; - case 'i64': return ['abort("No HEAP64")']; break; case 'float': return ['HEAPF32']; break; - case 'double': return ['HEAPF64']; break; + case 'i64': warn64(); return ['HEAP32']; break; + case 'double': warn64(); return ['HEAPF32']; break; default: { throw 'what, exactly, can we do for unknown types in TA2?! ' + new Error().stack; } diff --git a/src/preamble.js b/src/preamble.js index dd7f9f3e..e0f97f48 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -52,6 +52,10 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore) { } } function SAFE_HEAP_STORE(dest, value, type, ignore) { +#if SAFE_HEAP_LOG + print('SAFE_HEAP store: ' + [dest, type, value, ignore]); +#endif + if (!ignore && !value && value !== 0 && value !== false) { // false can be the result of a mathop comparator throw('Warning: Writing an invalid value of ' + JSON.stringify(value) + ' at ' + dest + ' :: ' + new Error().stack + '\n'); } @@ -75,11 +79,11 @@ function SAFE_HEAP_STORE(dest, value, type, ignore) { if (type[type.length-1] === '*') type = 'i32'; // hardcoded pointers as 32-bit switch(type) { case 'i8': HEAP8[dest] = value; break; - case 'i16': HEAP16[dest>>1] = value; break; - case 'i32': HEAP32[dest>>2] = value; break; - case 'i64': abort('no HEAP64'); break; - case 'float': HEAPF32[dest>>2] = value; break; - case 'double': HEAPF64[dest>>3] = value; break; + case 'i16': assert(dest % 2 === 0, type + ' loads must be aligned'); HEAP16[dest>>1] = value; break; + case 'i32': assert(dest % 4 === 0, type + ' loads must be aligned'); HEAP32[dest>>2] = value; break; + case 'i64': assert(dest % 4 === 0, type + ' loads must be aligned'); HEAP32[dest>>2] = value; break; // XXX store int64 as int32 + case 'float': assert(dest % 4 === 0, type + ' loads must be aligned'); HEAPF32[dest>>2] = value; break; + case 'double': assert(dest % 4 === 0, type + ' loads must be aligned'); HEAPF32[dest>>2] = value; break; // XXX store doubles as floats default: throw 'weird type for typed array II: ' + type + new Error().stack; } #else @@ -92,24 +96,61 @@ function SAFE_HEAP_LOAD(dest, type, ignore) { #if USE_TYPED_ARRAYS == 1 if (type in Runtime.FLOAT_TYPES) { +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, type, FHEAP[dest], ignore]); +#endif return FHEAP[dest]; } else { +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, type, IHEAP[dest], ignore]); +#endif return IHEAP[dest]; } #else #if USE_TYPED_ARRAYS == 2 +#if SAFE_HEAP_LOG + var originalType = type; +#endif if (type[type.length-1] === '*') type = 'i32'; // hardcoded pointers as 32-bit switch(type) { - case 'i8': return HEAP8[dest]; break; - case 'i16': return HEAP16[dest>>1]; break; - case 'i32': return HEAP32[dest>>2]; break; - case 'i64': abort('no HEAP64'); break; - case 'float': return HEAPF32[dest>>2]; break; - case 'double': return HEAPF64[dest>>3]; break; + case 'i8': { +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, originalType, HEAP8[dest], ignore]); +#endif + return HEAP8[dest]; + break; + } + case 'i16': { +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, originalType, HEAP16[dest], ignore]); +#endif + assert(dest % 2 === 0, type + ' loads must be aligned'); + return HEAP16[dest>>1]; + break; + } + case 'i32': case 'i64': { // XXX store int64 as int32 +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, originalType, HEAP32[dest], ignore]); +#endif + assert(dest % 4 === 0, type + ' loads must be aligned'); + return HEAP32[dest>>2]; + break; + } + case 'float': case 'double': { // XXX store doubles as floats +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, originalType, HEAPF32[dest], ignore]); +#endif + assert(dest % 4 === 0, type + ' loads must be aligned'); + return HEAPF32[dest>>2]; + break; + } default: throw 'weird type for typed array II: ' + type; } return null; #else +#if SAFE_HEAP_LOG + print('SAFE_HEAP load: ' + [dest, type, HEAP[dest], ignore]); +#endif return HEAP[dest]; #endif #endif @@ -350,7 +391,7 @@ var HEAP; var IHEAP, FHEAP; #endif #if USE_TYPED_ARRAYS == 2 -var HEAP8, HEAP16, HEAP32, HEAPF32, HEAPF64; +var HEAP8, HEAP16, HEAP32, HEAPF32; #endif var STACK_ROOT, STACKTOP, STACK_MAX; @@ -377,7 +418,6 @@ function __initializeRuntime__() { HEAP16 = new Int16Array(buffer); HEAP32 = new Int32Array(buffer); HEAPF32 = new Float32Array(buffer); - HEAPF64 = new Float64Array(buffer); #endif } else #endif diff --git a/src/runtime.js b/src/runtime.js index adf62521..4246ad1c 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -22,6 +22,7 @@ RuntimeGenerator = { // An allocation that lives as long as the current function call stackAlloc: function(size) { + if (USE_TYPED_ARRAYS === 2) 'STACKTOP += STACKTOP % ' + (QUANTUM_SIZE - (isNumber(size) ? Math.min(size, QUANTUM_SIZE) : QUANTUM_SIZE)) + ';'; var ret = RuntimeGenerator.alloc(size, 'STACK', INIT_STACK); if (ASSERTIONS) { ret += '; assert(STACKTOP < STACK_ROOT + STACK_MAX, "Ran out of stack")'; @@ -31,6 +32,7 @@ RuntimeGenerator = { stackEnter: function(initial) { if (initial === 0 && SKIP_STACK_IN_SMALL) return ''; + if (USE_TYPED_ARRAYS === 2) initial = Runtime.forceAlign(initial); var ret = 'var __stackBase__ = STACKTOP; STACKTOP += ' + initial; if (ASSERTIONS) { ret += '; assert(STACKTOP < STACK_MAX)'; @@ -59,7 +61,7 @@ RuntimeGenerator = { if (typeof quantum !== 'number') { quantum = '(quantum ? quantum : QUANTUM_SIZE)'; } - return target + ' = Math.ceil(' + target + '/' + quantum + ')*' + quantum + ';'; + return target + ' = ' + Runtime.forceAlign(target, quantum) + ';'; } }; @@ -70,9 +72,14 @@ function unInline(name_, params) { } Runtime = { - stackAlloc: unInline('stackAlloc', ['size']), - staticAlloc: unInline('staticAlloc', ['size']), - alignMemory: unInline('alignMemory', ['size', 'quantum']), + forceAlign: function(target, quantum) { + quantum = quantum || QUANTUM_SIZE; + if (isNumber(target) && isNumber(quantum)) { + return Math.ceil(target/quantum)*quantum; + } else { + return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum; + } + }, isNumberType: function(type) { return type in Runtime.INT_TYPES || type in Runtime.FLOAT_TYPES; @@ -167,6 +174,10 @@ Runtime = { } }; +Runtime.stackAlloc = unInline('stackAlloc', ['size']); +Runtime.staticAlloc = unInline('staticAlloc', ['size']); +Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']); + function getRuntime() { var ret = 'var Runtime = {\n'; for (i in Runtime) { diff --git a/src/settings.js b/src/settings.js index cbdb6ac1..f2dd9065 100644 --- a/src/settings.js +++ b/src/settings.js @@ -39,6 +39,13 @@ INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to OPTIMIZE = 0; // Optimize llvm operations into js commands RELOOP = 0; // Recreate js native loops from llvm data USE_TYPED_ARRAYS = 0; // Try to use typed arrays for the heap + // 1 has two heaps, IHEAP (int32) and FHEAP (double), + // and addresses there are a match for normal addresses. This wastes memory but can be fast. + // 2 is a single heap, accessible through views as int8, int32, etc. This saves memory but + // has more overhead of pointer calculations. It also is limited to storing doubles as floats, + // simply because double stores are not necessarily 64-bit aligned, and we can only access + // 64-bit aligned values with a 64-bit typed array. Likewise int64s are stored as int32's, + // which is potentially very dangerous! SKIP_STACK_IN_SMALL = 1; // When enabled, does not push/pop the stack at all in // functions that have no basic stack usage. But, they // may allocate stack later, and in a loop, this can be @@ -51,6 +58,8 @@ SAFE_HEAP = 0; // Check each write to the heap against a list of blocked address // SAFE_HEAP_LINES, checking only the specified lines. // If equal to 3, checking all *but* the specified lines. Note // that 3 is the option you usually want here. +SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations + LABEL_DEBUG = 0; // Print out labels and functions as we enter them EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops diff --git a/tests/runner.py b/tests/runner.py index 489196e3..738f639e 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -302,6 +302,8 @@ if 'benchmark' not in sys.argv: # Run in both JavaScript engines, if optimizing - significant differences there (typed arrays) if js_engines is None: js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE] + if USE_TYPED_ARRAYS == 2: + js_engines = [SPIDERMONKEY_ENGINE] # when oh when will v8 support typed arrays in the console for engine in js_engines: js_output = self.run_generated_code(engine, filename + '.o.js', args) if output_nicerizer is not None: @@ -2514,8 +2516,9 @@ TT = %s for llvm_opts in [0,1]: for name, compiler, quantum, embetter, typed_arrays in [ ('clang', CLANG, 1, 0, 0), ('clang', CLANG, 4, 0, 0), ('llvm_gcc', LLVM_GCC, 4, 0, 0), - ('clang', CLANG, 1, 1, 1), ('clang', CLANG, 4, 1, 1), ('llvm_gcc', LLVM_GCC, 4, 1, 1)#, -# ('clang', CLANG, 4, 1, 2), ('llvm_gcc', LLVM_GCC, 4, 1, 2) + ('clang', CLANG, 1, 1, 1), ('clang', CLANG, 4, 1, 1), ('llvm_gcc', LLVM_GCC, 4, 1, 1), +# ('clang', CLANG, 4, 1, 2), +# ('llvm_gcc', LLVM_GCC, 4, 0, 2) ]: fullname = '%s_%d_%d%s%s' % ( name, llvm_opts, embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) |