diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-11-20 16:48:58 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-11-20 16:48:58 -0800 |
commit | 22b996181d14580413f720703b68b15a7270dd41 (patch) | |
tree | fba1b6895c81b8033a968e1cb00b31df91b9049d | |
parent | 954e3a1fc346d7f3b6cb35a9953db3d8e835cecf (diff) |
hack up support for 'glue' phase in js compiler, to just generate glue for backend output
-rwxr-xr-x | emscripten.py | 212 | ||||
-rw-r--r-- | src/compiler.js | 6 | ||||
-rw-r--r-- | src/jsifier.js | 12 | ||||
-rw-r--r-- | src/modules.js | 4 |
4 files changed, 41 insertions, 193 deletions
diff --git a/emscripten.py b/emscripten.py index f7e4b777..a7faed0d 100755 --- a/emscripten.py +++ b/emscripten.py @@ -782,11 +782,11 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, print >> sys.stderr, "FUNCS", funcs print >> sys.stderr, "META", metadata - - 1/0 # XXX - if DEBUG: logging.debug('emscript: js compiler glue') + # Integrate info from backend + settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] + metadata['declares'] + # Save settings to a file to work around v8 issue 1579 settings_file = temp_files.get('.txt').name def save_settings(): @@ -797,172 +797,33 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, s.close() save_settings() - # Phase 1 - pre + # Call js compiler if DEBUG: t = time.time() - pre_file = temp_files.get('.pre.ll').name - pre_input = ''.join(pre) + '\n' + meta - out = None - if not out: - open(pre_file, 'w').write(pre_input) - #print >> sys.stderr, 'running', str([settings_file, pre_file, 'pre'] + libraries).replace("'/", "'") # see funcs - out = jsrun.run_js(compiler, compiler_engine, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, - cwd=path_from_root('src')) - assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' - pre, forwarded_data = out.split('//FORWARDED_DATA:') - forwarded_file = temp_files.get('.json').name - pre_input = None - open(forwarded_file, 'w').write(forwarded_data) - if DEBUG: logging.debug(' emscript: phase 1 took %s seconds' % (time.time() - t)) - - indexed_functions = set() - forwarded_json = json.loads(forwarded_data) - for key in forwarded_json['Functions']['indexedFunctions'].iterkeys(): - indexed_functions.add(key) - - # Phase 2 - func - - cores = int(os.environ.get('EMCC_CORES') or multiprocessing.cpu_count()) - assert cores >= 1 - if cores > 1: - intended_num_chunks = int(round(cores * NUM_CHUNKS_PER_CORE)) - chunk_size = max(MIN_CHUNK_SIZE, total_ll_size / intended_num_chunks) - chunk_size += 3*len(meta) # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task) - chunk_size = min(MAX_CHUNK_SIZE, chunk_size) - else: - chunk_size = MAX_CHUNK_SIZE # if 1 core, just use the max chunk size - - if DEBUG: t = time.time() - if settings.get('ASM_JS'): - settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] - save_settings() - - chunks = cache_module.chunkify( - funcs, chunk_size, - None) - - #sys.exit(1) - #chunks = [chunks[0]] # pick specific chunks for debugging/profiling - - funcs = None - - # TODO: minimize size of forwarded data from funcs to what we actually need - - if len(chunks) > 0: - if cores == 1 and total_ll_size < MAX_CHUNK_SIZE: - assert len(chunks) == 1, 'no point in splitting up without multiple cores' - - if DEBUG: logging.debug(' emscript: phase 2 working on %d chunks %s (intended chunk size: %.2f MB, meta: %.2f MB, forwarded: %.2f MB, total: %.2f MB)' % (len(chunks), ('using %d cores' % cores) if len(chunks) > 1 else '', chunk_size/(1024*1024.), len(meta)/(1024*1024.), len(forwarded_data)/(1024*1024.), total_ll_size/(1024*1024.))) - - commands = [] - for i in range(len(chunks)): - funcs_file = temp_files.get('.func_%d.ll' % i).name - f = open(funcs_file, 'w') - f.write(chunks[i]) - chunks[i] = None # leave chunks array alive (need its length later) - f.write('\n') - f.write(meta) - f.close() - commands.append( - (i, funcs_file, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine,# + ['--prof'], - DEBUG) - ) - - if len(chunks) > 1: - pool = multiprocessing.Pool(processes=cores) - outputs = pool.map(process_funcs, commands, chunksize=1) - elif len(chunks) == 1: - outputs = [process_funcs(commands[0])] + out = jsrun.run_js(compiler, compiler_engine, [settings_file, ';', 'glue'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, + cwd=path_from_root('src')) + assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' + glue, forwarded_data = out.split('//FORWARDED_DATA:') - commands = None + #print >> sys.stderr, out - else: - outputs = [] + last_forwarded_json = forwarded_json = json.loads(forwarded_data) - chunks = None + '''indexed_functions = set() + for key in forwarded_json['Functions']['indexedFunctions'].iterkeys(): + indexed_functions.add(key)''' - outputs = [output.split('//FORWARDED_DATA:') for output in outputs] - for output in outputs: - assert len(output) == 2, 'Did not receive forwarded data in an output - process failed? We only got: ' + output[0][-3000:] + pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') - if DEBUG: logging.debug(' emscript: phase 2 took %s seconds' % (time.time() - t)) - if DEBUG: t = time.time() - - # merge forwarded data - if settings.get('ASM_JS'): - all_exported_functions = set(settings['EXPORTED_FUNCTIONS']) # both asm.js and otherwise - for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented - all_exported_functions.add('_' + additional_export) - exported_implemented_functions = set() - for func_js, curr_forwarded_data in outputs: - curr_forwarded_json = json.loads(curr_forwarded_data) - forwarded_json['Types']['hasInlineJS'] = forwarded_json['Types']['hasInlineJS'] or curr_forwarded_json['Types']['hasInlineJS'] - forwarded_json['Types']['usesSIMD'] = forwarded_json['Types']['usesSIMD'] or curr_forwarded_json['Types']['usesSIMD'] - forwarded_json['Types']['preciseI64MathUsed'] = forwarded_json['Types']['preciseI64MathUsed'] or curr_forwarded_json['Types']['preciseI64MathUsed'] - for key, value in curr_forwarded_json['Functions']['blockAddresses'].iteritems(): - forwarded_json['Functions']['blockAddresses'][key] = value - for key in curr_forwarded_json['Functions']['indexedFunctions'].iterkeys(): - indexed_functions.add(key) - if settings.get('ASM_JS'): - export_bindings = settings['EXPORT_BINDINGS'] - export_all = settings['EXPORT_ALL'] - for key in curr_forwarded_json['Functions']['implementedFunctions'].iterkeys(): - if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): - exported_implemented_functions.add(key) - for key, value in curr_forwarded_json['Functions']['unimplementedFunctions'].iteritems(): - forwarded_json['Functions']['unimplementedFunctions'][key] = value - for key, value in curr_forwarded_json['Functions']['neededTables'].iteritems(): - forwarded_json['Functions']['neededTables'][key] = value + #print >> sys.stderr, 'glue:', pre, '\n\n||||||||||||||||\n\n', post, '...............' + funcs_js = [funcs] if settings.get('ASM_JS'): parts = pre.split('// ASM_LIBRARY FUNCTIONS\n') if len(parts) > 1: pre = parts[0] - outputs.append([parts[1]]) - funcs_js = [output[0] for output in outputs] - - outputs = None - if DEBUG: logging.debug(' emscript: phase 2b took %s seconds' % (time.time() - t)) - if DEBUG: t = time.time() - - # calculations on merged forwarded data - forwarded_json['Functions']['indexedFunctions'] = {} - i = settings['FUNCTION_POINTER_ALIGNMENT'] # universal counter - if settings['ASM_JS']: i += settings['RESERVED_FUNCTION_POINTERS']*settings['FUNCTION_POINTER_ALIGNMENT'] - base_fp = i - table_counters = {} # table-specific counters - alias = settings['ASM_JS'] and settings['ALIASING_FUNCTION_POINTERS'] - sig = None - for indexed in indexed_functions: - if alias: - sig = forwarded_json['Functions']['implementedFunctions'].get(indexed) or forwarded_json['Functions']['unimplementedFunctions'].get(indexed) - assert sig, indexed - if sig not in table_counters: - table_counters[sig] = base_fp - curr = table_counters[sig] - table_counters[sig] += settings['FUNCTION_POINTER_ALIGNMENT'] - else: - curr = i - i += settings['FUNCTION_POINTER_ALIGNMENT'] - #logging.debug('function indexing ' + str([indexed, curr, sig])) - forwarded_json['Functions']['indexedFunctions'][indexed] = curr # make sure not to modify this python object later - we use it in indexize - - def split_32(x): - x = int(x) - return '%d,%d,%d,%d' % (x&255, (x >> 8)&255, (x >> 16)&255, (x >> 24)&255) + funcs_js.append(parts[1]) - indexing = forwarded_json['Functions']['indexedFunctions'] - def indexize_mem(js): - return re.sub(r"\"?'?{{ FI_([\w\d_$]+) }}'?\"?,0,0,0", lambda m: split_32(indexing.get(m.groups(0)[0]) or 0), js) - def indexize(js): - return re.sub(r"'{{ FI_([\w\d_$]+) }}'", lambda m: str(indexing.get(m.groups(0)[0]) or 0), js) - - blockaddrs = forwarded_json['Functions']['blockAddresses'] - def blockaddrsize_mem(js): - return re.sub(r'"?{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}"?,0,0,0', lambda m: split_32(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), js) - def blockaddrsize(js): - return re.sub(r'"?{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}"?', lambda m: str(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), js) - - pre = blockaddrsize(blockaddrsize_mem(indexize(indexize_mem(pre)))) + # calculations on merged forwarded data TODO if settings.get('ASM_JS'): # move postsets into the asm module @@ -978,24 +839,10 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, #if DEBUG: outfile.write('// funcs\n') - # forward - forwarded_data = json.dumps(forwarded_json) - forwarded_file = temp_files.get('.2.json').name - open(forwarded_file, 'w').write(indexize(forwarded_data)) - if DEBUG: logging.debug(' emscript: phase 2c took %s seconds' % (time.time() - t)) - - # Phase 3 - post - if DEBUG: t = time.time() - post_file = temp_files.get('.post.ll').name - open(post_file, 'w').write('\n') # no input, just processing of forwarded data - out = jsrun.run_js(compiler, compiler_engine, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE, - cwd=path_from_root('src')) - post, last_forwarded_data = out.split('//FORWARDED_DATA:') # if this fails, perhaps the process failed prior to printing forwarded data? - last_forwarded_json = json.loads(last_forwarded_data) - if settings.get('ASM_JS'): - post_funcs, post_rest = post.split('// EMSCRIPTEN_END_FUNCS\n') - post = post_rest + #print >> sys.stderr, '<<<<<<', post, '>>>>>>' + post_funcs = '' #, post_rest = post.split('// EMSCRIPTEN_END_FUNCS\n') + #post = post_rest # Move preAsms to their right place def move_preasm(m): @@ -1057,7 +904,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT'] basic_float_vars = ['NaN', 'Infinity'] - if forwarded_json['Types']['preciseI64MathUsed'] or \ + if metadata.get('preciseI64MathUsed') or \ forwarded_json['Functions']['libraryFunctions'].get('llvm_cttz_i32') or \ forwarded_json['Functions']['libraryFunctions'].get('llvm_ctlz_i32'): basic_vars += ['cttz_i8', 'ctlz_i8'] @@ -1102,7 +949,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, basic_funcs.append('extCall_%s' % sig) # calculate exports - exported_implemented_functions = list(exported_implemented_functions) + exported_implemented_functions = ['_main'] # XXX list(exported_implemented_functions) exported_implemented_functions.append('runPostSets') exports = [] if not simple: @@ -1117,7 +964,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, except: pass # If no named globals, only need externals - global_vars = map(lambda g: g['name'], filter(lambda g: settings['NAMED_GLOBALS'] or g.get('external') or g.get('unIndexable'), forwarded_json['Variables']['globals'].values())) + global_vars = [] global_funcs = ['_' + key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2] def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] @@ -1166,7 +1013,7 @@ var asm = (function(global, env, buffer) { var HEAPU32 = new global.Uint32Array(buffer); var HEAPF32 = new global.Float32Array(buffer); var HEAPF64 = new global.Float64Array(buffer); -''' % (asm_setup, "'use asm';" if not forwarded_json['Types']['hasInlineJS'] and not settings['SIDE_MODULE'] and settings['ASM_JS'] == 1 else "'almost asm';") + '\n' + asm_global_vars + ''' +''' % (asm_setup, "'use asm';" if not metadata.get('hasInlineJS') and not settings['SIDE_MODULE'] and settings['ASM_JS'] == 1 else "'almost asm';") + '\n' + asm_global_vars + ''' var __THREW__ = 0; var threwValue = 0; var setjmpId = 0; @@ -1287,15 +1134,10 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) }; outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table).replace('"', '')) for i in range(len(funcs_js)): # do this loop carefully to save memory - funcs_js_item = funcs_js[i] - funcs_js[i] = None - funcs_js_item = indexize(funcs_js_item) - funcs_js_item = blockaddrsize(funcs_js_item) - outfile.write(funcs_js_item) + outfile.write(funcs_js[i]) funcs_js = None - outfile.write(indexize(post)) - if DEBUG: logging.debug(' emscript: phase 3 took %s seconds' % (time.time() - t)) + outfile.write(post) outfile.close() diff --git a/src/compiler.js b/src/compiler.js index f523022b..7d768c3d 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -267,7 +267,7 @@ function compile(raw) { function runPhase(currPhase) { //printErr('// JS compiler in action, phase ' + currPhase + typeof lines + (lines === null)); phase = currPhase; - if (phase != 'pre') { + if (phase != 'pre' && phase != 'glue') { if (singlePhase) PassManager.load(read(forwardedDataFile)); if (phase == 'funcs') { @@ -313,7 +313,9 @@ B = new Benchmarker(); try { if (ll_file) { - if (ll_file.indexOf(String.fromCharCode(10)) == -1) { + if (phase === 'glue') { + compile(';'); + } else if (ll_file.indexOf(String.fromCharCode(10)) == -1) { compile(read(ll_file)); } else { compile(ll_file); // we are given raw .ll diff --git a/src/jsifier.js b/src/jsifier.js index acfb6365..731f92bc 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -28,7 +28,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (mainPass) { var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js'); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { // We will start to print out the data, but must do so carefully - we are // dealing with potentially *huge* strings. Convenient replacements and // manipulations may create in-memory copies, and we may OOM. @@ -72,7 +72,7 @@ function JSify(data, functionsOnly, givenFunctions) { LibraryManager.load(); //B.stop('jsifier-libload'); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { var libFuncsToInclude; if (INCLUDE_FULL_LIBRARY) { assert(!(BUILD_AS_SHARED_LIB || SIDE_MODULE), 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB/SIDE_MODULE set.') @@ -474,7 +474,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent - if ((!ASM_JS || phase == 'pre') && + if ((!ASM_JS || phase == 'pre' || phase == 'glue') && (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) { contentText += '\nModule["' + ident + '"] = ' + ident + ';'; } @@ -1704,7 +1704,7 @@ function JSify(data, functionsOnly, givenFunctions) { // if (!mainPass) { - if (phase == 'pre' && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) { + if ((phase == 'pre' || phase == 'glue') && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) { Variables.generatedGlobalBase = true; // Globals are done, here is the rest of static memory assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules @@ -1719,7 +1719,7 @@ function JSify(data, functionsOnly, givenFunctions) { var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable); print(generated.map(function(item) { return item.JS; }).join('\n')); - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { if (memoryInitialization.length > 0) { // apply postsets directly into the big memory initialization itemsDict.GlobalVariablePostSet = itemsDict.GlobalVariablePostSet.filter(function(item) { @@ -1780,7 +1780,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // Print out global variables and postsets TODO: batching - if (phase == 'pre') { + if (phase == 'pre' || phase == 'glue') { var legalizedI64sDefault = legalizedI64s; legalizedI64s = false; diff --git a/src/modules.js b/src/modules.js index 5d48ede2..79f494c0 100644 --- a/src/modules.js +++ b/src/modules.js @@ -483,6 +483,10 @@ var PassManager = { print('\n//FORWARDED_DATA:' + JSON.stringify({ Functions: { tables: Functions.tables } })); + } else if (phase == 'glue') { + print('\n//FORWARDED_DATA:' + JSON.stringify({ + Functions: Functions + })); } }, load: function(json) { |