Skip to content

[Wasm64] Fix embind with addresses over 4gb #20071

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 18, 2023
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 .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ jobs:
wasm64_4gb.test_hello_world
wasm64_4gb.test_em_asm
wasm64_4gb.test_async_main
wasm64_4gb.*embind*
core_2gb.test_em_asm
wasm64l.test_bigswitch
other.test_memory64_proxies
Expand Down
92 changes: 46 additions & 46 deletions src/embind/embind.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var LibraryEmbind = {
$PureVirtualError__postset: "PureVirtualError = Module['PureVirtualError'] = extendError(Error, 'PureVirtualError');",
$PureVirtualError__deps: ['$extendError'],
$PureVirtualError: undefined,
$GenericWireTypeSize: {{{ 2 * POINTER_SIZE }}},

$init_embind__deps: [
'$getInheritedInstanceCount', '$getLiveInheritedInstances',
Expand Down Expand Up @@ -274,7 +275,7 @@ var LibraryEmbind = {
});
},

_embind_register_bool__deps: ['$readLatin1String', '$registerType'],
_embind_register_bool__deps: ['$readLatin1String', '$registerType', '$GenericWireTypeSize'],
_embind_register_bool: (rawType, name, trueValue, falseValue) => {
name = readLatin1String(name);
registerType(rawType, {
Expand All @@ -287,7 +288,7 @@ var LibraryEmbind = {
'toWireType': function(destructors, o) {
return o ? trueValue : falseValue;
},
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': function(pointer) {
return this['fromWireType'](HEAPU8[pointer]);
},
Expand All @@ -300,18 +301,18 @@ var LibraryEmbind = {
// integers are quite common, so generate very specialized functions
switch (width) {
case 1: return signed ?
function readS8FromPointer(pointer) { return HEAP8[pointer]; } :
function readU8FromPointer(pointer) { return HEAPU8[pointer]; };
(pointer) => {{{ makeGetValue('pointer', 0, 'i8') }}} :
(pointer) => {{{ makeGetValue('pointer', 0, 'u8') }}};
case 2: return signed ?
function readS16FromPointer(pointer) { return HEAP16[pointer >> 1]; } :
function readU16FromPointer(pointer) { return HEAPU16[pointer >> 1]; };
(pointer) => {{{ makeGetValue('pointer', 0, 'i16') }}} :
(pointer) => {{{ makeGetValue('pointer', 0, 'u16') }}}
case 4: return signed ?
function readS32FromPointer(pointer) { return HEAP32[pointer >> 2]; } :
function readU32FromPointer(pointer) { return HEAPU32[pointer >> 2]; };
(pointer) => {{{ makeGetValue('pointer', 0, 'i32') }}} :
(pointer) => {{{ makeGetValue('pointer', 0, 'u32') }}}
#if WASM_BIGINT
case 8: return signed ?
function readS64FromPointer(pointer) { return HEAP64[pointer >> 3]; } :
function readU64FromPointer(pointer) { return HEAPU64[pointer >> 3]; };
(pointer) => {{{ makeGetValue('pointer', 0, 'i64') }}} :
(pointer) => {{{ makeGetValue('pointer', 0, 'u64') }}}
#endif
default:
throw new TypeError(`invalid integer width (${width}): ${name}`);
Expand All @@ -321,18 +322,15 @@ var LibraryEmbind = {
$enumReadValueFromPointer__deps: [],
$enumReadValueFromPointer: (name, width, signed) => {
switch (width) {
case 1: return function(pointer) {
var heap = signed ? HEAP8 : HEAPU8;
return this['fromWireType'](heap[pointer]);
};
case 2: return function(pointer) {
var heap = signed ? HEAP16 : HEAPU16;
return this['fromWireType'](heap[pointer >> 1]);
};
case 4: return function(pointer) {
var heap = signed ? HEAP32 : HEAPU32;
return this['fromWireType'](heap[pointer >> 2]);
};
case 1: return signed ?
function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'i8') }}}) } :
function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'u8') }}}) };
case 2: return signed ?
function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'i16') }}}) } :
function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'u16') }}}) };
case 4: return signed ?
function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'i32') }}}) } :
function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'u32') }}}) };
default:
throw new TypeError(`invalid integer width (${width}): ${name}`);
}
Expand All @@ -342,10 +340,10 @@ var LibraryEmbind = {
$floatReadValueFromPointer: (name, width) => {
switch (width) {
case 4: return function(pointer) {
return this['fromWireType'](HEAPF32[pointer >> 2]);
return this['fromWireType']({{{ makeGetValue('pointer', 0, 'float') }}});
};
case 8: return function(pointer) {
return this['fromWireType'](HEAPF64[pointer >> 3]);
return this['fromWireType']({{{ makeGetValue('pointer', 0, 'double') }}});
};
default:
throw new TypeError(`invalid float width (${width}): ${name}`);
Expand Down Expand Up @@ -401,7 +399,7 @@ var LibraryEmbind = {
name,
'fromWireType': fromWireType,
'toWireType': toWireType,
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': integerReadValueFromPointer(name, size, minRange !== 0),
destructorFunction: null, // This type does not need a destructor
});
Expand Down Expand Up @@ -432,7 +430,7 @@ var LibraryEmbind = {
}
return value;
},
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': integerReadValueFromPointer(name, size, !isUnsignedType),
destructorFunction: null, // This type does not need a destructor
});
Expand Down Expand Up @@ -460,7 +458,7 @@ var LibraryEmbind = {
// https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue
return value;
},
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': floatReadValueFromPointer(name, size),
destructorFunction: null, // This type does not need a destructor
});
Expand All @@ -471,9 +469,13 @@ var LibraryEmbind = {
return this['fromWireType']({{{ makeGetValue('pointer', '0', 'i32') }}});
},

$readPointer: function(pointer) {
return this['fromWireType']({{{ makeGetValue('pointer', '0', '*') }}});
},

_embind_register_std_string__deps: [
'$readLatin1String', '$registerType',
'$simpleReadValueFromPointer', '$throwBindingError',
'$readPointer', '$throwBindingError',
'$stringToUTF8', '$lengthBytesUTF8', 'malloc', 'free'],
_embind_register_std_string: (rawType, name) => {
name = readLatin1String(name);
Expand Down Expand Up @@ -566,14 +568,14 @@ var LibraryEmbind = {
}
return base;
},
'argPackAdvance': 8,
'readValueFromPointer': simpleReadValueFromPointer,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': readPointer,
destructorFunction: (ptr) => _free(ptr),
});
},

_embind_register_std_wstring__deps: [
'$readLatin1String', '$registerType', '$simpleReadValueFromPointer',
'$readLatin1String', '$registerType', '$readPointer',
'$UTF16ToString', '$stringToUTF16', '$lengthBytesUTF16',
'$UTF32ToString', '$stringToUTF32', '$lengthBytesUTF32',
],
Expand All @@ -597,7 +599,7 @@ var LibraryEmbind = {
name,
'fromWireType': (value) => {
// Code mostly taken from _embind_register_std_string fromWireType
var length = HEAPU32[value >> 2];
var length = {{{ makeGetValue('value', 0, '*') }}};
var HEAP = getHeap();
var str;

Expand Down Expand Up @@ -639,7 +641,7 @@ var LibraryEmbind = {
}
return ptr;
},
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': simpleReadValueFromPointer,
destructorFunction: (ptr) => _free(ptr),
});
Expand All @@ -658,7 +660,7 @@ var LibraryEmbind = {
return rv;
},
'toWireType': (destructors, value) => Emval.toHandle(value),
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': simpleReadValueFromPointer,
destructorFunction: null, // This type does not need a destructor

Expand Down Expand Up @@ -687,18 +689,16 @@ var LibraryEmbind = {
var TA = typeMapping[dataTypeIndex];

function decodeMemoryView(handle) {
handle = handle >> 2;
var heap = HEAPU32;
var size = heap[handle]; // in elements
var data = heap[handle + 1]; // byte offset into emscripten heap
return new TA(heap.buffer, data, size);
var size = {{{ makeGetValue('handle', 0, '*') }}};
var data = {{{ makeGetValue('handle', POINTER_SIZE, '*') }}};
return new TA(HEAP8.buffer, data, size);
}

name = readLatin1String(name);
registerType(rawType, {
name,
'fromWireType': decodeMemoryView,
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': decodeMemoryView,
}, {
ignoreDuplicateRegistrations: true,
Expand Down Expand Up @@ -1098,7 +1098,7 @@ var LibraryEmbind = {
}
return ptr;
},
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': simpleReadValueFromPointer,
destructorFunction: rawDestructor,
}];
Expand Down Expand Up @@ -1210,7 +1210,7 @@ var LibraryEmbind = {
}
return ptr;
},
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': simpleReadValueFromPointer,
destructorFunction: rawDestructor,
}];
Expand Down Expand Up @@ -1340,14 +1340,14 @@ var LibraryEmbind = {
},

$init_RegisteredPointer__deps: [
'$simpleReadValueFromPointer',
'$readPointer',
'$RegisteredPointer_getPointee', '$RegisteredPointer_destructor',
'$RegisteredPointer_deleteObject', '$RegisteredPointer_fromWireType'],
$init_RegisteredPointer: () => {
RegisteredPointer.prototype.getPointee = RegisteredPointer_getPointee;
RegisteredPointer.prototype.destructor = RegisteredPointer_destructor;
RegisteredPointer.prototype['argPackAdvance'] = 8;
RegisteredPointer.prototype['readValueFromPointer'] = simpleReadValueFromPointer;
RegisteredPointer.prototype['argPackAdvance'] = GenericWireTypeSize;
RegisteredPointer.prototype['readValueFromPointer'] = readPointer;
RegisteredPointer.prototype['deleteObject'] = RegisteredPointer_deleteObject;
RegisteredPointer.prototype['fromWireType'] = RegisteredPointer_fromWireType;
},
Expand Down Expand Up @@ -2379,7 +2379,7 @@ var LibraryEmbind = {
return this.constructor.values[c];
},
'toWireType': (destructors, c) => c.value,
'argPackAdvance': 8,
'argPackAdvance': GenericWireTypeSize,
'readValueFromPointer': enumReadValueFromPointer(name, size, isSigned),
destructorFunction: null,
});
Expand Down
23 changes: 8 additions & 15 deletions system/include/emscripten/val.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,17 +191,14 @@ struct PackSize<Arg, Args...> {
union GenericWireType {
union {
unsigned u;
size_t s;
float f;
// Use uint32_t for pointer values. This limits us, for now, to 32-bit
// address ranges even on wasm64. This is enforced by assertions below.
// TODO(sbc): Allow full 64-bit address range here under wasm64, most
// likely by increasing the size of GenericWireType on wasm64.
uint32_t p;
void* p;
} w[2];
double d;
uint64_t u;
double d;
uint64_t u;
};
static_assert(sizeof(GenericWireType) == 8, "GenericWireType must be 8 bytes");
static_assert(sizeof(GenericWireType) == 2*sizeof(void*), "GenericWireType must be size of 2 pointers");
static_assert(alignof(GenericWireType) == 8, "GenericWireType must be 8-byte-aligned");

inline void writeGenericWireType(GenericWireType*& cursor, float wt) {
Expand All @@ -226,18 +223,14 @@ inline void writeGenericWireType(GenericWireType*& cursor, uint64_t wt) {

template<typename T>
void writeGenericWireType(GenericWireType*& cursor, T* wt) {
uintptr_t short_ptr = reinterpret_cast<uintptr_t>(wt);
assert(short_ptr <= UINT32_MAX);
cursor->w[0].p = short_ptr;
cursor->w[0].p = wt;
++cursor;
}

template<typename ElementType>
inline void writeGenericWireType(GenericWireType*& cursor, const memory_view<ElementType>& wt) {
uintptr_t short_ptr = reinterpret_cast<uintptr_t>(wt.data);
assert(short_ptr <= UINT32_MAX);
cursor->w[0].u = wt.size;
cursor->w[1].p = short_ptr;
cursor->w[0].s = wt.size;
cursor->w[1].p = (void*)wt.data;
++cursor;
}

Expand Down
4 changes: 4 additions & 0 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,10 @@ def setup_node_pthreads(self):
self.emcc_args += ['-Wno-pthreads-mem-growth', '-pthread']
if self.get_setting('MINIMAL_RUNTIME'):
self.skipTest('node pthreads not yet supported with MINIMAL_RUNTIME')
# Pthread support requires IMPORTED_MEMORY which depends on the JS API
# for creating 64-bit memories.
if self.get_setting('GLOBAL_BASE') == '4gb':
self.skipTest('no support for IMPORTED_MEMORY over 4gb yet')
self.js_engines = [config.NODE_JS]
self.node_args += shared.node_pthread_flags()

Expand Down