~ubuntu-branches/ubuntu/trusty/emscripten/trusty-proposed

« back to all changes in this revision

Viewing changes to emscripten.py

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2014-01-19 14:12:40 UTC
  • mfrom: (1.2.4)
  • Revision ID: package-import@ubuntu.com-20140119141240-jg1l42cc158j59tn
Tags: 1.9.0~20140119~7dc8c2f-1
* New snapshot release (Closes: #733714)
* Provide sources for javascript and flash. Done in orig-tar.sh
  Available in third_party/websockify/include/web-socket-js/src/
  (Closes: #735903)

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
headers, for the libc implementation in JS).
10
10
'''
11
11
 
12
 
import os, sys, json, optparse, subprocess, re, time, multiprocessing, string
 
12
import os, sys, json, optparse, subprocess, re, time, multiprocessing, string, logging, shutil
13
13
 
 
14
from tools import shared
14
15
from tools import jsrun, cache as cache_module, tempfiles
15
16
from tools.response_file import read_response_file
16
17
 
25
26
  if hasattr(get_configuration, 'configuration'):
26
27
    return get_configuration.configuration
27
28
 
28
 
  from tools import shared
29
29
  configuration = shared.Configuration(environ=os.environ)
30
30
  get_configuration.configuration = configuration
31
31
  return configuration
39
39
  if len(blockaddrs) > 0:
40
40
    settings['NECESSARY_BLOCKADDRS'] = blockaddrs
41
41
 
42
 
NUM_CHUNKS_PER_CORE = 1.25
 
42
NUM_CHUNKS_PER_CORE = 1.0
43
43
MIN_CHUNK_SIZE = 1024*1024
44
44
MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes
45
45
 
46
46
STDERR_FILE = os.environ.get('EMCC_STDERR_FILE')
47
47
if STDERR_FILE:
48
48
  STDERR_FILE = os.path.abspath(STDERR_FILE)
49
 
  print >> sys.stderr, 'logging stderr in js compiler phase into %s' % STDERR_FILE
 
49
  logging.info('logging stderr in js compiler phase into %s' % STDERR_FILE)
50
50
  STDERR_FILE = open(STDERR_FILE, 'w')
51
51
 
52
 
def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)):
 
52
def process_funcs((i, funcs_file, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, DEBUG)):
53
53
  try:
54
 
    funcs_file = temp_files.get('.func_%d.ll' % i).name
55
 
    f = open(funcs_file, 'w')
56
 
    f.write(funcs)
57
 
    funcs = None
58
 
    f.write('\n')
59
 
    f.write(meta)
60
 
    f.close()
 
54
    #print >> sys.stderr, 'running', str([settings_file, funcs_file, 'funcs', forwarded_file] + libraries).replace("'/", "'") # can use this in src/compiler_funcs.html arguments,
 
55
    #                                                                                                                         # just copy temp dir to under this one
61
56
    out = jsrun.run_js(
62
57
      compiler,
63
58
      engine=compiler_engine,
68
63
  except KeyboardInterrupt:
69
64
    # Python 2.7 seems to lock up when a child process throws KeyboardInterrupt
70
65
    raise Exception()
71
 
  finally:
72
 
    tempfiles.try_delete(funcs_file)
73
 
  if DEBUG: print >> sys.stderr, '.'
 
66
  if DEBUG: logging.debug('.')
74
67
  return out
75
68
 
76
69
def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
91
84
  #   2 aka 'funcs': Process functions. We can parallelize this, working on each function independently.
92
85
  #   3 aka 'post' : Process globals, generate postamble and finishing touches.
93
86
 
94
 
  if DEBUG: print >> sys.stderr, 'emscript: ll=>js'
 
87
  if DEBUG: logging.debug('emscript: ll=>js')
95
88
 
96
89
  if jcache: jcache.ensure()
97
90
 
100
93
  ll = open(infile).read()
101
94
  scan(ll, settings)
102
95
  total_ll_size = len(ll)
103
 
  ll = None # allow collection
104
 
  if DEBUG: print >> sys.stderr, '  emscript: scan took %s seconds' % (time.time() - t)
 
96
  if DEBUG: logging.debug('  emscript: scan took %s seconds' % (time.time() - t))
105
97
 
106
98
  # Split input into the relevant parts for each phase
 
99
 
 
100
  if DEBUG: t = time.time()
 
101
 
107
102
  pre = []
108
103
  funcs = [] # split up functions here, for parallelism later
109
 
  meta = [] # needed by each function XXX
110
 
 
111
 
  if DEBUG: t = time.time()
112
 
  in_func = False
113
 
  ll_lines = open(infile).readlines()
114
 
  curr_func = None
115
 
  for line in ll_lines:
116
 
    if in_func:
117
 
      curr_func.append(line)
118
 
      if line.startswith('}'):
119
 
        in_func = False
120
 
        funcs.append((curr_func[0], ''.join(curr_func))) # use the entire line as the identifier
121
 
        # pre needs to know about all implemented functions, even for non-pre func
122
 
        pre.append(curr_func[0])
123
 
        pre.append(line)
124
 
        curr_func = None
125
 
    else:
126
 
      if line.startswith(';'): continue
127
 
      if line.startswith('define '):
128
 
        in_func = True
129
 
        curr_func = [line]
130
 
      elif line.find(' = type { ') > 0:
131
 
        pre.append(line) # type
132
 
      elif line.startswith('!'):
133
 
        if line.startswith('!llvm.module'): continue # we can ignore that
134
 
        meta.append(line) # metadata
135
 
      else:
136
 
        pre.append(line) # pre needs it so we know about globals in pre and funcs. So emit globals there
137
 
  ll_lines = None
138
 
  meta = ''.join(meta)
139
 
  if DEBUG and len(meta) > 1024*1024: print >> sys.stderr, 'emscript warning: large amounts of metadata, will slow things down'
140
 
  if DEBUG: print >> sys.stderr, '  emscript: split took %s seconds' % (time.time() - t)
 
104
 
 
105
  meta_start = ll.find('\n!')
 
106
  if meta_start > 0:
 
107
    meta = ll[meta_start:]
 
108
  else:
 
109
    meta = ''
 
110
    meta_start = -1
 
111
 
 
112
  start = ll.find('\n') if ll[0] == ';' else 0 # ignore first line, which contains ; ModuleID = '/dir name'
 
113
 
 
114
  func_start = start
 
115
  last = func_start
 
116
  while 1:
 
117
    last = func_start
 
118
    func_start = ll.find('\ndefine ', func_start)
 
119
    if func_start > last:
 
120
      pre.append(ll[last:min(func_start+1, meta_start) if meta_start > 0 else func_start+1] + '\n')
 
121
    if func_start < 0:
 
122
      pre.append(ll[last:meta_start] + '\n')
 
123
      break
 
124
    header = ll[func_start+1:ll.find('\n', func_start+1)+1]
 
125
    end = ll.find('\n}', func_start)
 
126
    last = end+3
 
127
    funcs.append((header, ll[func_start+1:last]))
 
128
    pre.append(header + '}\n')
 
129
    func_start = last
 
130
  ll = None
 
131
 
 
132
  if DEBUG and len(meta) > 1024*1024: logging.debug('emscript warning: large amounts of metadata, will slow things down')
 
133
  if DEBUG: logging.debug('  emscript: split took %s seconds' % (time.time() - t))
141
134
 
142
135
  if len(funcs) == 0:
143
 
    print >> sys.stderr, 'No functions to process. Make sure you prevented LLVM from eliminating them as dead (use EXPORTED_FUNCTIONS if necessary, see the FAQ)'
 
136
    logging.error('No functions to process. Make sure you prevented LLVM from eliminating them as dead (use EXPORTED_FUNCTIONS if necessary, see the FAQ)')
144
137
 
145
138
  #if DEBUG:
146
 
  #  print >> sys.stderr, '========= pre ================\n'
147
 
  #  print >> sys.stderr, ''.join(pre)
148
 
  #  print >> sys.stderr, '========== funcs ===============\n'
 
139
  #  logging.debug('========= pre ================\n')
 
140
  #  logging.debug(''.join(pre))
 
141
  #  logging.debug('========== funcs ===============\n')
149
142
  #  for func in funcs:
150
 
  #    print >> sys.stderr, '\n// ===\n\n', ''.join(func)
151
 
  #  print >> sys.stderr, '=========================\n'
 
143
  #    logging.debug('\n// ===\n\n', ''.join(func))
 
144
  #  logging.debug('=========================\n')
152
145
 
153
146
  # Save settings to a file to work around v8 issue 1579
154
147
  settings_file = temp_files.get('.txt').name
168
161
  if jcache:
169
162
    keys = [pre_input, settings_text, ','.join(libraries)]
170
163
    shortkey = jcache.get_shortkey(keys)
171
 
    if DEBUG_CACHE: print >>sys.stderr, 'shortkey', shortkey
 
164
    if DEBUG_CACHE: logging.debug('shortkey', shortkey)
172
165
 
173
166
    out = jcache.get(shortkey, keys)
174
167
 
181
174
      dfp.write("\n\n========================== libraries\n\n")
182
175
      dfp.write("\n".join(libraries))
183
176
      dfp.close()
184
 
      print >>sys.stderr, '  cache miss, key data dumped to %s' % dfpath
 
177
      logging.debug('  cache miss, key data dumped to %s' % dfpath)
185
178
 
186
 
    if out and DEBUG: print >> sys.stderr, '  loading pre from jcache'
 
179
    if out and DEBUG: logging.debug('  loading pre from jcache')
187
180
  if not out:
188
181
    open(pre_file, 'w').write(pre_input)
 
182
    #print >> sys.stderr, 'running', str([settings_file, pre_file, 'pre'] + libraries).replace("'/", "'") # see funcs
189
183
    out = jsrun.run_js(compiler, compiler_engine, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE,
190
184
                       cwd=path_from_root('src'))
191
185
    assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?'
192
186
    if jcache:
193
 
      if DEBUG: print >> sys.stderr, '  saving pre to jcache'
 
187
      if DEBUG: logging.debug('  saving pre to jcache')
194
188
      jcache.set(shortkey, keys, out)
195
189
  pre, forwarded_data = out.split('//FORWARDED_DATA:')
196
190
  forwarded_file = temp_files.get('.json').name
 
191
  pre_input = None
197
192
  open(forwarded_file, 'w').write(forwarded_data)
198
 
  if DEBUG: print >> sys.stderr, '  emscript: phase 1 took %s seconds' % (time.time() - t)
 
193
  if DEBUG: logging.debug('  emscript: phase 1 took %s seconds' % (time.time() - t))
199
194
 
200
195
  indexed_functions = set()
201
196
  forwarded_json = json.loads(forwarded_data)
209
204
  if cores > 1:
210
205
    intended_num_chunks = int(round(cores * NUM_CHUNKS_PER_CORE))
211
206
    chunk_size = max(MIN_CHUNK_SIZE, total_ll_size / intended_num_chunks)
212
 
    chunk_size += 3*len(meta) + len(forwarded_data)/3 # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task) and forwarded data (less expensive but potentially significant)
 
207
    chunk_size += 3*len(meta) # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task)
213
208
    chunk_size = min(MAX_CHUNK_SIZE, chunk_size)
214
209
  else:
215
210
    chunk_size = MAX_CHUNK_SIZE # if 1 core, just use the max chunk size
223
218
    funcs, chunk_size,
224
219
    jcache.get_cachename('emscript_files') if jcache else None)
225
220
 
 
221
  #sys.exit(1)
 
222
  #chunks = [chunks[0]] # pick specific chunks for debugging/profiling
 
223
 
226
224
  funcs = None
227
225
 
228
226
  if jcache:
238
236
      return True
239
237
    chunks = filter(load_from_cache, chunks)
240
238
    if len(cached_outputs) > 0:
241
 
      if out and DEBUG: print >> sys.stderr, '  loading %d funcchunks from jcache' % len(cached_outputs)
 
239
      if out and DEBUG: logging.debug('  loading %d funcchunks from jcache' % len(cached_outputs))
242
240
    else:
243
241
      cached_outputs = []
244
242
 
248
246
    if cores == 1 and total_ll_size < MAX_CHUNK_SIZE:
249
247
      assert len(chunks) == 1, 'no point in splitting up without multiple cores'
250
248
 
251
 
    if DEBUG: print >> sys.stderr, '  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.))
 
249
    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.)))
252
250
 
253
 
    commands = [
254
 
      (i, chunk, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)
255
 
      for i, chunk in enumerate(chunks)
256
 
    ]
 
251
    commands = []
 
252
    for i in range(len(chunks)):
 
253
      funcs_file = temp_files.get('.func_%d.ll' % i).name
 
254
      f = open(funcs_file, 'w')
 
255
      f.write(chunks[i])
 
256
      if not jcache:
 
257
        chunks[i] = None # leave chunks array alive (need its length later)
 
258
      f.write('\n')
 
259
      f.write(meta)
 
260
      f.close()
 
261
      commands.append(
 
262
        (i, funcs_file, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine,# + ['--prof'],
 
263
         DEBUG)
 
264
      )
257
265
 
258
266
    if len(chunks) > 1:
259
267
      pool = multiprocessing.Pool(processes=cores)
273
281
      keys = [settings_text, forwarded_data, chunk]
274
282
      shortkey = jcache.get_shortkey(keys)
275
283
      jcache.set(shortkey, keys, outputs[i])
276
 
    if out and DEBUG and len(chunks) > 0: print >> sys.stderr, '  saving %d funcchunks to jcache' % len(chunks)
 
284
    if out and DEBUG and len(chunks) > 0: logging.debug('  saving %d funcchunks to jcache' % len(chunks))
277
285
 
278
286
  chunks = None
279
287
 
283
291
  for output in outputs:
284
292
    assert len(output) == 2, 'Did not receive forwarded data in an output - process failed? We only got: ' + output[0][-3000:]
285
293
 
286
 
  if DEBUG: print >> sys.stderr, '  emscript: phase 2 took %s seconds' % (time.time() - t)
 
294
  if DEBUG: logging.debug('  emscript: phase 2 took %s seconds' % (time.time() - t))
287
295
  if DEBUG: t = time.time()
288
296
 
289
297
  # merge forwarded data
295
303
  for func_js, curr_forwarded_data in outputs:
296
304
    curr_forwarded_json = json.loads(curr_forwarded_data)
297
305
    forwarded_json['Types']['hasInlineJS'] = forwarded_json['Types']['hasInlineJS'] or curr_forwarded_json['Types']['hasInlineJS']
 
306
    forwarded_json['Types']['usesSIMD'] = forwarded_json['Types']['usesSIMD'] or curr_forwarded_json['Types']['usesSIMD']
298
307
    forwarded_json['Types']['preciseI64MathUsed'] = forwarded_json['Types']['preciseI64MathUsed'] or curr_forwarded_json['Types']['preciseI64MathUsed']
299
308
    for key, value in curr_forwarded_json['Functions']['blockAddresses'].iteritems():
300
309
      forwarded_json['Functions']['blockAddresses'][key] = value
319
328
  funcs_js = [output[0] for output in outputs]
320
329
 
321
330
  outputs = None
322
 
  if DEBUG: print >> sys.stderr, '  emscript: phase 2b took %s seconds' % (time.time() - t)
 
331
  if DEBUG: logging.debug('  emscript: phase 2b took %s seconds' % (time.time() - t))
323
332
  if DEBUG: t = time.time()
324
333
 
325
334
  # calculations on merged forwarded data
326
335
  forwarded_json['Functions']['indexedFunctions'] = {}
327
 
  i = 2 # universal counter
328
 
  if settings['ASM_JS']: i += 2*settings['RESERVED_FUNCTION_POINTERS']
 
336
  i = settings['FUNCTION_POINTER_ALIGNMENT'] # universal counter
 
337
  if settings['ASM_JS']: i += settings['RESERVED_FUNCTION_POINTERS']*settings['FUNCTION_POINTER_ALIGNMENT']
 
338
  base_fp = i
329
339
  table_counters = {} # table-specific counters
330
340
  alias = settings['ASM_JS'] and settings['ALIASING_FUNCTION_POINTERS']
331
341
  sig = None
334
344
      sig = forwarded_json['Functions']['implementedFunctions'].get(indexed) or forwarded_json['Functions']['unimplementedFunctions'].get(indexed)
335
345
      assert sig, indexed
336
346
      if sig not in table_counters:
337
 
        table_counters[sig] = 2 + 2*settings['RESERVED_FUNCTION_POINTERS']
 
347
        table_counters[sig] = base_fp
338
348
      curr = table_counters[sig]
339
 
      table_counters[sig] += 2
 
349
      table_counters[sig] += settings['FUNCTION_POINTER_ALIGNMENT']
340
350
    else:
341
351
      curr = i
342
 
      i += 2
343
 
    #print >> sys.stderr, 'function indexing', indexed, curr, sig
 
352
      i += settings['FUNCTION_POINTER_ALIGNMENT']
 
353
    #logging.debug('function indexing ' + str([indexed, curr, sig]))
344
354
    forwarded_json['Functions']['indexedFunctions'][indexed] = curr # make sure not to modify this python object later - we use it in indexize
345
355
 
346
356
  def split_32(x):
348
358
    return '%d,%d,%d,%d' % (x&255, (x >> 8)&255, (x >> 16)&255, (x >> 24)&255)
349
359
 
350
360
  indexing = forwarded_json['Functions']['indexedFunctions']
 
361
  def indexize_mem(js):
 
362
    return re.sub(r"\"?'?{{ FI_([\w\d_$]+) }}'?\"?,0,0,0", lambda m: split_32(indexing.get(m.groups(0)[0]) or 0), js)
351
363
  def indexize(js):
352
 
    # In the global initial allocation, we need to split up into Uint8 format
353
 
    ret = re.sub(r"\"?'?{{ FI_([\w\d_$]+) }}'?\"?,0,0,0", lambda m: split_32(indexing.get(m.groups(0)[0]) or 0), js)
354
 
    return re.sub(r"'{{ FI_([\w\d_$]+) }}'", lambda m: str(indexing.get(m.groups(0)[0]) or 0), ret)
 
364
    return re.sub(r"'{{ FI_([\w\d_$]+) }}'", lambda m: str(indexing.get(m.groups(0)[0]) or 0), js)
355
365
 
356
366
  blockaddrs = forwarded_json['Functions']['blockAddresses']
 
367
  def blockaddrsize_mem(js):
 
368
    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)
357
369
  def blockaddrsize(js):
358
 
    ret = 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)
359
 
    return re.sub(r'"?{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}"?', lambda m: str(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), ret)
 
370
    return re.sub(r'"?{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}"?', lambda m: str(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), js)
360
371
 
361
 
  pre = blockaddrsize(indexize(pre))
 
372
  pre = blockaddrsize(blockaddrsize_mem(indexize(indexize_mem(pre))))
362
373
 
363
374
  if settings.get('ASM_JS'):
364
375
    # move postsets into the asm module
378
389
  forwarded_data = json.dumps(forwarded_json)
379
390
  forwarded_file = temp_files.get('.2.json').name
380
391
  open(forwarded_file, 'w').write(indexize(forwarded_data))
381
 
  if DEBUG: print >> sys.stderr, '  emscript: phase 2c took %s seconds' % (time.time() - t)
 
392
  if DEBUG: logging.debug('  emscript: phase 2c took %s seconds' % (time.time() - t))
382
393
 
383
394
  # Phase 3 - post
384
395
  if DEBUG: t = time.time()
405
416
    simple = os.environ.get('EMCC_SIMPLE_ASM')
406
417
    class Counter:
407
418
      i = 0
 
419
      j = 0
408
420
    pre_tables = last_forwarded_json['Functions']['tables']['pre']
409
421
    del last_forwarded_json['Functions']['tables']['pre']
410
422
 
413
425
      Counter.i += 1
414
426
      bad = 'b' + str(i)
415
427
      params = ','.join(['p%d' % p for p in range(len(sig)-1)])
416
 
      coercions = ';'.join(['p%d = %sp%d%s' % (p, '+' if sig[p+1] != 'i' else '', p, '' if sig[p+1] != 'i' else '|0') for p in range(len(sig)-1)]) + ';'
417
 
      ret = '' if sig[0] == 'v' else ('return %s0' % ('+' if sig[0] != 'i' else ''))
 
428
      coercions = ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';'
 
429
      ret = '' if sig[0] == 'v' else ('return %s' % shared.JS.make_initializer(sig[0], settings))
418
430
      start = raw.index('[')
419
431
      end = raw.rindex(']')
420
432
      body = raw[start+1:end].split(',')
421
433
      for j in range(settings['RESERVED_FUNCTION_POINTERS']):
422
 
        body[2 + 2*j] = 'jsCall_%s_%s' % (sig, j)
 
434
        body[settings['FUNCTION_POINTER_ALIGNMENT'] * (1 + j)] = 'jsCall_%s_%s' % (sig, j)
 
435
      Counter.j = 0
423
436
      def fix_item(item):
424
 
        newline = '\n' in item
425
 
        return (bad if item.replace('\n', '') == '0' else item) + ('\n' if newline else '')
 
437
        Counter.j += 1
 
438
        newline = Counter.j % 30 == 29
 
439
        if item == '0': return bad if not newline else (bad + '\n')
 
440
        return item if not newline else (item + '\n')
426
441
      body = ','.join(map(fix_item, body))
427
 
      return ('function %s(%s) { %s %s(%d); %s }' % (bad, params, coercions, 'abort' if not settings['ASSERTIONS'] else 'nullFunc', i, ret), raw[:start+1] + body + raw[end:])
 
442
      return ('function %s(%s) { %s %s(%d); %s }' % (bad, params, coercions, 'abort' if not settings['ASSERTIONS'] else 'nullFunc', i, ret), ''.join([raw[:start+1], body, raw[end:]]))
 
443
 
428
444
    infos = [make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()]
 
445
 
429
446
    function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n// EMSCRIPTEN_END_FUNCS\n' + '\n'.join([info[1] for info in infos])
430
447
 
431
448
    asm_setup = ''
434
451
    math_envs = ['Math.min'] # TODO: move min to maths
435
452
    asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs])
436
453
 
437
 
    if settings['TO_FLOAT32']: maths += ['Math.toFloat32']
 
454
    if settings['PRECISE_F32']: maths += ['Math.fround']
438
455
 
439
456
    basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
440
457
    if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
459
476
 
460
477
    asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
461
478
    # function tables
462
 
    def asm_coerce(value, sig):
463
 
      if sig == 'v': return value
464
 
      return ('+' if sig != 'i' else '') + value + ('|0' if sig == 'i' else '')
465
 
 
466
479
    function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
467
480
    function_tables_impls = []
 
481
 
468
482
    for sig in last_forwarded_json['Functions']['tables'].iterkeys():
469
483
      args = ','.join(['a' + str(i) for i in range(1, len(sig))])
470
 
      arg_coercions = ' '.join(['a' + str(i) + '=' + asm_coerce('a' + str(i), sig[i]) + ';' for i in range(1, len(sig))])
471
 
      coerced_args = ','.join([asm_coerce('a' + str(i), sig[i]) for i in range(1, len(sig))])
472
 
      ret = ('return ' if sig[0] != 'v' else '') + asm_coerce('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0])
 
484
      arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))])
 
485
      coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))])
 
486
      ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings)
473
487
      function_tables_impls.append('''
474
488
  function dynCall_%s(index%s%s) {
475
489
    index = index|0;
479
493
''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret))
480
494
 
481
495
      for i in range(settings['RESERVED_FUNCTION_POINTERS']):
482
 
        jsret = ('return ' if sig[0] != 'v' else '') + asm_coerce('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0])
 
496
        jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0], settings)
483
497
        function_tables_impls.append('''
484
498
  function jsCall_%s_%s(%s) {
485
499
    %s
487
501
  }
488
502
 
489
503
''' % (sig, i, args, arg_coercions, jsret))
490
 
      from tools import shared
491
504
      shared.Settings.copy(settings)
492
505
      asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n'
493
506
      basic_funcs.append('invoke_%s' % sig)
539
552
 
540
553
    # finalize
541
554
 
542
 
    if DEBUG: print >> sys.stderr, 'asm text sizes', map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs.replace('\n', '\n  ')), len(exports), len(the_global), len(sending), len(receiving)
 
555
    if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs.replace('\n', '\n  ')), len(exports), len(the_global), len(sending), len(receiving)]))
543
556
 
544
557
    funcs_js = ['''
545
558
%s
560
573
  var HEAPU32 = new global.Uint32Array(buffer);
561
574
  var HEAPF32 = new global.Float32Array(buffer);
562
575
  var HEAPF64 = new global.Float64Array(buffer);
563
 
''' % (asm_setup, "'use asm';" if not forwarded_json['Types']['hasInlineJS'] and not settings['SIDE_MODULE'] else "'almost asm';") + '\n' + asm_global_vars + '''
 
576
''' % (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 + '''
564
577
  var __THREW__ = 0;
565
578
  var threwValue = 0;
566
579
  var setjmpId = 0;
567
580
  var undef = 0;
568
581
  var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
569
582
''' + ''.join(['''
570
 
  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs + '''
 
583
  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + ['  var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('PRECISE_F32') else '0.0')] + ['''
571
584
// EMSCRIPTEN_START_FUNCS
572
585
function stackAlloc(size) {
573
586
  size = size|0;
574
587
  var ret = 0;
575
588
  ret = STACKTOP;
576
589
  STACKTOP = (STACKTOP + size)|0;
577
 
''' + ('STACKTOP = ((STACKTOP + 3)>>2)<<2;' if settings['TARGET_X86'] else 'STACKTOP = ((STACKTOP + 7)>>3)<<3;') + '''
 
590
''' + ('STACKTOP = (STACKTOP + 3)&-4;' if settings['TARGET_X86'] else 'STACKTOP = (STACKTOP + 7)&-8;') + '''
578
591
  return ret|0;
579
592
}
580
593
function stackSave() {
680
693
            symbol_table[value] = str(i)
681
694
    outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table).replace('"', ''))
682
695
 
683
 
  for funcs_js_item in funcs_js: # do this loop carefully to save memory
 
696
  for i in range(len(funcs_js)): # do this loop carefully to save memory
 
697
    funcs_js_item = funcs_js[i]
 
698
    funcs_js[i] = None
684
699
    funcs_js_item = indexize(funcs_js_item)
685
700
    funcs_js_item = blockaddrsize(funcs_js_item)
686
701
    outfile.write(funcs_js_item)
687
702
  funcs_js = None
688
703
 
689
704
  outfile.write(indexize(post))
690
 
  if DEBUG: print >> sys.stderr, '  emscript: phase 3 took %s seconds' % (time.time() - t)
691
 
 
692
 
  outfile.close()
 
705
  if DEBUG: logging.debug('  emscript: phase 3 took %s seconds' % (time.time() - t))
 
706
 
 
707
  outfile.close()
 
708
 
 
709
# emscript_fast: emscript'en code using the 'fast' compilation path, using
 
710
#                an LLVM backend
 
711
# FIXME: this is just a copy-paste of normal emscript(), and we trample it
 
712
#        if the proper env var is set (see below). we should refactor to
 
713
#        share code between the two, once emscript_fast stabilizes (or,
 
714
#        leaving it separate like it is will make it trivial to rip out
 
715
#        if the experiment fails)
 
716
 
 
717
def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
 
718
             jcache=None, temp_files=None, DEBUG=None, DEBUG_CACHE=None):
 
719
  """Runs the emscripten LLVM-to-JS compiler. We parallelize as much as possible
 
720
 
 
721
  Args:
 
722
    infile: The path to the input LLVM assembly file.
 
723
    settings: JSON-formatted settings that override the values
 
724
      defined in src/settings.js.
 
725
    outfile: The file where the output is written.
 
726
  """
 
727
 
 
728
  assert(settings['ASM_JS']) # TODO: apply ASM_JS even in -O0 for fastcomp
 
729
 
 
730
  # Overview:
 
731
  #   * Run LLVM backend to emit JS. JS includes function bodies, memory initializer,
 
732
  #     and various metadata
 
733
  #   * Run compiler.js on the metadata to emit the shell js code, pre/post-ambles,
 
734
  #     JS library dependencies, etc.
 
735
 
 
736
  if DEBUG:
 
737
    logging.debug('emscript: llvm backend')
 
738
    t = time.time()
 
739
 
 
740
  temp_js = temp_files.get('.4.js').name
 
741
  backend_compiler = os.path.join(shared.LLVM_ROOT, 'llc')
 
742
  shared.jsrun.timeout_run(subprocess.Popen([backend_compiler, infile, '-march=js', '-filetype=asm', '-o', temp_js], stdout=subprocess.PIPE))
 
743
 
 
744
  if DEBUG:
 
745
    logging.debug('  emscript: llvm backend took %s seconds' % (time.time() - t))
 
746
    t = time.time()
 
747
 
 
748
  # Split up output
 
749
  backend_output = open(temp_js).read()
 
750
  #if DEBUG: print >> sys.stderr, backend_output
 
751
 
 
752
  start_funcs_marker = '// EMSCRIPTEN_START_FUNCTIONS'
 
753
  end_funcs_marker = '// EMSCRIPTEN_END_FUNCTIONS'
 
754
  metadata_split_marker = '// EMSCRIPTEN_METADATA'
 
755
 
 
756
  start_funcs = backend_output.index(start_funcs_marker)
 
757
  end_funcs = backend_output.rindex(end_funcs_marker)
 
758
  metadata_split = backend_output.rindex(metadata_split_marker)
 
759
 
 
760
  funcs = backend_output[start_funcs+len(start_funcs_marker):end_funcs]
 
761
  metadata_raw = backend_output[metadata_split+len(metadata_split_marker):]
 
762
  #if DEBUG: print >> sys.stderr, "METAraw", metadata_raw
 
763
  metadata = json.loads(metadata_raw)
 
764
  mem_init = backend_output[end_funcs+len(end_funcs_marker):metadata_split]
 
765
  #if DEBUG: print >> sys.stderr, "FUNCS", funcs
 
766
  #if DEBUG: print >> sys.stderr, "META", metadata
 
767
  #if DEBUG: print >> sys.stderr, "meminit", mem_init
 
768
 
 
769
  # function table masks
 
770
 
 
771
  table_sizes = {}
 
772
  for k, v in metadata['tables'].iteritems():
 
773
    table_sizes[k] = str(v.count(',')) # undercounts by one, but that is what we want
 
774
  funcs = re.sub(r"#FM_(\w+)#", lambda m: table_sizes[m.groups(0)[0]], funcs)
 
775
 
 
776
  # fix +float into float.0, if not running js opts
 
777
  if not settings['RUNNING_JS_OPTS']:
 
778
    def fix_dot_zero(m):
 
779
      num = m.group(3)
 
780
      # TODO: handle 0x floats?
 
781
      if num.find('.') < 0:
 
782
        e = num.find('e');
 
783
        if e < 0:
 
784
          num += '.0'
 
785
        else:
 
786
          num = num[:e] + '.0' + num[e:]
 
787
      return m.group(1) + m.group(2) + num
 
788
    funcs = re.sub(r'([(=,+\-*/%<>:?] *)\+(-?)((0x)?[0-9a-f]*\.?[0-9]+([eE][-+]?[0-9]+)?)', lambda m: fix_dot_zero(m), funcs)
 
789
 
 
790
  # js compiler
 
791
 
 
792
  if DEBUG: logging.debug('emscript: js compiler glue')
 
793
 
 
794
  # Settings changes
 
795
  assert settings['TARGET_LE32'] == 1
 
796
  settings['TARGET_LE32'] = 2
 
797
  if 'i64Add' in metadata['declares']: # TODO: others, once we split them up
 
798
    settings['PRECISE_I64_MATH'] = 2
 
799
    metadata['declares'] = filter(lambda i64_func: i64_func not in ['getHigh32', 'setHigh32', '__muldi3', '__divdi3', '__remdi3', '__udivdi3', '__uremdi3'], metadata['declares']) # FIXME: do these one by one as normal js lib funcs
 
800
 
 
801
  # Integrate info from backend
 
802
  settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] = list(
 
803
    set(settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE'] + map(shared.JS.to_nice_ident, metadata['declares'])).difference(
 
804
      map(lambda x: x[1:], metadata['implementedFunctions'])
 
805
    )
 
806
  ) + map(lambda x: x[1:], metadata['externs'])
 
807
  if metadata['simd']:
 
808
    settings['SIMD'] = 1
 
809
    settings['ASM_JS'] = 2
 
810
 
 
811
  # Save settings to a file to work around v8 issue 1579
 
812
  settings_file = temp_files.get('.txt').name
 
813
  def save_settings():
 
814
    global settings_text
 
815
    settings_text = json.dumps(settings, sort_keys=True)
 
816
    s = open(settings_file, 'w')
 
817
    s.write(settings_text)
 
818
    s.close()
 
819
  save_settings()
 
820
 
 
821
  # Call js compiler
 
822
  if DEBUG: t = time.time()
 
823
  out = jsrun.run_js(path_from_root('src', 'compiler.js'), compiler_engine, [settings_file, ';', 'glue'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE,
 
824
                     cwd=path_from_root('src'))
 
825
  assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?'
 
826
  glue, forwarded_data = out.split('//FORWARDED_DATA:')
 
827
 
 
828
  if DEBUG:
 
829
    logging.debug('  emscript: glue took %s seconds' % (time.time() - t))
 
830
    t = time.time()
 
831
 
 
832
  last_forwarded_json = forwarded_json = json.loads(forwarded_data)
 
833
 
 
834
  # merge in information from llvm backend
 
835
 
 
836
  last_forwarded_json['Functions']['tables'] = metadata['tables']
 
837
 
 
838
  '''indexed_functions = set()
 
839
  for key in forwarded_json['Functions']['indexedFunctions'].iterkeys():
 
840
    indexed_functions.add(key)'''
 
841
 
 
842
  pre, post = glue.split('// EMSCRIPTEN_END_FUNCS')
 
843
 
 
844
  #print >> sys.stderr, 'glue:', pre, '\n\n||||||||||||||||\n\n', post, '...............'
 
845
 
 
846
  # memory and global initializers
 
847
 
 
848
  global_initializers = ', '.join(map(lambda i: '{ func: function() { %s() } }' % i, metadata['initializers']))
 
849
 
 
850
  pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + Runtime.alignMemory(%d);
 
851
/* global initializers */ __ATINIT__.push(%s);
 
852
%s''' % (mem_init.count(',')+1, global_initializers, mem_init)) # XXX wrong size calculation!
 
853
 
 
854
  funcs_js = [funcs]
 
855
  if settings.get('ASM_JS'):
 
856
    parts = pre.split('// ASM_LIBRARY FUNCTIONS\n')
 
857
    if len(parts) > 1:
 
858
      pre = parts[0]
 
859
      funcs_js.append(parts[1])
 
860
 
 
861
  # calculations on merged forwarded data TODO
 
862
 
 
863
  # merge forwarded data
 
864
  assert settings.get('ASM_JS'), 'fastcomp is asm.js only'
 
865
  settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS']
 
866
  all_exported_functions = set(settings['EXPORTED_FUNCTIONS']) # both asm.js and otherwise
 
867
  for additional_export in settings['DEFAULT_LIBRARY_FUNCS_TO_INCLUDE']: # additional functions to export from asm, if they are implemented
 
868
    all_exported_functions.add('_' + additional_export)
 
869
  exported_implemented_functions = set()
 
870
  export_bindings = settings['EXPORT_BINDINGS']
 
871
  export_all = settings['EXPORT_ALL']
 
872
  for key in metadata['implementedFunctions'] + forwarded_json['Functions']['implementedFunctions'].keys(): # XXX perf
 
873
    if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')):
 
874
      exported_implemented_functions.add(key)
 
875
  implemented_functions = set(metadata['implementedFunctions'])
 
876
 
 
877
  # Add named globals
 
878
  named_globals = '\n'.join(['var %s = %s;' % (k, v) for k, v in metadata['namedGlobals'].iteritems()])
 
879
  pre = pre.replace('// === Body ===', '// === Body ===\n' + named_globals + '\n')
 
880
 
 
881
  #if DEBUG: outfile.write('// pre\n')
 
882
  outfile.write(pre)
 
883
  pre = None
 
884
 
 
885
  #if DEBUG: outfile.write('// funcs\n')
 
886
 
 
887
  if settings.get('ASM_JS'):
 
888
    # Move preAsms to their right place
 
889
    def move_preasm(m):
 
890
      contents = m.groups(0)[0]
 
891
      outfile.write(contents + '\n')
 
892
      return ''
 
893
    funcs_js[1] = re.sub(r'/\* PRE_ASM \*/(.*)\n', lambda m: move_preasm(m), funcs_js[1])
 
894
 
 
895
    funcs_js += ['\n// EMSCRIPTEN_END_FUNCS\n']
 
896
 
 
897
    simple = os.environ.get('EMCC_SIMPLE_ASM')
 
898
    class Counter:
 
899
      i = 0
 
900
      j = 0
 
901
    if 'pre' in last_forwarded_json['Functions']['tables']:
 
902
      pre_tables = last_forwarded_json['Functions']['tables']['pre']
 
903
      del last_forwarded_json['Functions']['tables']['pre']
 
904
    else:
 
905
      pre_tables = ''
 
906
 
 
907
    def make_table(sig, raw):
 
908
      i = Counter.i
 
909
      Counter.i += 1
 
910
      bad = 'b' + str(i)
 
911
      params = ','.join(['p%d' % p for p in range(len(sig)-1)])
 
912
      coerced_params = ','.join([shared.JS.make_coercion('p%d', sig[p+1], settings) % p for p in range(len(sig)-1)])
 
913
      coercions = ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';'
 
914
      def make_func(name, code):
 
915
        return 'function %s(%s) { %s %s }' % (name, params, coercions, code)
 
916
      Counter.pre = [make_func(bad, ('abort' if not settings['ASSERTIONS'] else 'nullFunc') + '(' + str(i) + ');' + (
 
917
        '' if sig[0] == 'v' else ('return %s' % shared.JS.make_initializer(sig[0], settings))
 
918
      ))]
 
919
      start = raw.index('[')
 
920
      end = raw.rindex(']')
 
921
      body = raw[start+1:end].split(',')
 
922
      for j in range(settings['RESERVED_FUNCTION_POINTERS']):
 
923
        body[settings['FUNCTION_POINTER_ALIGNMENT'] * (1 + j)] = 'jsCall_%s_%s' % (sig, j)
 
924
      Counter.j = 0
 
925
      def fix_item(item):
 
926
        Counter.j += 1
 
927
        newline = Counter.j % 30 == 29
 
928
        if item == '0': return bad if not newline else (bad + '\n')
 
929
        if item not in implemented_functions:
 
930
          # this is imported into asm, we must wrap it
 
931
          call_ident = item
 
932
          if call_ident in metadata['redirects']: call_ident = metadata['redirects'][call_ident]
 
933
          if not call_ident.startswith('_') and not call_ident.startswith('Math_'): call_ident = '_' + call_ident
 
934
          code = call_ident + '(' + coerced_params + ')'
 
935
          if sig[0] != 'v':
 
936
            code = 'return ' + shared.JS.make_coercion(code, sig[0], settings)
 
937
          code += ';'
 
938
          Counter.pre.append(make_func(item + '__wrapper', code))
 
939
          return item + '__wrapper'
 
940
        return item if not newline else (item + '\n')
 
941
      body = ','.join(map(fix_item, body))
 
942
      return ('\n'.join(Counter.pre), ''.join([raw[:start+1], body, raw[end:]]))
 
943
 
 
944
    infos = [make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()]
 
945
    Counter.pre = []
 
946
 
 
947
    function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n// EMSCRIPTEN_END_FUNCS\n' + '\n'.join([info[1] for info in infos])
 
948
 
 
949
    asm_setup = ''
 
950
    maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul']]
 
951
    fundamentals = ['Math', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array']
 
952
    math_envs = ['Math.min'] # TODO: move min to maths
 
953
    asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs])
 
954
 
 
955
    if settings['PRECISE_F32']: maths += ['Math.fround']
 
956
 
 
957
    basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
 
958
    if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
 
959
    if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR']
 
960
    if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
 
961
    if settings['ASSERTIONS']:
 
962
      basic_funcs += ['nullFunc']
 
963
      asm_setup += 'function nullFunc(x) { Module["printErr"]("Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?"); abort(x) }\n'
 
964
 
 
965
    basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT']
 
966
    basic_float_vars = ['NaN', 'Infinity']
 
967
 
 
968
    if metadata.get('preciseI64MathUsed') or \
 
969
       forwarded_json['Functions']['libraryFunctions'].get('llvm_cttz_i32') or \
 
970
       forwarded_json['Functions']['libraryFunctions'].get('llvm_ctlz_i32'):
 
971
      basic_vars += ['cttz_i8', 'ctlz_i8']
 
972
 
 
973
    if settings.get('DLOPEN_SUPPORT'):
 
974
      for sig in last_forwarded_json['Functions']['tables'].iterkeys():
 
975
        basic_vars.append('F_BASE_%s' % sig)
 
976
        asm_setup += '  var F_BASE_%s = %s;\n' % (sig, 'FUNCTION_TABLE_OFFSET' if settings.get('SIDE_MODULE') else '0') + '\n'
 
977
 
 
978
    asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
 
979
    # function tables
 
980
    function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
 
981
    function_tables_impls = []
 
982
 
 
983
    for sig in last_forwarded_json['Functions']['tables'].iterkeys():
 
984
      args = ','.join(['a' + str(i) for i in range(1, len(sig))])
 
985
      arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))])
 
986
      coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))])
 
987
      ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings)
 
988
      function_tables_impls.append('''
 
989
  function dynCall_%s(index%s%s) {
 
990
    index = index|0;
 
991
    %s
 
992
    %s;
 
993
  }
 
994
''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret))
 
995
 
 
996
      for i in range(settings['RESERVED_FUNCTION_POINTERS']):
 
997
        jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0], settings)
 
998
        function_tables_impls.append('''
 
999
  function jsCall_%s_%s(%s) {
 
1000
    %s
 
1001
    %s;
 
1002
  }
 
1003
 
 
1004
''' % (sig, i, args, arg_coercions, jsret))
 
1005
      shared.Settings.copy(settings)
 
1006
      asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n'
 
1007
      basic_funcs.append('invoke_%s' % sig)
 
1008
      if settings.get('DLOPEN_SUPPORT'):
 
1009
        asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n'
 
1010
        basic_funcs.append('extCall_%s' % sig)
 
1011
 
 
1012
    # calculate exports
 
1013
    exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers']
 
1014
    exported_implemented_functions.append('runPostSets')
 
1015
    exports = []
 
1016
    if not simple:
 
1017
      for export in exported_implemented_functions + asm_runtime_funcs + function_tables:
 
1018
        exports.append("%s: %s" % (export, export))
 
1019
      exports = '{ ' + ', '.join(exports) + ' }'
 
1020
    else:
 
1021
      exports = '_main'
 
1022
    # calculate globals
 
1023
    try:
 
1024
      del forwarded_json['Variables']['globals']['_llvm_global_ctors'] # not a true variable
 
1025
    except:
 
1026
      pass
 
1027
    # If no named globals, only need externals
 
1028
    global_vars = metadata['externs'] #+ forwarded_json['Variables']['globals']
 
1029
    global_funcs = list(set(['_' + key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2]).difference(set(global_vars)).difference(implemented_functions))
 
1030
    def math_fix(g):
 
1031
      return g if not g.startswith('Math_') else g.split('_')[1]
 
1032
    asm_global_funcs = ''.join(['  var ' + g.replace('.', '_') + '=global.' + g + ';\n' for g in maths]) + \
 
1033
                       ''.join(['  var ' + g + '=env.' + math_fix(g) + ';\n' for g in basic_funcs + global_funcs])
 
1034
    asm_global_vars = ''.join(['  var ' + g + '=env.' + g + '|0;\n' for g in basic_vars + global_vars])
 
1035
    # In linkable modules, we need to add some explicit globals for global variables that can be linked and used across modules
 
1036
    if settings.get('MAIN_MODULE') or settings.get('SIDE_MODULE'):
 
1037
      assert settings.get('TARGET_LE32'), 'TODO: support x86 target when linking modules (needs offset of 4 and not 8 here)'
 
1038
      for key, value in forwarded_json['Variables']['globals'].iteritems():
 
1039
        if value.get('linkable'):
 
1040
          init = forwarded_json['Variables']['indexedGlobals'][key] + 8 # 8 is Runtime.GLOBAL_BASE / STATIC_BASE
 
1041
          if settings.get('SIDE_MODULE'): init = '(H_BASE+' + str(init) + ')|0'
 
1042
          asm_global_vars += '  var %s=%s;\n' % (key, str(init))
 
1043
 
 
1044
    # sent data
 
1045
    the_global = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }'
 
1046
    sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }'
 
1047
    # received
 
1048
    if not simple:
 
1049
      receiving = ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables])
 
1050
    else:
 
1051
      receiving = 'var _main = Module["_main"] = asm;'
 
1052
 
 
1053
    # finalize
 
1054
 
 
1055
    if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs.replace('\n', '\n  ')), len(exports), len(the_global), len(sending), len(receiving)]))
 
1056
 
 
1057
    funcs_js = ['''
 
1058
%s
 
1059
function asmPrintInt(x, y) {
 
1060
  Module.print('int ' + x + ',' + y);// + ' ' + new Error().stack);
 
1061
}
 
1062
function asmPrintFloat(x, y) {
 
1063
  Module.print('float ' + x + ',' + y);// + ' ' + new Error().stack);
 
1064
}
 
1065
// EMSCRIPTEN_START_ASM
 
1066
var asm = (function(global, env, buffer) {
 
1067
  %s
 
1068
  var HEAP8 = new global.Int8Array(buffer);
 
1069
  var HEAP16 = new global.Int16Array(buffer);
 
1070
  var HEAP32 = new global.Int32Array(buffer);
 
1071
  var HEAPU8 = new global.Uint8Array(buffer);
 
1072
  var HEAPU16 = new global.Uint16Array(buffer);
 
1073
  var HEAPU32 = new global.Uint32Array(buffer);
 
1074
  var HEAPF32 = new global.Float32Array(buffer);
 
1075
  var HEAPF64 = new global.Float64Array(buffer);
 
1076
''' % (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 + '''
 
1077
  var __THREW__ = 0;
 
1078
  var threwValue = 0;
 
1079
  var setjmpId = 0;
 
1080
  var undef = 0;
 
1081
  var nan = +env.NaN, inf = +env.Infinity;
 
1082
  var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
 
1083
''' + ''.join(['''
 
1084
  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + ['  var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('PRECISE_F32') else '0.0')] + ['''
 
1085
// EMSCRIPTEN_START_FUNCS
 
1086
function stackAlloc(size) {
 
1087
  size = size|0;
 
1088
  var ret = 0;
 
1089
  ret = STACKTOP;
 
1090
  STACKTOP = (STACKTOP + size)|0;
 
1091
''' + ('STACKTOP = (STACKTOP + 3)&-4;' if settings['TARGET_X86'] else 'STACKTOP = (STACKTOP + 7)&-8;') + '''
 
1092
  return ret|0;
 
1093
}
 
1094
function stackSave() {
 
1095
  return STACKTOP|0;
 
1096
}
 
1097
function stackRestore(top) {
 
1098
  top = top|0;
 
1099
  STACKTOP = top;
 
1100
}
 
1101
function setThrew(threw, value) {
 
1102
  threw = threw|0;
 
1103
  value = value|0;
 
1104
  if ((__THREW__|0) == 0) {
 
1105
    __THREW__ = threw;
 
1106
    threwValue = value;
 
1107
  }
 
1108
}
 
1109
function copyTempFloat(ptr) {
 
1110
  ptr = ptr|0;
 
1111
  HEAP8[tempDoublePtr] = HEAP8[ptr];
 
1112
  HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0];
 
1113
  HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0];
 
1114
  HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0];
 
1115
}
 
1116
function copyTempDouble(ptr) {
 
1117
  ptr = ptr|0;
 
1118
  HEAP8[tempDoublePtr] = HEAP8[ptr];
 
1119
  HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0];
 
1120
  HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0];
 
1121
  HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0];
 
1122
  HEAP8[tempDoublePtr+4|0] = HEAP8[ptr+4|0];
 
1123
  HEAP8[tempDoublePtr+5|0] = HEAP8[ptr+5|0];
 
1124
  HEAP8[tempDoublePtr+6|0] = HEAP8[ptr+6|0];
 
1125
  HEAP8[tempDoublePtr+7|0] = HEAP8[ptr+7|0];
 
1126
}
 
1127
''' + ''.join(['''
 
1128
function setTempRet%d(value) {
 
1129
  value = value|0;
 
1130
  tempRet%d = value;
 
1131
}
 
1132
''' % (i, i) for i in range(10)])] + funcs_js + ['''
 
1133
  %s
 
1134
 
 
1135
  return %s;
 
1136
})
 
1137
// EMSCRIPTEN_END_ASM
 
1138
(%s, %s, buffer);
 
1139
%s;
 
1140
''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n  '), exports, the_global, sending, receiving)]
 
1141
 
 
1142
    if not settings.get('SIDE_MODULE'):
 
1143
      funcs_js.append('''
 
1144
Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) };
 
1145
Runtime.stackSave = function() { return asm['stackSave']() };
 
1146
Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
 
1147
''')
 
1148
 
 
1149
    # Set function table masks
 
1150
    masks = {}
 
1151
    max_mask = 0
 
1152
    for sig, table in last_forwarded_json['Functions']['tables'].iteritems():
 
1153
      mask = table.count(',')
 
1154
      masks[sig] = str(mask)
 
1155
      max_mask = max(mask, max_mask)
 
1156
    def function_table_maskize(js, masks):
 
1157
      def fix(m):
 
1158
        sig = m.groups(0)[0]
 
1159
        return masks[sig]
 
1160
      return re.sub(r'{{{ FTM_([\w\d_$]+) }}}', lambda m: fix(m), js) # masks[m.groups(0)[0]]
 
1161
    funcs_js = map(lambda js: function_table_maskize(js, masks), funcs_js)
 
1162
 
 
1163
    if settings.get('DLOPEN_SUPPORT'):
 
1164
      funcs_js.append('''
 
1165
  asm.maxFunctionIndex = %(max_mask)d;
 
1166
  DLFCN.registerFunctions(asm, %(max_mask)d+1, %(sigs)s, Module);
 
1167
  Module.SYMBOL_TABLE = SYMBOL_TABLE;
 
1168
''' % { 'max_mask': max_mask, 'sigs': str(map(str, last_forwarded_json['Functions']['tables'].keys())) })
 
1169
 
 
1170
  else:
 
1171
    function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()])
 
1172
    outfile.write(function_tables_defs)
 
1173
    funcs_js = ['''
 
1174
// EMSCRIPTEN_START_FUNCS
 
1175
'''] + funcs_js + ['''
 
1176
// EMSCRIPTEN_END_FUNCS
 
1177
''']
 
1178
 
 
1179
  # Create symbol table for self-dlopen
 
1180
  if settings.get('DLOPEN_SUPPORT'):
 
1181
    symbol_table = {}
 
1182
    for k, v in forwarded_json['Variables']['indexedGlobals'].iteritems():
 
1183
       if forwarded_json['Variables']['globals'][k]['named']:
 
1184
         symbol_table[k] = str(v + forwarded_json['Runtime']['GLOBAL_BASE'])
 
1185
    for raw in last_forwarded_json['Functions']['tables'].itervalues():
 
1186
      if raw == '': continue
 
1187
      table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(","))
 
1188
      for i in range(len(table)):
 
1189
        value = table[i]
 
1190
        if value != '0':
 
1191
          if settings.get('SIDE_MODULE'):
 
1192
            symbol_table[value] = 'FUNCTION_TABLE_OFFSET+' + str(i)
 
1193
          else:
 
1194
            symbol_table[value] = str(i)
 
1195
    outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table).replace('"', ''))
 
1196
 
 
1197
  for i in range(len(funcs_js)): # do this loop carefully to save memory
 
1198
    outfile.write(funcs_js[i])
 
1199
  funcs_js = None
 
1200
 
 
1201
  outfile.write(post)
 
1202
 
 
1203
  outfile.close()
 
1204
 
 
1205
  if DEBUG: logging.debug('  emscript: final python processing took %s seconds' % (time.time() - t))
 
1206
 
 
1207
if os.environ.get('EMCC_FAST_COMPILER'):
 
1208
  emscript = emscript_fast
693
1209
 
694
1210
def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBUG_CACHE):
695
1211
  # Prepare settings for serialization to JSON.
707
1223
      relooper = cache.get_path('relooper.js')
708
1224
    settings.setdefault('RELOOPER', relooper)
709
1225
    if not os.path.exists(relooper):
710
 
      from tools import shared
711
1226
      shared.Building.ensure_relooper(relooper)
712
 
 
 
1227
  
 
1228
  settings.setdefault('STRUCT_INFO', cache.get_path('struct_info.compiled.json'))
 
1229
  struct_info = settings.get('STRUCT_INFO')
 
1230
  
 
1231
  if not os.path.exists(struct_info):
 
1232
    shared.Building.ensure_struct_info(struct_info)
 
1233
  
713
1234
  emscript(args.infile, settings, args.outfile, libraries, compiler_engine=compiler_engine,
714
1235
           jcache=jcache, temp_files=temp_files, DEBUG=DEBUG, DEBUG_CACHE=DEBUG_CACHE)
715
1236
 
779
1300
  keywords, positional = parser.parse_args()
780
1301
 
781
1302
  if not keywords.suppressUsageWarning:
782
 
    print >> sys.stderr, '''
 
1303
    logging.warning('''
783
1304
==============================================================
784
1305
WARNING: You should normally never use this! Use emcc instead.
785
1306
==============================================================
786
 
  '''
 
1307
  ''')
787
1308
 
788
1309
  if len(positional) != 1:
789
1310
    raise RuntimeError('Must provide exactly one positional argument. Got ' + str(len(positional)) + ': "' + '", "'.join(positional) + '"')
806
1327
    temp_files = tempfiles.TempFiles(temp_dir)
807
1328
 
808
1329
  if keywords.compiler is None:
809
 
    from tools import shared
810
1330
    keywords.compiler = shared.COMPILER_ENGINE
811
1331
 
812
1332
  if keywords.verbose is None: