aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-03-04 17:07:25 -0800
committerAlon Zakai <alonzakai@gmail.com>2013-03-04 17:07:25 -0800
commit26a3a085fc1be4f3d648a2e5e6b05c54f4b04a00 (patch)
treee791121bed35a7bfe1377ac8eca0151309842c24
parent719e805ecfe14ec3582bb8285527e4a9fde5cb21 (diff)
emscripten_jcache_printf
-rwxr-xr-xemcc17
-rw-r--r--src/library.js17
-rw-r--r--system/include/emscripten/emscripten.h22
-rw-r--r--tests/hello_libcxx_mod2.cpp10
-rw-r--r--tests/hello_libcxx_mod2a.cpp11
-rwxr-xr-xtests/runner.py27
-rw-r--r--tools/shared.py28
7 files changed, 130 insertions, 2 deletions
diff --git a/emcc b/emcc
index b3581ea8..f644b924 100755
--- a/emcc
+++ b/emcc
@@ -421,6 +421,22 @@ Options that are modified or new in %s include:
'jsfuncs' will be cached. So avoid modifying
globals to let caching work fully.
+ To work around the problem mentioned in the
+ previous paragraph, you can use
+
+ emscripten_jcache_printf
+
+ when adding debug printfs to your code. That
+ function is specially preprocessed so that it
+ does not create a constant string global for
+ its first argument. See emscripten.h for more
+ details. Note in particular that you need to
+ already have a call to that function in your
+ code *before* you add one and do an incremental
+ build, so that adding an external reference
+ (also a global property) does not invalidate
+ everything.
+
--clear-cache Manually clears the cache of compiled
emscripten system libraries (libc++,
libc++abi, libc). This is normally
@@ -978,6 +994,7 @@ try:
for input_file in input_files:
if input_file.endswith(SOURCE_SUFFIXES):
if DEBUG: print >> sys.stderr, 'emcc: compiling source file: ', input_file
+ input_file = shared.Building.preprocess(input_file, in_temp(uniquename(input_file)))
output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
temp_files.append(output_file)
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
diff --git a/src/library.js b/src/library.js
index 1676a82c..1cd7e3f5 100644
--- a/src/library.js
+++ b/src/library.js
@@ -7368,6 +7368,23 @@ LibraryManager.library = {
emscripten_random: function() {
return Math.random();
},
+
+ emscripten_jcache_printf___deps: ['_formatString'],
+ emscripten_jcache_printf_: function(varargs) {
+ var MAX = 10240;
+ if (!_emscripten_jcache_printf_.buffer) {
+ _emscripten_jcache_printf_.buffer = _malloc(MAX);
+ }
+ var i = 0;
+ do {
+ var curr = {{{ makeGetValue('varargs', 'i*4', 'i8') }}};
+ {{{ makeSetValue('_emscripten_jcache_printf_.buffer', 'i', 'curr', 'i8') }}};
+ i++;
+ assert(i*4 < MAX);
+ } while (curr != 0);
+ Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs + i*4)).replace('\\n', ''));
+ Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free
+ },
};
function autoAddDeps(object, name) {
diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h
index 93551f39..e6670869 100644
--- a/system/include/emscripten/emscripten.h
+++ b/system/include/emscripten/emscripten.h
@@ -345,6 +345,28 @@ extern void EMSCRIPTEN_PROFILE_INIT(int max);
extern void EMSCRIPTEN_PROFILE_BEGIN(int id);
extern void EMSCRIPTEN_PROFILE_END(int id);
+/*
+ * jcache-friendly printf. printf in general will receive a string
+ * literal, which becomes a global constant, which invalidates all
+ * jcache entries. emscripten_jcache_printf is parsed before
+ * clang into something without any string literals, so you can
+ * add such printouts to your code and only the (chunk containing
+ * the) function you modify will be invalided and recompiled.
+ *
+ * Note in particular that you need to already have a call to this
+ * function in your code *before* you add one and do an incremental
+ * build, so that adding an external reference does not invalidate
+ * everything.
+ *
+ * This function assumes the first argument is a string literal
+ * (otherwise you don't need it), and the other arguments, if any,
+ * are neither strings nor complex expressions (but just simple
+ * variables). (You can create a variable to store a complex
+ * expression on the previous line, if necessary.)
+ */
+void emscripten_jcache_printf(const char *format, ...);
+void emscripten_jcache_printf_(...); /* internal use */
+
#ifdef __cplusplus
}
#endif
diff --git a/tests/hello_libcxx_mod2.cpp b/tests/hello_libcxx_mod2.cpp
new file mode 100644
index 00000000..b18a523a
--- /dev/null
+++ b/tests/hello_libcxx_mod2.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+#include <emscripten.h>
+
+int main()
+{
+ std::cout << "hello, world!" << std::endl;
+ emscripten_jcache_printf("waka %d waka\n", 5);
+ return 0;
+}
+
diff --git a/tests/hello_libcxx_mod2a.cpp b/tests/hello_libcxx_mod2a.cpp
new file mode 100644
index 00000000..f48ad4fe
--- /dev/null
+++ b/tests/hello_libcxx_mod2a.cpp
@@ -0,0 +1,11 @@
+#include <iostream>
+#include <emscripten.h>
+
+int main()
+{
+ std::cout << "hello, world!" << std::endl;
+ emscripten_jcache_printf("waka %d waka\n", 5);
+ emscripten_jcache_printf("yet another printf %.2f %d\n", 5.5, 66);
+ return 0;
+}
+
diff --git a/tests/runner.py b/tests/runner.py
index 120a965d..29c15950 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -9749,6 +9749,23 @@ seeked= file.
if optimize_normally: del os.environ['EMCC_OPTIMIZE_NORMALLY']
del os.environ['EMCC_DEBUG']
+ def test_jcache_printf(self):
+ open(self.in_dir('src.cpp'), 'w').write(r'''
+ #include <stdio.h>
+ #include <stdint.h>
+ #include <emscripten.h>
+ int main() {
+ emscripten_jcache_printf("hello world\n");
+ emscripten_jcache_printf("hello %d world\n", 5);
+ emscripten_jcache_printf("hello %.3f world\n", 123.456789123);
+ emscripten_jcache_printf("hello %llx world\n", 0x1234567811223344ULL);
+ return 0;
+ }
+ ''')
+ Popen([PYTHON, EMCC, self.in_dir('src.cpp')]).communicate()
+ output = run_js('a.out.js')
+ self.assertIdentical('hello world\nhello 5 world\nhello 123.457 world\nhello 1234567811223300 world\n', output)
+
def test_conftest_s_flag_passing(self):
open(os.path.join(self.get_dir(), 'conftest.c'), 'w').write(r'''
int main() {
@@ -11992,9 +12009,17 @@ fi
# finally, build a file close to the previous, to see that some chunks are found in the cache and some not
(['--jcache'], 'hello_libcxx_mod1.cpp', False, True, True, True, True, True, []), # win on pre, mix on funcs, mix on jsfuncs
(['--jcache'], 'hello_libcxx_mod1.cpp', False, True, False, True, False, True, []),
+ (None, None, None, None, None, None, None, None, None), # clear
+ (['--jcache'], 'hello_libcxx_mod2.cpp', True, False, True, False, True, False, []), # load into cache
+ (['--jcache'], 'hello_libcxx_mod2a.cpp', False, True, True, True, True, True, []) # add a printf, do not lose everything
]:
- print >> sys.stderr, args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected
self.clear()
+ if args is None:
+ Cache.erase()
+ continue
+
+ print >> sys.stderr, args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected
+
out, err = Popen([PYTHON, EMCC, '-O2', path_from_root('tests', input_file)] + args, stdout=PIPE, stderr=PIPE).communicate()
errtail = err.split('emcc invocation')[-1]
self.assertContained('hello, world!', run_js('a.out.js'), errtail)
diff --git a/tools/shared.py b/tools/shared.py
index 4b12942c..c5774c1a 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -1,4 +1,4 @@
-import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess, hashlib, cPickle, zlib
+import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess, hashlib, cPickle, zlib, re
from subprocess import Popen, PIPE, STDOUT
from tempfile import mkstemp
@@ -1202,6 +1202,32 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
print >> sys.stderr, 'bootstrapping relooper failed. You may need to manually create src/relooper.js by compiling it, see src/relooper/emscripten'
1/0
+ @staticmethod
+ def preprocess(infile, outfile):
+ '''
+ Preprocess source C/C++ in some special ways that emscripten needs. Returns
+ a filename (potentially the same one if nothing was changed).
+
+ Currently this only does emscripten_jcache_printf(..) rewriting.
+ '''
+ src = open(infile).read() # stack warning on jcacheprintf! in docs # add jcache printf test separatrely, for content of printf
+ if 'emscripten_jcache_printf' not in src: return infile
+ def fix(m):
+ text = m.groups(0)[0]
+ assert text.count('(') == 1 and text.count(')') == 1, 'must have simple expressions in emscripten_jcache_printf calls, no parens'
+ assert text.count('"') == 2, 'must have simple expressions in emscripten_jcache_printf calls, no strings as varargs parameters'
+ start = text.index('(')
+ end = text.rindex(')')
+ args = text[start+1:end].split(',')
+ args = map(lambda x: x.strip(), args)
+ if args[0][0] == '"':
+ # flatten out
+ args = map(lambda x: str(ord(x)), args[0][1:len(args[0])-1]) + ['0'] + args[1:]
+ return 'emscripten_jcache_printf_(' + ','.join(args) + ')'
+ src = re.sub(r'(emscripten_jcache_printf\([^)]+\))', lambda m: fix(m), src)
+ open(outfile, 'w').write(src)
+ return outfile
+
# Permanent cache for dlmalloc and stdlibc++
class Cache:
dirname = os.environ.get('EM_CACHE')