Skip to content

Commit 2ad7b10

Browse files
committed
Merge pull request #2311 from fadams/fix-64bit-long-return-issue
Fix 64bit long return issue
2 parents 9c613ff + deb4977 commit 2ad7b10

File tree

7 files changed

+82
-12
lines changed

7 files changed

+82
-12
lines changed

emscripten.py

+22-12
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ def fix_item(item):
477477
if '_rand' in exported_implemented_functions or '_srand' in exported_implemented_functions:
478478
basic_vars += ['___rand_seed']
479479

480-
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
480+
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)] + ['getTempRet%d' % i for i in range(10)]
481481
# function tables
482482
function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
483483
function_tables_impls = []
@@ -632,6 +632,10 @@ def math_fix(g):
632632
value = value|0;
633633
tempRet%d = value;
634634
}
635+
''' % (i, i) for i in range(10)]) + ''.join(['''
636+
function getTempRet%d() {
637+
return tempRet%d|0;
638+
}
635639
''' % (i, i) for i in range(10)])] + [PostSets.js + '\n'] + funcs_js + ['''
636640
%s
637641
@@ -644,9 +648,11 @@ def math_fix(g):
644648

645649
if not settings.get('SIDE_MODULE'):
646650
funcs_js.append('''
647-
Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) };
648-
Runtime.stackSave = function() { return asm['stackSave']() };
649-
Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
651+
Runtime.stackAlloc = asm['stackAlloc'];
652+
Runtime.stackSave = asm['stackSave'];
653+
Runtime.stackRestore = asm['stackRestore'];
654+
Runtime.setTempRet0 = asm['setTempRet0'];
655+
Runtime.getTempRet0 = asm['getTempRet0'];
650656
''')
651657

652658
# Set function table masks
@@ -1058,7 +1064,7 @@ def keyfunc(other):
10581064
if '_rand' in exported_implemented_functions or '_srand' in exported_implemented_functions:
10591065
basic_vars += ['___rand_seed']
10601066

1061-
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
1067+
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew', 'setTempRet0', 'getTempRet0']
10621068
# function tables
10631069
function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
10641070
function_tables_impls = []
@@ -1208,12 +1214,14 @@ def math_fix(g):
12081214
HEAP8[tempDoublePtr+6>>0] = HEAP8[ptr+6>>0];
12091215
HEAP8[tempDoublePtr+7>>0] = HEAP8[ptr+7>>0];
12101216
}
1211-
''' + ''.join(['''
1212-
function setTempRet%d(value) {
1217+
function setTempRet0(value) {
12131218
value = value|0;
1214-
tempRet%d = value;
1219+
tempRet0 = value;
1220+
}
1221+
function getTempRet0() {
1222+
return tempRet0|0;
12151223
}
1216-
''' % (i, i) for i in range(10)])] + funcs_js + ['''
1224+
'''] + funcs_js + ['''
12171225
%s
12181226
12191227
return %s;
@@ -1225,9 +1233,11 @@ def math_fix(g):
12251233

12261234
if not settings.get('SIDE_MODULE'):
12271235
funcs_js.append('''
1228-
Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) };
1229-
Runtime.stackSave = function() { return asm['stackSave']() };
1230-
Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
1236+
Runtime.stackAlloc = asm['stackAlloc'];
1237+
Runtime.stackSave = asm['stackSave'];
1238+
Runtime.stackRestore = asm['stackRestore'];
1239+
Runtime.setTempRet0 = asm['setTempRet0'];
1240+
Runtime.getTempRet0 = asm['getTempRet0'];
12311241
''')
12321242

12331243
# Set function table masks

src/runtime.js

+9
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ function unInline(name_, params) {
9696
}
9797

9898
var Runtime = {
99+
// When a 64 bit long is returned from a compiled function the least significant
100+
// 32 bit word is passed in the return value, but the most significant 32 bit
101+
// word is placed in tempRet0. This provides an accessor for that value.
102+
setTempRet0: function(value) {
103+
tempRet0 = value;
104+
},
105+
getTempRet0: function() {
106+
return tempRet0;
107+
},
99108
stackSave: function() {
100109
return STACKTOP;
101110
},

tests/return64bit/test.c

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
// This is just a trivial test function, the key bit of interest is that it returns a 64 bit long.
3+
long long test() {
4+
long long x = ((long long)1234 << 32) + 5678;
5+
return x;
6+
}

tests/return64bit/testbind.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This code represents a simple native JavaScript binding to a test C function
2+
// that returns a 64 bit long. Notice that the least significant 32 bits are
3+
// returned in the normal return value, but the most significant 32 bits are
4+
// returned via the accessor method Runtime.getTempRet0()
5+
6+
var Module = {
7+
'noExitRuntime' : true
8+
};
9+
10+
Module['runtest'] = function() {
11+
var low = _test();
12+
var high = Runtime.getTempRet0();
13+
14+
console.log("low = " + low);
15+
console.log("high = " + high);
16+
};
17+
18+

tests/return64bit/testbindend.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
})(); // End of self calling lambda used to wrap library.

tests/return64bit/testbindstart.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
(function() { // Start of self-calling lambda used to avoid polluting global namespace.
3+

tests/test_core.py

+22
Original file line numberDiff line numberDiff line change
@@ -6528,6 +6528,28 @@ def test_locale(self):
65286528
if self.emcc_args is None: return self.skip('needs emcc')
65296529
self.do_run_from_file(path_from_root('tests', 'test_locale.c'), path_from_root('tests', 'test_locale.out'))
65306530

6531+
def test_64bit_return_value(self):
6532+
# This test checks that the most significant 32 bits of a 64 bit long are correctly made available
6533+
# to native JavaScript applications that wish to interact with compiled code returning 64 bit longs.
6534+
# The MS 32 bits should be available in Runtime.getTempRet0() even when compiled with -O2 --closure 1
6535+
# Run with ./runner.py test_64bit_return_value
6536+
6537+
# Compile test.c and wrap it in a native JavaScript binding so we can call our compiled function from JS.
6538+
Popen([PYTHON, EMCC, path_from_root('tests', 'return64bit', 'test.c'), '--pre-js', path_from_root('tests', 'return64bit', 'testbindstart.js'), '--pre-js', path_from_root('tests', 'return64bit', 'testbind.js'), '--post-js', path_from_root('tests', 'return64bit', 'testbindend.js'), '-s', 'EXPORTED_FUNCTIONS=["_test"]', '-o', 'test.js', '-O2', '--closure', '1'], stdout=PIPE, stderr=PIPE).communicate()
6539+
6540+
# Simple test program to load the test.js binding library and call the binding to the
6541+
# C function returning the 64 bit long.
6542+
open(os.path.join(self.get_dir(), 'testrun.js'), 'w').write('''
6543+
var test = require("./test.js");
6544+
test.runtest();
6545+
''')
6546+
6547+
# Run the test and confirm the output is as expected.
6548+
if NODE_JS in JS_ENGINES:
6549+
out = run_js('testrun.js', engine=NODE_JS, full_output=True)
6550+
assert "low = 5678" in out
6551+
assert "high = 1234" in out
6552+
65316553
# Generate tests for everything
65326554
def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0,
65336555
typed_arrays=0, emcc_args=None, env=None):

0 commit comments

Comments
 (0)