Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Sam Gao <gaoshan274@gmail.com>
* Sebastian Mayr <me@sam.st>
* Vladimir Gamalyan <vladimir.gamalyan@gmail.com>
* Wouter van Oortmerssen <wvo@google.com> (copyright owned by Google, LLC)
8 changes: 8 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ def backend_binaryen_passes():
# safe heap must run before post-emscripten, so post-emscripten can apply the sbrk ptr
if shared.Settings.SAFE_HEAP:
passes += ['--safe-heap']
if shared.Settings.MEMORY64 == 2:
passes += ['--memory64-lowering']
if run_binaryen_optimizer:
passes += ['--post-emscripten']
if not shared.Settings.EXIT_RUNTIME:
Expand Down Expand Up @@ -1894,6 +1896,12 @@ def include_and_export(name):
if shared.Settings.USE_PTHREADS:
newargs.append('-pthread')

# Any "pointers" passed to JS will now be i64's, in both modes.
if shared.Settings.MEMORY64:
Comment thread
aardappel marked this conversation as resolved.
if settings_key_changes.get('WASM_BIGINT') == '0':
exit_with_error('MEMORY64 is not compatible with WASM_BIGINT=0')
shared.Settings.WASM_BIGINT = 1

# check if we can address the 2GB mark and higher: either if we start at
# 2GB, or if we allow growth to either any amount or to 2GB or more.
if shared.Settings.INITIAL_MEMORY > 2 * 1024 * 1024 * 1024 or \
Expand Down
6 changes: 6 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ var MEMORY_GROWTH_GEOMETRIC_CAP = 96*1024*1024;
// and MEMORY_GROWTH_GEOMETRIC_CAP are ignored.
var MEMORY_GROWTH_LINEAR_STEP = -1;

// The "architecture" to compile for. 0 means the default wasm32, 1 is
// the full end-to-end wasm64 mode, and 2 is wasm64 for clang/lld but lowered to
// wasm32 in Binaryen (such that it can run on wasm32 engines, while internally
// using i64 pointers).
var MEMORY64 = 0;

// If true, allows more functions to be added to the table at runtime. This is
// necessary for dynamic linking, and set automatically in that mode.
var ALLOW_TABLE_GROWTH = 0;
Expand Down
6 changes: 6 additions & 0 deletions system/include/wasi/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
#include <stddef.h>
#include <stdint.h>

#pragma push_macro("_Static_assert")
#undef _Static_assert
#define _Static_assert(X, Y)

Comment thread
aardappel marked this conversation as resolved.
_Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout");
_Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout");
_Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout");
Expand Down Expand Up @@ -2751,4 +2755,6 @@ __wasi_errno_t __wasi_sock_shutdown(
}
#endif

#pragma pop_macro("_Static_assert")

#endif
44 changes: 27 additions & 17 deletions system/lib/compiler-rt/stack_limits.S
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,32 @@
.globl emscripten_stack_get_base
.globl emscripten_stack_get_end

.globaltype __stack_pointer, i32
#ifdef __wasm64__
#define PTR i64
#define ALIGN 3
#define PTRSTORE .int64
#else
#define PTR i32
#define ALIGN 2
#define PTRSTORE .int32
#endif

.globaltype __stack_pointer, PTR

# TODO(sbc): It would be nice if these we initialized directly
# using i32.const rather than using the `emscripten_stack_init`
.globaltype __stack_end, i32
# using PTR.const rather than using the `emscripten_stack_init`
.globaltype __stack_end, PTR
__stack_end:
.globaltype __stack_base, i32
.globaltype __stack_base, PTR
__stack_base:

emscripten_stack_get_base:
.functype emscripten_stack_get_base () -> (i32)
.functype emscripten_stack_get_base () -> (PTR)
global.get __stack_base
end_function

emscripten_stack_get_end:
.functype emscripten_stack_get_end () -> (i32)
.functype emscripten_stack_get_end () -> (PTR)
global.get __stack_end
end_function

Expand All @@ -33,41 +43,41 @@ emscripten_stack_init:
#ifdef __PIC__
global.get __heap_base@GOT
#else
i32.const __heap_base
PTR.const __heap_base
#endif
global.set __stack_base

# The end of stack data is the limit of the stack growth
#ifdef __PIC__
global.get __data_end@GOT
#else
i32.const __data_end
PTR.const __data_end
#endif
# Align up to 16 bytes
i32.const 0xf
i32.add
i32.const -0x10
i32.and
PTR.const 0xf
PTR.add
PTR.const -0x10
PTR.and
global.set __stack_end

end_function

emscripten_stack_set_limits:
.functype emscripten_stack_set_limits (i32, i32) -> ()
.functype emscripten_stack_set_limits (PTR, PTR) -> ()
local.get 0
global.set __stack_base
local.get 1
global.set __stack_end
end_function

emscripten_stack_get_free:
.functype emscripten_stack_get_free () -> (i32)
.functype emscripten_stack_get_free () -> (PTR)
global.get __stack_pointer
global.get __stack_end
i32.sub
PTR.sub
end_function

# Add emscripten_stack_init to static ctors
.section .init_array.1,"",@
.p2align 2
.int32 emscripten_stack_init
.p2align ALIGN
PTRSTORE emscripten_stack_init
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,45 @@
.globl stackAlloc
Comment thread
aardappel marked this conversation as resolved.
.globl emscripten_stack_get_current

.globaltype __stack_pointer, i32
#ifdef __wasm64__
#define PTR i64
#define MASK 0xfffffffffffffff0
#else
#define PTR i32
#define MASK 0xfffffff0
#endif

.globaltype __stack_pointer, PTR

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

stackRestore:
.functype stackRestore(i32) -> ()
.functype stackRestore(PTR) -> ()
local.get 0
global.set __stack_pointer
end_function

stackAlloc:
.functype stackAlloc(i32) -> (i32)
.local i32, i32
.functype stackAlloc(PTR) -> (PTR)
.local PTR, PTR
global.get __stack_pointer
# Get arg 0 -> number of bytes to allocate
local.get 0
# Stack grows down. Subtract arg0 from __stack_pointer
i32.sub
PTR.sub
# Align result by anding with ~15
i32.const 0xfffffff0
i32.and
PTR.const MASK
PTR.and
local.tee 1
global.set __stack_pointer
local.get 1
end_function

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

63 changes: 44 additions & 19 deletions system/lib/pthread/library_pthread_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,46 +295,71 @@ int pthread_barrier_destroy(pthread_barrier_t* mutex) { return 0; }

int pthread_barrier_wait(pthread_barrier_t* mutex) { return 0; }

/* magic value to track a validly constructed TLS slot */
#define PTHREAD_TLS_MAGIC_ID 0x02468ACE
// pthread_key_t is 32-bit, so to be able to store pointers in there, we sadly
// have to track an array of them.
static size_t num_tls_entries = 0;
static size_t max_tls_entries = 0;
struct entry_t { const void* value; int allocated; };
static struct entry_t* tls_entries = NULL;

int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
if (key == 0)
return EINVAL;
uintptr_t* tls = (uintptr_t*)malloc(sizeof(uintptr_t) * 2);
tls[0] = 0;
tls[1] = PTHREAD_TLS_MAGIC_ID;
*key = (pthread_key_t)tls;
if (!max_tls_entries) {
// First time we're called, allocate entry table.
max_tls_entries = 4;
tls_entries = (struct entry_t*)malloc(max_tls_entries * sizeof(void *));
}
// Find empty spot.
size_t entry = 0;
for (; entry < num_tls_entries; entry++) {
if (!tls_entries[entry].allocated) break;
}
if (entry == max_tls_entries) {
// No empty spots, table full: double the table.
max_tls_entries *= 2;
tls_entries =
(struct entry_t*)realloc(tls_entries, num_tls_entries * sizeof(void *));
}
if (entry == num_tls_entries) {
// No empty spots, but table not full.
num_tls_entries++;
}
struct entry_t* e = &tls_entries[entry];
e->value = NULL;
e->allocated = 1;
// Key can't be zero.
*key = (pthread_key_t)entry + 1;
return 0;
}

int pthread_key_delete(pthread_key_t key) {
if (key == 0)
if (key == 0 || key > num_tls_entries)
return EINVAL;
uintptr_t* tls = (uintptr_t*)key;
if (tls[1] != PTHREAD_TLS_MAGIC_ID)
struct entry_t* e = &tls_entries[key - 1];
if (!e->allocated)
return EINVAL;
tls[0] = tls[1] = 0;
free(tls);
e->value = NULL;
e->allocated = 0;
return 0;
}

void* pthread_getspecific(pthread_key_t key) {
if (key == 0)
if (key == 0 || key > num_tls_entries)
return NULL;
uintptr_t* tls = (uintptr_t*)key;
if (tls[1] != PTHREAD_TLS_MAGIC_ID)
struct entry_t* e = &tls_entries[key - 1];
if (!e->allocated)
return NULL;
return (void*)tls[0];
return (void*)e->value;
}

int pthread_setspecific(pthread_key_t key, const void* value) {
if (key == 0)
if (key == 0 || key > num_tls_entries)
return EINVAL;
uintptr_t* tls = (uintptr_t*)key;
if (tls[1] != PTHREAD_TLS_MAGIC_ID)
struct entry_t* e = &tls_entries[key - 1];
if (!e->allocated)
return EINVAL;
tls[0] = (uintptr_t)value;
e->value = value;
return 0;
}

Expand Down
5 changes: 5 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8207,6 +8207,11 @@ def test_export_start(self):
self.set_setting('EXPORTED_FUNCTIONS', ['__start'])
self.do_run_in_out_file_test('tests', 'core', 'test_hello_world.c')

@unittest.skip("memory64 functionality only partially working")
def test_memory64_hello_world(self):
self.set_setting('MEMORY64', 2)
Comment thread
aardappel marked this conversation as resolved.
self.do_run_in_out_file_test('tests', 'core', 'test_hello_world.c')

# Tests the operation of API found in #include <emscripten/math.h>
def test_emscripten_math(self):
self.do_run_in_out_file_test('tests', 'core', 'test_emscripten_math.c')
Expand Down
5 changes: 5 additions & 0 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@ def lld_flags_for_executable(external_symbol_list):
if Settings.USE_PTHREADS:
cmd.append('--shared-memory')

if Settings.MEMORY64:
cmd.append('-mwasm64')

# wasm-ld can strip debug info for us. this strips both the Names
# section and DWARF, so we can only use it when we don't need any of
# those things.
Expand Down Expand Up @@ -1545,6 +1548,8 @@ def get_binaryen_feature_flags():
ret = ['--mvp-features']
if Settings.USE_PTHREADS:
ret += ['--enable-threads']
if Settings.MEMORY64:
ret += ['--enable-memory64']
ret += Settings.BINARYEN_FEATURES
return ret

Expand Down
2 changes: 2 additions & 0 deletions tools/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def __init__(self, dirname, use_subdir=True):
subdir += '-lto'
if shared.Settings.RELOCATABLE:
subdir += '-pic'
if shared.Settings.MEMORY64:
subdir += '-memory64'
Comment thread
aardappel marked this conversation as resolved.
dirname = os.path.join(dirname, subdir)

self.dirname = dirname
Expand Down
5 changes: 4 additions & 1 deletion tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,10 @@ def apply_configuration():


def get_llvm_target():
return 'wasm32-unknown-emscripten'
if Settings.MEMORY64:
return 'wasm64-unknown-emscripten'
else:
return 'wasm32-unknown-emscripten'


def emsdk_ldflags(user_args):
Expand Down
6 changes: 5 additions & 1 deletion tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def get_cflags(force_object_files=False):
flags += ['-flto=' + shared.Settings.LTO]
if shared.Settings.RELOCATABLE:
flags += ['-s', 'RELOCATABLE']
if shared.Settings.MEMORY64:
flags += ['-s', 'MEMORY64=' + str(shared.Settings.MEMORY64)]
return flags


Expand Down Expand Up @@ -371,6 +373,8 @@ def build_objects(self):
cmd = [shared.EMXX]
if ext != '.s':
cmd += cflags
elif shared.Settings.MEMORY64:
cmd += ['-s', 'MEMORY64=' + str(shared.Settings.MEMORY64)]
commands.append(cmd + ['-c', src, '-o', o])
objects.append(o)
run_build_commands(commands)
Expand Down Expand Up @@ -644,7 +648,7 @@ class libcompiler_rt(MTLibrary):
cflags = ['-O2', '-fno-builtin']
src_dir = ['system', 'lib', 'compiler-rt', 'lib', 'builtins']
src_files = glob_in_path(src_dir, '*.c')
src_files.append(shared.path_from_root('system', 'lib', 'compiler-rt', 'stack_ops.s'))
src_files.append(shared.path_from_root('system', 'lib', 'compiler-rt', 'stack_ops.S'))
src_files.append(shared.path_from_root('system', 'lib', 'compiler-rt', 'stack_limits.S'))
src_files.append(shared.path_from_root('system', 'lib', 'compiler-rt', 'emscripten_setjmp.c'))
src_files.append(shared.path_from_root('system', 'lib', 'compiler-rt', 'emscripten_exception_builtins.c'))
Expand Down