~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to src/modules.js

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//"use strict";
 
2
 
 
3
// Various namespace-like modules
 
4
 
 
5
var STACK_ALIGN = TARGET_X86 ? 4 : 8;
 
6
 
 
7
var LLVM = {
 
8
  LINKAGES: set('private', 'linker_private', 'linker_private_weak', 'linker_private_weak_def_auto', 'internal',
 
9
                'available_externally', 'linkonce', 'common', 'weak', 'appending', 'extern_weak', 'linkonce_odr',
 
10
                'weak_odr', 'externally_visible', 'dllimport', 'dllexport', 'unnamed_addr', 'thread_local'),
 
11
  VISIBILITIES: set('default', 'hidden', 'protected'),
 
12
  PARAM_ATTR: set('noalias', 'signext', 'zeroext', 'inreg', 'sret', 'nocapture', 'nest'),
 
13
  FUNC_ATTR: set('hidden', 'nounwind', 'define', 'inlinehint', '{'),
 
14
  CALLING_CONVENTIONS: set('ccc', 'fastcc', 'coldcc', 'cc10', 'x86_fastcallcc', 'x86_stdcallcc', 'cc11'),
 
15
  ACCESS_OPTIONS: set('volatile', 'atomic'),
 
16
  INVOKE_MODIFIERS: set('alignstack', 'alwaysinline', 'inlinehint', 'naked', 'noimplicitfloat', 'noinline', 'alwaysinline attribute.', 'noredzone', 'noreturn', 'nounwind', 'optsize', 'readnone', 'readonly', 'ssp', 'sspreq'),
 
17
  SHIFTS: set('ashr', 'lshr', 'shl'),
 
18
  PHI_REACHERS: set('branch', 'switch', 'invoke', 'indirectbr'),
 
19
  EXTENDS: set('sext', 'zext'),
 
20
  COMPS: set('icmp', 'fcmp'),
 
21
  CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui'),
 
22
  INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
 
23
};
 
24
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
 
25
 
 
26
var Debugging = {
 
27
  handleMetadata: function(lines) {
 
28
    for (var i = lines.length-1; i >= 0; i--) {
 
29
      if (/^!\d+ = metadata .*/.exec(lines[i])) {
 
30
        Debugging.processMetadata(lines);
 
31
        break;
 
32
      }
 
33
    }
 
34
  },
 
35
 
 
36
  processMetadata: function(lines) {
 
37
    var llvmLineToMetadata = {};
 
38
    var metadataToSourceLine = {};
 
39
    var metadataToParentMetadata = {};
 
40
    var metadataToFilename = {};
 
41
 
 
42
    var form1 = new RegExp(/^  .*, !dbg !(\d+) *$/);
 
43
    var form2 = new RegExp(/^  .*, !dbg !(\d+) *; .*$/);
 
44
    var form3 = new RegExp(/^!(\d+) = metadata !{i32 (\d+), (?:i32 \d+|null), metadata !(\d+), .*}$/);
 
45
    var form3a = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:i32 \d+|metadata !\d+), (?:i32 \d+|null), (?:i32 \d+|null), metadata !(\d+), (?:i32 \d+|null)}.*/);
 
46
    var form3ab = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:metadata !\d+|i32 \d+|null), metadata !(\d+).*$/);
 
47
    var form3ac = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:metadata !\d+|null), metadata !"[^"]*", metadata !(\d+)[^\[]*.*$/);
 
48
    var form3ad = new RegExp(/^!(\d+) = metadata !{i32 \d+, (?:i32 \d+|null), (?:i32 \d+|null), metadata !"[^"]*", metadata !"[^"]*", metadata !"[^"]*", metadata !(\d+),.*$/);
 
49
    var form3b = new RegExp(/^!(\d+) = metadata !{i32 \d+, metadata !"([^"]+)", metadata !"([^"]*)", (metadata !\d+|null)}.*$/);
 
50
    var form3c = new RegExp(/^!(\d+) = metadata !{\w+\d* !?(\d+)[^\d].*$/);
 
51
    var form4 = new RegExp(/^!llvm.dbg.[\w\.]+ = .*$/);
 
52
    var form5 = new RegExp(/^!(\d+) = metadata !{.*$/);
 
53
    var form6 = new RegExp(/^  (tail )?call void \@llvm.dbg.\w+\(metadata .*$/);
 
54
 
 
55
    for (var i = 0; i < lines.length; i++) {
 
56
      var line = lines[i];
 
57
      line = line.replace(/; +\[debug line = \d+:\d+\]/, '');
 
58
      var skipLine = false;
 
59
 
 
60
      if (form6.exec(line)) {
 
61
        lines[i] = ';';
 
62
        continue;
 
63
      }
 
64
 
 
65
      var calc = form1.exec(line) || form2.exec(line);
 
66
      if (calc) {
 
67
        llvmLineToMetadata[i+1] = calc[1];
 
68
        lines[i] = line.replace(/, !dbg !\d+/, '');
 
69
        continue;
 
70
      }
 
71
      calc = form3.exec(line);
 
72
      if (calc) {
 
73
        metadataToSourceLine[calc[1]] = calc[2];
 
74
        metadataToParentMetadata[calc[1]] = calc[3];
 
75
        lines[i] = ';'; // return an empty line, to keep line numbers of subsequent lines the same
 
76
        continue;
 
77
      }
 
78
      calc = form3a.exec(line) || form3ab.exec(line) || form3ac.exec(line) || form3ad.exec(line);
 
79
      if (calc) {
 
80
        metadataToParentMetadata[calc[1]] = calc[2];
 
81
        lines[i] = ';';
 
82
        continue;
 
83
      }
 
84
      calc = form3b.exec(line);
 
85
      if (calc) {
 
86
        metadataToFilename[calc[1]] = /* LLVM 2.8<= : calc[3] + '/' + */ calc[2];
 
87
        lines[i] = ';';
 
88
        continue;
 
89
      }
 
90
      calc = form3c.exec(line) || form4.exec(line) || form5.exec(line);
 
91
      if (calc) {
 
92
        lines[i] = ';';
 
93
        continue;
 
94
      }
 
95
      if (line[0] == '!') skipLine = true;
 
96
      lines[i] = skipLine ? ';' : line;
 
97
    }
 
98
 
 
99
    /*
 
100
    dprint("ll ==> meta: " + JSON.stringify(llvmLineToMetadata));
 
101
    dprint("meta ==> sline: " + JSON.stringify(metadataToSourceLine));
 
102
    dprint("meta ==> pmeta: " + JSON.stringify(metadataToParentMetadata));
 
103
    dprint("meta ==> fname: " + JSON.stringify(metadataToFilename));
 
104
    */
 
105
 
 
106
    this.llvmLineToSourceLine = {};
 
107
    this.llvmLineToSourceFile = {};
 
108
    for (var l in llvmLineToMetadata) {
 
109
      var m = llvmLineToMetadata[l];
 
110
      this.llvmLineToSourceLine[l] = metadataToSourceLine[m];
 
111
      dprint('metadata', 'starting to recurse metadata for: ' + m);
 
112
      while (!metadataToFilename[m]) {
 
113
        dprint('metadata', 'recursing metadata, at: ' + m);
 
114
        m = metadataToParentMetadata[m];
 
115
        assert(m, 'Confused as to parent metadata for llvm #' + l + ', metadata !' + m);
 
116
      }
 
117
      this.llvmLineToSourceFile[l] = metadataToFilename[m];
 
118
    }
 
119
 
 
120
    this.on = true;
 
121
  },
 
122
 
 
123
  clear: function() {
 
124
    this.llvmLineToSourceLine = {};
 
125
    this.llvmLineToSourceFile = {};
 
126
    this.on = false;
 
127
  },
 
128
 
 
129
  getComment: function(lineNum) {
 
130
    if (!this.on) return null;
 
131
    return lineNum in this.llvmLineToSourceLine ? ' //@line ' + this.llvmLineToSourceLine[lineNum] + ' "' +
 
132
                                                                this.llvmLineToSourceFile[lineNum] + '"' : '';
 
133
  },
 
134
 
 
135
  getAssociatedSourceFile: function(lineNum) {
 
136
    if (!this.on) return null;
 
137
    return lineNum in this.llvmLineToSourceLine ? this.llvmLineToSourceFile[lineNum] : null;
 
138
  },
 
139
  
 
140
  getIdentifier: function(lineNum) {
 
141
    if (!this.on) return null;
 
142
    if (lineNum === undefined) {
 
143
      lineNum = Framework.currItem.lineNum;
 
144
      assert(lineNum !== undefined);
 
145
    }
 
146
    var approx = false;
 
147
    var sourceFile;
 
148
    while (lineNum >= 0) {
 
149
      var sourceFile = this.llvmLineToSourceFile[lineNum];
 
150
      if (sourceFile) break;
 
151
      lineNum--;
 
152
      approx = true;
 
153
    }
 
154
    if (!sourceFile) return 'UNKNOWN';
 
155
    return sourceFile.split('/').slice(-1)[0] + ':' + (approx ? '~' : '') + this.llvmLineToSourceLine[lineNum];
 
156
  }
 
157
};
 
158
 
 
159
var PreProcessor = {
 
160
  eliminateUnneededIntrinsics: function(lines) {
 
161
    // LLVM sometimes aggresively adds lifetime annotations, for example
 
162
    //
 
163
    //    %0 = bitcast %"class.std::__1::__tree"** %this.addr.i to i8* ; [#uses=1 type=i8*]
 
164
    //    call void @llvm.lifetime.start(i64 -1, i8* %0) nounwind
 
165
    //    [..]
 
166
    //    %6 = bitcast float* %__x.addr.i to i8*          ; [#uses=1 type=i8*]
 
167
    //    call void @llvm.lifetime.end(i64 -1, i8* %6) nounwind
 
168
    //
 
169
    // This greatly hurts us if we do not eliminate it ahead of time, because while we
 
170
    // will correctly do nothing for the lifetime intrinsic itself, the bitcast of the
 
171
    // parameter to it will prevent nativization of the variable being cast (!)
 
172
    for (var i = 0; i < lines.length; i++) {
 
173
      var line = lines[i];
 
174
      if (/call void @llvm.lifetime.(start|end)\(i\d+ -1,.*/.exec(line)) {
 
175
        lines[i] = ';';
 
176
      }
 
177
    }
 
178
  }
 
179
};
 
180
 
 
181
var Variables = {
 
182
  globals: {},
 
183
  indexedGlobals: {}, // for indexed globals, ident ==> index
 
184
  // Used in calculation of indexed globals
 
185
  nextIndexedOffset: 0,
 
186
 
 
187
  resolveAliasToIdent: function(ident) {
 
188
    while (1) {
 
189
      var varData = Variables.globals[ident];
 
190
      if (!(varData && varData.targetIdent)) break;
 
191
      ident = varData.targetIdent; // might need to eval to turn (6) into 6
 
192
    }
 
193
    return ident;
 
194
  },
 
195
};
 
196
 
 
197
var Types = {
 
198
  types: {},
 
199
  fatTypes: {}, // With QUANTUM_SIZE=1, we store the full-size type data here
 
200
  flipTypes: function() {
 
201
    var temp = this.fatTypes;
 
202
    this.fatTypes = this.types;
 
203
    this.types = temp;
 
204
  },
 
205
  structMetadata: {},
 
206
 
 
207
  // Remove all data not needed during runtime (like line numbers, JS, etc.)
 
208
  cleanForRuntime: function() {
 
209
    values(this.types).forEach(function(type) {
 
210
      delete type.intertype;
 
211
      delete type.name_;
 
212
      delete type.lineNum;
 
213
      delete type.lines;
 
214
      delete type.needsFlattening;
 
215
      delete type.JS;
 
216
    });
 
217
    keys(this.types).forEach(function(longer) {
 
218
      var shorter = longer.replace('%struct.', '').replace('%class.');
 
219
      if (shorter === longer) return;
 
220
      if (shorter in this.types) return;
 
221
      this.types[shorter] = this.types[longer];
 
222
    }, this);
 
223
  },
 
224
 
 
225
  needAnalysis: {}, // Types noticed during parsing, that need analysis
 
226
 
 
227
  // Set to true if we actually use precise i64 math: If PRECISE_I64_MATH is set, and also such math is actually
 
228
  // needed (+,-,*,/,% - we do not need it for bitops), or PRECISE_I64_MATH is 2 (forced)
 
229
  preciseI64MathUsed: (PRECISE_I64_MATH == 2)
 
230
};
 
231
 
 
232
var Functions = {
 
233
  // All functions that will be implemented in this file. Maps id to signature
 
234
  implementedFunctions: {},
 
235
  libraryFunctions: {}, // functions added from the library. value 2 means asmLibraryFunction
 
236
  unimplementedFunctions: {}, // library etc. functions that we need to index, maps id to signature
 
237
 
 
238
  indexedFunctions: {},
 
239
  nextIndex: (ASM_JS ? 2*RESERVED_FUNCTION_POINTERS : 0) + 2, // Start at a non-0 (even, see below) value
 
240
 
 
241
  blockAddresses: {}, // maps functions to a map of block labels to label ids
 
242
 
 
243
  getSignature: function(returnType, argTypes, hasVarArgs) {
 
244
    var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f');
 
245
    for (var i = 0; i < argTypes.length; i++) {
 
246
      var type = argTypes[i];
 
247
      if (!type) break; // varargs
 
248
      sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s
 
249
    }
 
250
    if (hasVarArgs) sig += 'i';
 
251
    return sig;
 
252
  },
 
253
 
 
254
  // Mark a function as needing indexing. Python will coordinate them all
 
255
  getIndex: function(ident, doNotCreate) {
 
256
    if (doNotCreate && !(ident in this.indexedFunctions)) {
 
257
      if (!Functions.getIndex.tentative) Functions.getIndex.tentative = {}; // only used by GL emulation; TODO: generalize when needed
 
258
      Functions.getIndex.tentative[ident] = 0;
 
259
    }
 
260
    if (phase != 'post' && singlePhase) {
 
261
      if (!doNotCreate) this.indexedFunctions[ident] = 0; // tell python we need this indexized
 
262
      return "'{{ FI_" + ident + " }}'"; // something python will replace later
 
263
    } else {
 
264
      var ret = this.indexedFunctions[ident];
 
265
      if (!ret) {
 
266
        if (doNotCreate) return '0';
 
267
        ret = this.nextIndex;
 
268
        this.nextIndex += 2; // Need to have indexes be even numbers, see |polymorph| test
 
269
        this.indexedFunctions[ident] = ret;
 
270
      }
 
271
      return ret.toString();
 
272
    }
 
273
  },
 
274
 
 
275
  getTable: function(sig) {
 
276
    return ASM_JS ? 'FUNCTION_TABLE_' + sig : 'FUNCTION_TABLE';
 
277
  },
 
278
 
 
279
  // Generate code for function indexing
 
280
  generateIndexing: function() {
 
281
    var total = this.nextIndex;
 
282
    if (ASM_JS) total = ceilPowerOfTwo(total); // must be power of 2 for mask
 
283
    function emptyTable(sig) {
 
284
      return zeros(total);
 
285
    }
 
286
    var tables = { pre: '' };
 
287
    if (ASM_JS) {
 
288
      ['v', 'vi', 'ii', 'iii'].forEach(function(sig) { // add some default signatures that are used in the library
 
289
        tables[sig] = emptyTable(sig); // TODO: make them compact
 
290
      });
 
291
    }
 
292
    for (var ident in this.indexedFunctions) {
 
293
      var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] || LibraryManager.library[ident.substr(1) + '__sig'] : 'x';
 
294
      assert(sig, ident);
 
295
      if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact
 
296
      tables[sig][this.indexedFunctions[ident]] = ident;
 
297
    }
 
298
    var generated = false;
 
299
    var wrapped = {};
 
300
    for (var t in tables) {
 
301
      if (t == 'pre') continue;
 
302
      generated = true;
 
303
      var table = tables[t];
 
304
      for (var i = 0; i < table.length; i++) {
 
305
        // Resolve multi-level aliases all the way down
 
306
        while (1) {
 
307
          var varData = Variables.globals[table[i]];
 
308
          if (!(varData && varData.resolvedAlias)) break;
 
309
          table[i] = table[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6
 
310
        }
 
311
        // Resolve library aliases
 
312
        if (table[i]) {
 
313
          var libName = LibraryManager.getRootIdent(table[i].substr(1));
 
314
          if (libName && typeof libName == 'string') {
 
315
            table[i] = (libName.indexOf('.') < 0 ? '_' : '') + libName;
 
316
          }
 
317
        }
 
318
        if (ASM_JS) {
 
319
          var curr = table[i];
 
320
          if (curr && curr != '0' && !Functions.implementedFunctions[curr]) {
 
321
            // This is a library function, we can't just put it in the function table, need a wrapper
 
322
            if (!wrapped[curr]) {
 
323
              var args = '', arg_coercions = '', call = curr + '(', retPre = '', retPost = '';
 
324
              if (t[0] != 'v') {
 
325
                if (t[0] == 'i') {
 
326
                  retPre = 'return ';
 
327
                  retPost = '|0';
 
328
                } else {
 
329
                  retPre = 'return +';
 
330
                }
 
331
              }
 
332
              for (var j = 1; j < t.length; j++) {
 
333
                args += (j > 1 ? ',' : '') + 'a' + j;
 
334
                arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32') + ';';
 
335
                call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32');
 
336
              }
 
337
              call += ')';
 
338
              tables.pre += 'function ' + curr + '__wrapper(' + args + ') { ' + arg_coercions + ' ; ' + retPre + call + retPost + ' }\n';
 
339
              wrapped[curr] = 1;
 
340
            }
 
341
            table[i] = curr + '__wrapper';
 
342
          }
 
343
        }
 
344
      }
 
345
      if (table.length > 20) {
 
346
        // add some newlines in the table, for readability
 
347
        var j = 10;
 
348
        while (j+10 < table.length) {
 
349
          table[j] += '\n';
 
350
          j += 10;
 
351
        }
 
352
      }
 
353
      var indices = table.toString().replace('"', '');
 
354
      if (BUILD_AS_SHARED_LIB) {
 
355
        // Shared libraries reuse the parent's function table.
 
356
        tables[t] = Functions.getTable(t) + '.push.apply(' + Functions.getTable(t) + ', [' + indices + ']);\n';
 
357
      } else {
 
358
        tables[t] = 'var ' + Functions.getTable(t) + ' = [' + indices + '];\n';
 
359
        if (SAFE_DYNCALLS) {
 
360
          tables[t] += 'var FUNCTION_TABLE_NAMES = ' + JSON.stringify(table).replace(/\n/g, '').replace(/,0/g, ',0\n') + ';\n';
 
361
        }
 
362
      }
 
363
    }
 
364
    if (!generated && !ASM_JS) {
 
365
      tables['x'] = 'var FUNCTION_TABLE = [0, 0];\n'; // default empty table
 
366
    }
 
367
    Functions.tables = tables;
 
368
  }
 
369
};
 
370
 
 
371
var LibraryManager = {
 
372
  library: null,
 
373
  structs: {},
 
374
  loaded: false,
 
375
 
 
376
  load: function() {
 
377
    if (this.library) return;
 
378
 
 
379
    var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries);
 
380
    for (var i = 0; i < libraries.length; i++) {
 
381
      eval(processMacros(preprocess(read(libraries[i]))));
 
382
    }
 
383
 
 
384
    this.loaded = true;
 
385
  },
 
386
 
 
387
  // Given an ident, see if it is an alias for something, and so forth, returning
 
388
  // the earliest ancestor (the root)
 
389
  getRootIdent: function(ident) {
 
390
    if (!this.library) return null;
 
391
    var ret = LibraryManager.library[ident];
 
392
    if (!ret) return null;
 
393
    var last = ident;
 
394
    while (typeof ret === 'string') {
 
395
      last = ret;
 
396
      ret = LibraryManager.library[ret];
 
397
    }
 
398
    return last;
 
399
  },
 
400
 
 
401
  isStubFunction: function(ident) {
 
402
    var libCall = LibraryManager.library[ident.substr(1)];
 
403
    return typeof libCall === 'function' && libCall.toString().replace(/\s/g, '') === 'function(){}'
 
404
                                         && !(ident in Functions.implementedFunctions);
 
405
  }
 
406
};
 
407
 
 
408
// Safe way to access a C define. We check that we don't add library functions with missing defines.
 
409
function cDefine(key) {
 
410
  return key in C_DEFINES ? C_DEFINES[key] : ('0 /* XXX missing C define ' + key + ' */');
 
411
}
 
412
 
 
413
var PassManager = {
 
414
  serialize: function() {
 
415
    if (phase == 'pre') {
 
416
      print('\n//FORWARDED_DATA:' + JSON.stringify({
 
417
        Types: Types,
 
418
        Variables: Variables,
 
419
        Functions: Functions,
 
420
        EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors)
 
421
      }));
 
422
    } else if (phase == 'funcs') {
 
423
      print('\n//FORWARDED_DATA:' + JSON.stringify({
 
424
        Types: { preciseI64MathUsed: Types.preciseI64MathUsed },
 
425
        Functions: {
 
426
          blockAddresses: Functions.blockAddresses,
 
427
          indexedFunctions: Functions.indexedFunctions,
 
428
          implementedFunctions: ASM_JS ? Functions.implementedFunctions : [],
 
429
          unimplementedFunctions: Functions.unimplementedFunctions,
 
430
        }
 
431
      }));
 
432
    } else if (phase == 'post') {
 
433
      print('\n//FORWARDED_DATA:' + JSON.stringify({
 
434
        Functions: { tables: Functions.tables }
 
435
      }));
 
436
    }
 
437
  },
 
438
  load: function(json) {
 
439
    var data = JSON.parse(json);
 
440
    for (var i in data.Types) {
 
441
      Types[i] = data.Types[i];
 
442
    }
 
443
    for (var i in data.Variables) {
 
444
      Variables[i] = data.Variables[i];
 
445
    }
 
446
    for (var i in data.Functions) {
 
447
      Functions[i] = data.Functions[i];
 
448
    }
 
449
    /*
 
450
    print('\n//LOADED_DATA:' + phase + ':' + JSON.stringify({
 
451
      Types: Types,
 
452
      Variables: Variables,
 
453
      Functions: Functions
 
454
    }));
 
455
    */
 
456
  }
 
457
};
 
458