Skip to content

Commit 976b43d

Browse files
authored
Simplify and fix SAFE_HEAP (#24291)
This streamlines the `SAFE_HEAP` transform in acorn-optimizer. I was going to send this as a separate cleanup PR as part of my other work on acorn-optimizer, but it happens to also be the easy fix for a regression mentioned in #24289 and caused by #24283 which added support for 64-bit integers to acorn-optimizer passes. I could fix that issue separately by tweaking `unSign`, but this way we don't need to bother - the values with correct sign are now automatically retrieved by accessing the correct `HEAP*` array. I've added a regression test for the linked issue as well, which fails before this PR and passes after.
1 parent 4607fb4 commit 976b43d

File tree

8 files changed

+50
-254
lines changed

8 files changed

+50
-254
lines changed

src/lib/libgetvalue.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,6 @@ var LibraryMemOps = {
5959
* @param {string} type
6060
*/`,
6161
$getValue: getValueImpl,
62-
63-
#if SAFE_HEAP
64-
// The same as the above two functions, but known to the safeHeap pass
65-
// in tools/acorn-optimizer.mjs. The heap accesses within these two
66-
// functions will *not* get re-written.
67-
// Note that we do not use the alias mechanism here since we need separate
68-
// instances of above setValueImpl/getValueImpl functions.
69-
$setValue_safe__internal: true,
70-
$setValue_safe: setValueImpl,
71-
72-
$getValue_safe__internal: true,
73-
$getValue_safe: getValueImpl,
74-
#endif
7562
};
7663

7764
addToLibrary(LibraryMemOps);

src/runtime_safe_heap.js

Lines changed: 12 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,91 +8,39 @@
88
#error "should only be inclded in SAFE_HEAP mode"
99
#endif
1010

11-
/** @param {number|boolean=} isFloat */
12-
function getSafeHeapType(bytes, isFloat) {
13-
switch (bytes) {
14-
case 1: return 'i8';
15-
case 2: return 'i16';
16-
case 4: return isFloat ? 'float' : 'i32';
17-
case 8: return isFloat ? 'double' : 'i64';
18-
default: abort(`getSafeHeapType() invalid bytes=${bytes}`);
19-
}
20-
}
21-
2211
#if SAFE_HEAP_LOG
2312
var SAFE_HEAP_COUNTER = 0;
2413
#endif
2514

26-
/** @param {number|boolean=} isFloat */
27-
function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
15+
function SAFE_HEAP_INDEX(arr, idx, action) {
2816
#if CAN_ADDRESS_2GB
29-
dest >>>= 0;
17+
idx >>>= 0;
3018
#endif
19+
const bytes = arr.BYTES_PER_ELEMENT;
20+
const dest = idx * bytes;
3121
#if SAFE_HEAP_LOG
32-
dbg('SAFE_HEAP store: ' + [dest, value, bytes, isFloat, SAFE_HEAP_COUNTER++]);
33-
#endif
34-
if (dest <= 0) abort(`segmentation fault storing ${bytes} bytes to address ${dest}`);
35-
#if SAFE_HEAP == 1
36-
if (dest % bytes !== 0) abort(`alignment error storing to address ${dest}, which was expected to be aligned to a multiple of ${bytes}`);
37-
#else
38-
if (dest % bytes !== 0) warnOnce(`alignment error in a memory store operation, alignment was a multiple of ${(((dest ^ (dest-1)) >> 1) + 1)}, but was was expected to be aligned to a multiple of ${bytes}`);
22+
dbg(`SAFE_HEAP ${action}: ${[arr.constructor.name, idx, SAFE_HEAP_COUNTER++]}`);
3923
#endif
24+
if (idx <= 0) abort(`segmentation fault ${action} ${bytes} bytes at address ${dest}`);
4025
#if EXIT_RUNTIME
4126
if (runtimeInitialized && !runtimeExited) {
4227
#else
4328
if (runtimeInitialized) {
4429
#endif
4530
var brk = _sbrk(0);
46-
if (dest + bytes > brk) abort(`segmentation fault, exceeded the top of the available dynamic heap when storing ${bytes} bytes to address ${dest}. DYNAMICTOP=${brk}`);
31+
if (dest + bytes > brk) abort(`segmentation fault, exceeded the top of the available dynamic heap when ${action} ${bytes} bytes at address ${dest}. DYNAMICTOP=${brk}`);
4732
if (brk < _emscripten_stack_get_base()) abort(`brk >= _emscripten_stack_get_base() (brk=${brk}, _emscripten_stack_get_base()=${_emscripten_stack_get_base()})`); // sbrk-managed memory must be above the stack
4833
if (brk > wasmMemory.buffer.byteLength) abort(`brk <= wasmMemory.buffer.byteLength (brk=${brk}, wasmMemory.buffer.byteLength=${wasmMemory.buffer.byteLength})`);
4934
}
50-
setValue_safe(dest, value, getSafeHeapType(bytes, isFloat));
51-
return value;
52-
}
53-
function SAFE_HEAP_STORE_D(dest, value, bytes) {
54-
return SAFE_HEAP_STORE(dest, value, bytes, true);
35+
return idx;
5536
}
5637

57-
/** @param {number|boolean=} isFloat */
58-
function SAFE_HEAP_LOAD(dest, bytes, unsigned, isFloat) {
59-
#if CAN_ADDRESS_2GB
60-
dest >>>= 0;
61-
#endif
62-
if (dest <= 0) abort(`segmentation fault loading ${bytes} bytes from address ${dest}`);
63-
#if SAFE_HEAP == 1
64-
if (dest % bytes !== 0) abort(`alignment error loading from address ${dest}, which was expected to be aligned to a multiple of ${bytes}`);
65-
#else
66-
if (dest % bytes !== 0) warnOnce(`alignment error in a memory load operation, alignment was a multiple of ${(((dest ^ (dest-1)) >> 1) + 1)}, but was was expected to be aligned to a multiple of ${bytes}`);
67-
#endif
68-
#if EXIT_RUNTIME
69-
if (runtimeInitialized && !runtimeExited) {
70-
#else
71-
if (runtimeInitialized) {
72-
#endif
73-
var brk = _sbrk(0);
74-
if (dest + bytes > brk) abort(`segmentation fault, exceeded the top of the available dynamic heap when loading ${bytes} bytes from address ${dest}. DYNAMICTOP=${brk}`);
75-
if (brk < _emscripten_stack_get_base()) abort(`brk >= _emscripten_stack_get_base() (brk=${brk}, _emscripten_stack_get_base()=${_emscripten_stack_get_base()})`); // sbrk-managed memory must be above the stack
76-
if (brk > wasmMemory.buffer.byteLength) abort(`brk <= wasmMemory.buffer.byteLength (brk=${brk}, wasmMemory.buffer.byteLength=${wasmMemory.buffer.byteLength})`);
77-
}
78-
var type = getSafeHeapType(bytes, isFloat);
79-
var ret = getValue_safe(dest, type);
80-
if (unsigned) ret = unSign(ret, parseInt(type.slice(1), 10));
81-
#if SAFE_HEAP_LOG
82-
dbg('SAFE_HEAP load: ' + [dest, ret, bytes, isFloat, unsigned, SAFE_HEAP_COUNTER++]);
83-
#endif
84-
return ret;
85-
}
86-
function SAFE_HEAP_LOAD_D(dest, bytes, unsigned) {
87-
return SAFE_HEAP_LOAD(dest, bytes, unsigned, true);
38+
function SAFE_HEAP_LOAD(arr, idx) {
39+
return arr[SAFE_HEAP_INDEX(arr, idx, 'loading')];
8840
}
8941

90-
function SAFE_FT_MASK(value, mask) {
91-
var ret = value & mask;
92-
if (ret !== value) {
93-
abort(`Function table mask error: function pointer is ${value} which is masked by ${mask}, the likely cause of this is that the function pointer is being called by the wrong type.`);
94-
}
95-
return ret;
42+
function SAFE_HEAP_STORE(arr, idx, value) {
43+
return arr[SAFE_HEAP_INDEX(arr, idx, 'storing')] = value;
9644
}
9745

9846
function segfault() {

test/js_optimizer/safeHeap-output.js

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,57 @@
1-
SAFE_HEAP_STORE(x, 1, 1);
1+
SAFE_HEAP_STORE(HEAP8, x, 1);
22

3-
SAFE_HEAP_STORE(x * 2, 2, 2);
3+
SAFE_HEAP_STORE(HEAP16, x, 2);
44

5-
SAFE_HEAP_STORE(x * 4, 3, 4);
5+
SAFE_HEAP_STORE(HEAP32, x, 3);
66

7-
SAFE_HEAP_STORE(x, 4, 1);
7+
SAFE_HEAP_STORE(HEAPU8, x, 4);
88

9-
SAFE_HEAP_STORE(x * 2, 5, 2);
9+
SAFE_HEAP_STORE(HEAPU16, x, 5);
1010

11-
SAFE_HEAP_STORE(x * 4, 6, 4);
11+
SAFE_HEAP_STORE(HEAPU32, x, 6);
1212

13-
SAFE_HEAP_STORE_D(x * 4, 7, 4);
13+
SAFE_HEAP_STORE(HEAPF32, x, 7);
1414

15-
SAFE_HEAP_STORE_D(x * 8, 8, 8);
15+
SAFE_HEAP_STORE(HEAPF64, x, 8);
1616

17-
SAFE_HEAP_STORE(x * 8, 9n, 8);
17+
SAFE_HEAP_STORE(HEAP64, x, 9n);
1818

19-
SAFE_HEAP_STORE(x * 8, 10n, 8);
19+
SAFE_HEAP_STORE(HEAPU64, x, 10n);
2020

21-
a1 = SAFE_HEAP_LOAD(x, 1, 0);
21+
a1 = SAFE_HEAP_LOAD(HEAP8, x);
2222

23-
a2 = SAFE_HEAP_LOAD(x * 2, 2, 0);
23+
a2 = SAFE_HEAP_LOAD(HEAP16, x);
2424

25-
a3 = SAFE_HEAP_LOAD(x * 4, 4, 0);
25+
a3 = SAFE_HEAP_LOAD(HEAP32, x);
2626

27-
a4 = SAFE_HEAP_LOAD(x, 1, 1);
27+
a4 = SAFE_HEAP_LOAD(HEAPU8, x);
2828

29-
a5 = SAFE_HEAP_LOAD(x * 2, 2, 1);
29+
a5 = SAFE_HEAP_LOAD(HEAPU16, x);
3030

31-
a6 = SAFE_HEAP_LOAD(x * 4, 4, 1);
31+
a6 = SAFE_HEAP_LOAD(HEAPU32, x);
3232

33-
a7 = SAFE_HEAP_LOAD_D(x * 4, 4, 0);
33+
a7 = SAFE_HEAP_LOAD(HEAPF32, x);
3434

35-
a8 = SAFE_HEAP_LOAD_D(x * 8, 8, 0);
35+
a8 = SAFE_HEAP_LOAD(HEAPF64, x);
3636

37-
a9 = SAFE_HEAP_LOAD(x * 8, 8, 0);
37+
a9 = SAFE_HEAP_LOAD(HEAP64, x);
3838

39-
a10 = SAFE_HEAP_LOAD(x * 8, 8, 1);
39+
a10 = SAFE_HEAP_LOAD(HEAPU64, x);
4040

41-
foo = SAFE_HEAP_STORE(1337, 42, 1);
41+
foo = SAFE_HEAP_STORE(HEAPU8, 1337, 42);
4242

43-
SAFE_HEAP_LOAD(bar(SAFE_HEAP_LOAD_D(5 * 8, 8, 0)) * 2, 2, 0);
43+
SAFE_HEAP_LOAD(HEAP16, bar(SAFE_HEAP_LOAD(HEAPF64, 5)));
4444

45-
SAFE_HEAP_STORE_D(x * 4, SAFE_HEAP_LOAD(y * 4, 4, 0), 4);
45+
SAFE_HEAP_STORE(HEAPF32, x, SAFE_HEAP_LOAD(HEAP32, y));
4646

4747
function SAFE_HEAP_FOO(ptr) {
4848
return HEAP8[ptr];
4949
}
5050

51-
function setValue_safe(ptr) {
52-
return HEAP8[ptr];
53-
}
54-
55-
function getValue_safe(ptr) {
56-
return HEAP8[ptr];
57-
}
58-
5951
function somethingElse() {
60-
return SAFE_HEAP_LOAD(ptr, 1, 0);
52+
return SAFE_HEAP_LOAD(HEAP8, ptr);
6153
}
6254

6355
HEAP8.length;
6456

65-
SAFE_HEAP_LOAD(length, 1, 0);
57+
SAFE_HEAP_LOAD(HEAP8, length);

test/js_optimizer/safeHeap.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ HEAPF32[x] = HEAP32[y];
4040
function SAFE_HEAP_FOO(ptr) {
4141
return HEAP8[ptr];
4242
}
43-
function setValue_safe(ptr) {
44-
return HEAP8[ptr];
45-
}
46-
function getValue_safe(ptr) {
47-
return HEAP8[ptr];
48-
}
4943

5044
// but do handle everything else
5145
function somethingElse() {

test/test_core.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7495,8 +7495,15 @@ def test_embind_dynamic_initialization(self):
74957495
self.do_run_in_out_file_test('embind/test_dynamic_initialization.cpp')
74967496

74977497
@no_wasm2js('wasm_bigint')
7498-
def test_embind_i64_val(self):
7498+
@parameterized({
7499+
'': (False,),
7500+
'safe_heap': (True,),
7501+
})
7502+
def test_embind_i64_val(self, safe_heap):
74997503
self.set_setting('WASM_BIGINT')
7504+
if safe_heap and '-fsanitize=address' in self.emcc_args:
7505+
self.skipTest('asan does not work with SAFE_HEAP')
7506+
self.set_setting('SAFE_HEAP', safe_heap)
75007507
self.emcc_args += ['-lembind']
75017508
self.node_args += shared.node_bigint_flags(self.get_nodejs())
75027509
self.do_run_in_out_file_test('embind/test_i64_val.cpp', assert_identical=True)
@@ -8967,7 +8974,7 @@ def test_asan_modularized_with_closure(self):
89678974
def test_safe_heap_user_js(self):
89688975
self.set_setting('SAFE_HEAP')
89698976
self.do_runf('core/test_safe_heap_user_js.c',
8970-
expected_output=['Aborted(segmentation fault storing 1 bytes to address 0)'], assert_returncode=NON_ZERO)
8977+
expected_output=['Aborted(segmentation fault storing 1 bytes at address 0)'], assert_returncode=NON_ZERO)
89718978

89728979
def test_safe_stack(self):
89738980
self.set_setting('STACK_OVERFLOW_CHECK', 2)

test/test_other.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12050,7 +12050,7 @@ def test_safe_heap_2(self):
1205012050
def test_safe_heap_log(self):
1205112051
self.set_setting('SAFE_HEAP')
1205212052
self.set_setting('SAFE_HEAP_LOG')
12053-
self.do_runf('hello_world.c', 'SAFE_HEAP load: ')
12053+
self.do_runf('hello_world.c', 'SAFE_HEAP loading: ')
1205412054

1205512055
def test_mini_printfs(self):
1205612056
def test(code):

0 commit comments

Comments
 (0)