Skip to content

Commit

Permalink
Add functions for access emscripten stack layout information (#11437)
Browse files Browse the repository at this point in the history
This change adds emscripten_stack_get_base/free/end/current.

This is a cut down version of #11162 which uses wasm assembly
rather than relying on binaryen changes.

Also remove old emscripten_get_stack_base/emscripten_get_stack_top
from src/library.js.  emscripten_get_stack_top never worked anyway
since its was not marked as asm and therefore not referencing
the arm-internal version of STACKTOP.
  • Loading branch information
sbc100 committed Jun 17, 2020
1 parent 56dc964 commit 1b58a80
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 11 deletions.
8 changes: 0 additions & 8 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -4547,14 +4547,6 @@ LibraryManager.library = {
});
},

emscripten_get_stack_top: function() {
return STACKTOP;
},

emscripten_get_stack_base: function() {
return STACK_BASE;
},

_readAsmConstArgsArray: '=[]',
$readAsmConstArgs__deps: ['_readAsmConstArgsArray'],
$readAsmConstArgs: function(sigPtr, buf) {
Expand Down
25 changes: 23 additions & 2 deletions src/library_stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@
*/

mergeInto(LibraryManager.library, {
#if WASM_BACKEND == 0
emscripten_stack_get_base: function() {
return STACK_BASE;
},
emscripten_stack_get_end: function() {
// TODO(sbc): rename STACK_MAX -> STACK_END?
return STACK_MAX;
},

#if !WASM_BACKEND
$abortStackOverflow__deps: ['$stackSave'],
#endif
$abortStackOverflow__import: true,
$abortStackOverflow: function(allocSize) {
abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - stackSave() + allocSize) + ' bytes available!');
},

#if WASM_BACKEND == 0
#if !WASM_BACKEND
$stackAlloc__asm: true,
$stackAlloc__sig: 'ii',
#if ASSERTIONS || STACK_OVERFLOW_CHECK >= 2
Expand Down Expand Up @@ -43,5 +51,18 @@ mergeInto(LibraryManager.library, {
top = top|0;
STACKTOP = top;
},

// With the wasm backend, these functions are implemented as native
// functions in compiler-rt/stack_ops.s
emscripten_stack_get_current__asm: true,
emscripten_stack_get_current__sig: 'i',
emscripten_stack_get_current: function() {
return STACKTOP|0;
},
emscripten_stack_get_free__asm: true,
emscripten_stack_get_free__sig: 'i',
emscripten_stack_get_free: function() {
return (STACK_MAX|0) - (STACKTOP|0);
}
#endif
});
37 changes: 37 additions & 0 deletions system/include/emscripten/stack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2020 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#pragma once

// API that gives access to introspecting the Wasm data stack. Build with
// -lstack.js to use this API.

#ifdef __cplusplus
extern "C" {
#endif

// Returns the starting address of the wasm stack. This is the address
// that the stack pointer would point to when no bytes are in use on the stack.
uintptr_t emscripten_stack_get_base(void);

// Returns the end address of the wasm stack. This is the address that the stack
// pointer would point to when the whole stack is in use. (the address pointed
// to by the end is not part of the stack itself) Note that in fastcomp, the
// stack grows up, whereas in wasm backend, it grows down. So with wasm
// backend, the address returned by emscripten_stack_get_end() is smaller than
// emscripten_stack_get_base().
uintptr_t emscripten_stack_get_end(void);

// Returns the current stack pointer.
uintptr_t emscripten_stack_get_current(void);

// Returns the number of free bytes left on the stack.
size_t emscripten_stack_get_free(void);

#ifdef __cplusplus
}
#endif
26 changes: 26 additions & 0 deletions system/lib/compiler-rt/stack_ops.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.globl stackSave
.globl stackRestore
.globl stackAlloc
.globl emscripten_stack_get_current
.globl emscripten_stack_get_free

.globaltype __stack_pointer, i32

Expand Down Expand Up @@ -30,3 +32,27 @@ stackAlloc:
global.set __stack_pointer
local.get 1
end_function

emscripten_stack_get_current:
.functype emscripten_stack_get_current () -> (i32)
global.get __stack_pointer
end_function

.functype emscripten_stack_get_end () -> (i32)
.functype emscripten_stack_get_base () -> (i32)
.globaltype __stack_end, i32
__stack_end:

emscripten_stack_get_free:
# set __stack_base/__stack_end on first call
.functype emscripten_stack_get_free () -> (i32)
global.get __stack_end
i32.eqz
if
call emscripten_stack_get_end
global.set __stack_end
end_if
global.get __stack_pointer
global.get __stack_end
i32.sub
end_function
47 changes: 47 additions & 0 deletions tests/core/test_stack_get_free.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <stdlib.h>
#include <stdio.h>
#include <alloca.h>
#include <emscripten.h>
#include <emscripten/stack.h>
#include <assert.h>
#include <string.h>

void __attribute__((noinline)) DoSomething(char *addr) {
memset(addr, 42, 13);
}

void TestStackValidity() {
uintptr_t base = emscripten_stack_get_base();
uintptr_t end = emscripten_stack_get_end();

uintptr_t used = abs((intptr_t)base - (intptr_t)emscripten_stack_get_current());
uintptr_t free = abs((intptr_t)end - (intptr_t)emscripten_stack_get_current());
uintptr_t free2 = emscripten_stack_get_free();
uintptr_t total = abs((intptr_t)end - (intptr_t)base);
assert(used + free == total);
assert(free == free2);
}

int increment = 256 * 1024;

int main() {
TestStackValidity();

uintptr_t origFree = emscripten_stack_get_free();
uintptr_t prevFree = emscripten_stack_get_free();
printf("Stack used: %u\n", origFree - emscripten_stack_get_free());
for(int i = 0; i < 10; ++i) {
int increment_noopt = emscripten_random() >= 0 ? increment : 2;
char *p = alloca(increment_noopt);
DoSomething(p);
uintptr_t free = emscripten_stack_get_free();
assert(prevFree - free == increment);
prevFree = free;
// Print something from the allocationed region to prevent whole program
// optimizations from elminiating the alloca completely.
printf("Val: %d\n", p[10]);
printf("Stack used: %u\n", origFree - emscripten_stack_get_free());
TestStackValidity();
}
return 0;
}
21 changes: 21 additions & 0 deletions tests/core/test_stack_get_free.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Stack used: 0
Val: 42
Stack used: 262144
Val: 42
Stack used: 524288
Val: 42
Stack used: 786432
Val: 42
Stack used: 1048576
Val: 42
Stack used: 1310720
Val: 42
Stack used: 1572864
Val: 42
Stack used: 1835008
Val: 42
Stack used: 2097152
Val: 42
Stack used: 2359296
Val: 42
Stack used: 2621440
9 changes: 8 additions & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8664,13 +8664,20 @@ def test_emscripten_math(self):
self.do_run_in_out_file_test('tests', 'core', 'test_emscripten_math')

# Tests that users can pass custom JS options from command line using
# the -jsDfoo=val syntax. (https://github.com/emscripten-core/emscripten/issues/10580)
# the -jsDfoo=val syntax:
# See https://github.com/emscripten-core/emscripten/issues/10580.
def test_custom_js_options(self):
self.emcc_args += ['--js-library', path_from_root('tests', 'core', 'test_custom_js_settings.js'), '-jsDCUSTOM_JS_OPTION=1']
self.do_run_in_out_file_test('tests', 'core', 'test_custom_js_settings')

self.assertContained('cannot change built-in settings values with a -jsD directive', self.expect_fail([EMCC, '-jsDWASM=0']))

# Tests <emscripten/stack.h> API
def test_emscripten_stack(self):
self.emcc_args += ['-lstack.js']
self.set_setting('TOTAL_STACK', 4 * 1024 * 1024)
self.do_run_in_out_file_test('tests', 'core', 'test_stack_get_free')


# Generate tests for everything
def make_run(name, emcc_args, settings=None, env=None):
Expand Down

0 comments on commit 1b58a80

Please sign in to comment.