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

« back to all changes in this revision

Viewing changes to src/intertyper.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
// LLVM assembly => internal intermediate representation, which is ready
 
4
// to be processed by the later stages.
 
5
 
 
6
var tokenizer; // TODO: Clean this up/out
 
7
               //       XXX In particular, this closes over the substrate, which can keep stuff in memory, which is bad
 
8
function tokenize(text) {
 
9
  return tokenizer.processItem({ lineText: text }, true);
 
10
}
 
11
 
 
12
// Handy sets
 
13
 
 
14
var ENCLOSER_STARTERS = set('[', '(', '<');
 
15
var ENCLOSER_ENDERS = {
 
16
  '[': ']',
 
17
  '(': ')',
 
18
  '<': '>'
 
19
};
 
20
var ZEROINIT_UNDEF = set('zeroinitializer', 'undef');
 
21
var NSW_NUW = set('nsw', 'nuw');
 
22
 
 
23
// Intertyper
 
24
 
 
25
function intertyper(data, sidePass, baseLineNums) {
 
26
  var mainPass = !sidePass;
 
27
  baseLineNums = baseLineNums || [[0,0]]; // each pair [#0,#1] means "starting from line #0, the base line num is #1"
 
28
 
 
29
  dprint('framework', 'Big picture: Starting intertyper, main pass=' + mainPass);
 
30
 
 
31
  // Substrate
 
32
 
 
33
  var substrate = new Substrate('Intertyper');
 
34
 
 
35
  // Line splitter. We break off some bunches of lines into unparsedBundles, which are
 
36
  // parsed in separate passes later. This helps to keep memory usage low - we can start
 
37
  // from raw lines and end up with final JS for each function individually that way, instead
 
38
  // of intertyping them all, then analyzing them all, etc.
 
39
  substrate.addActor('LineSplitter', {
 
40
    processItem: function _lineSplitter(item) {
 
41
      var lines = item.llvmLines;
 
42
      var ret = [];
 
43
      var inContinual = false;
 
44
      var inFunction = false;
 
45
      var currFunctionLines;
 
46
      var currFunctionLineNum;
 
47
      var unparsedBundles = [];
 
48
      var unparsedTypes, unparsedGlobals;
 
49
      if (mainPass) {
 
50
        unparsedTypes = {
 
51
          intertype: 'unparsedTypes',
 
52
          lines: []
 
53
        };
 
54
        unparsedBundles.push(unparsedTypes);
 
55
        unparsedGlobals = {
 
56
          intertype: 'unparsedGlobals',
 
57
          lines: []
 
58
        };
 
59
        unparsedBundles.push(unparsedGlobals);
 
60
      }
 
61
      var baseLineNumPosition = 0;
 
62
      for (var i = 0; i < lines.length; i++) {
 
63
        var line = lines[i];
 
64
        if (singlePhase) lines[i] = null; // lines may be very very large. Allow GCing to occur in the loop by releasing refs here
 
65
 
 
66
        while (baseLineNumPosition < baseLineNums.length-1 && i >= baseLineNums[baseLineNumPosition+1][0]) {
 
67
          baseLineNumPosition++;
 
68
        }
 
69
 
 
70
        if (mainPass && (line[0] == '%' || line[0] == '@')) {
 
71
          // If this isn't a type, it's a global variable, make a note of the information now, we will need it later
 
72
          var parts = line.split(' = ');
 
73
          assert(parts.length >= 2);
 
74
          var left = parts[0], right = parts.slice(1).join(' = ');
 
75
          var testType = /^type .*/.exec(right);
 
76
          if (!testType) {
 
77
            var globalIdent = toNiceIdent(left);
 
78
            var testAlias = /^(hidden )?alias .*/.exec(right);
 
79
            Variables.globals[globalIdent] = {
 
80
              name: globalIdent,
 
81
              alias: !!testAlias,
 
82
              impl: VAR_EMULATED
 
83
            };
 
84
            unparsedGlobals.lines.push(line);
 
85
          } else {
 
86
            unparsedTypes.lines.push(line);
 
87
          }
 
88
          continue;
 
89
        }
 
90
        if (mainPass && /^define .*/.test(line)) {
 
91
          inFunction = true;
 
92
          currFunctionLines = [];
 
93
          currFunctionLineNum = i + 1;
 
94
        }
 
95
        if (!inFunction || !mainPass) {
 
96
          if (inContinual || /^\ +(to|catch |filter |cleanup).*/.test(line)) {
 
97
            // to after invoke or landingpad second line
 
98
            ret.slice(-1)[0].lineText += line;
 
99
            if (/^\ +\]/.test(line)) { // end of llvm switch
 
100
              inContinual = false;
 
101
            }
 
102
          } else {
 
103
            ret.push({
 
104
              lineText: line,
 
105
              lineNum: i + 1 + baseLineNums[baseLineNumPosition][1] - baseLineNums[baseLineNumPosition][0]
 
106
            });
 
107
            if (/^\ +switch\ .*/.test(line)) {
 
108
              // beginning of llvm switch
 
109
              inContinual = true;
 
110
            }
 
111
          }
 
112
        } else {
 
113
          currFunctionLines.push(line);
 
114
        }
 
115
        if (mainPass && /^}.*/.test(line)) {
 
116
          inFunction = false;
 
117
          if (mainPass) {
 
118
            var func = funcHeader.processItem(tokenizer.processItem({ lineText: currFunctionLines[0], lineNum: currFunctionLineNum }, true))[0];
 
119
 
 
120
            if (SKIP_STACK_IN_SMALL && /emscripten_autodebug/.exec(func.ident)) {
 
121
              warnOnce('Disabling SKIP_STACK_IN_SMALL because we are apparently processing autodebugger data');
 
122
              SKIP_STACK_IN_SMALL = 0;
 
123
            }
 
124
 
 
125
            var ident = toNiceIdent(func.ident);
 
126
            if (!(ident in DEAD_FUNCTIONS)) {
 
127
              unparsedBundles.push({
 
128
                intertype: 'unparsedFunction',
 
129
                // We need this early, to know basic function info - ident, params, varargs
 
130
                ident: ident,
 
131
                params: func.params,
 
132
                returnType: func.returnType,
 
133
                hasVarArgs: func.hasVarArgs,
 
134
                lineNum: currFunctionLineNum,
 
135
                lines: currFunctionLines
 
136
              });
 
137
            }
 
138
            currFunctionLines = [];
 
139
          }
 
140
        }
 
141
      }
 
142
      // We need lines beginning with ';' inside functions, because older LLVM versions generated labels that way. But when not
 
143
      // parsing functions, we can ignore all such lines and save some time that way.
 
144
      this.forwardItems(ret.filter(function(item) { return item.lineText && (item.lineText[0] != ';' || !mainPass); }), 'Tokenizer');
 
145
      return unparsedBundles;
 
146
    }
 
147
  });
 
148
 
 
149
  // Line tokenizer
 
150
  tokenizer = substrate.addActor('Tokenizer', {
 
151
    processItem: function _tokenizer(item, inner) {
 
152
      //assert(item.lineNum != 40000);
 
153
      //if (item.lineNum) print(item.lineNum);
 
154
      var tokens = [];
 
155
      var quotes = 0;
 
156
      var lastToken = null;
 
157
      var CHUNKSIZE = 64; // How much forward to peek forward. Too much means too many string segments copied
 
158
      // Note: '{' is not an encloser, as its use in functions is split over many lines
 
159
      var enclosers = {
 
160
        '[': 0,
 
161
        ']': '[',
 
162
        '(': 0,
 
163
        ')': '(',
 
164
        '<': 0,
 
165
        '>': '<'
 
166
      };
 
167
      var totalEnclosing = 0;
 
168
      var that = this;
 
169
      function makeToken(text) {
 
170
        if (text.length == 0) return;
 
171
        // merge certain tokens
 
172
        if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.test(text) ) ) {
 
173
          lastToken.text += text;
 
174
          return;
 
175
        }
 
176
 
 
177
        var token = {
 
178
          text: text
 
179
        };
 
180
        if (text[0] in enclosers) {
 
181
          token.item = that.processItem({
 
182
            lineText: text.substr(1, text.length-2)
 
183
          }, true);
 
184
          token.type = text[0];
 
185
        }
 
186
        // merge certain tokens
 
187
        if (lastToken && isType(lastToken.text) && isFunctionDef(token)) {
 
188
          lastToken.text += ' ' + text;
 
189
        } else if (lastToken && text[0] == '}') { // }, }*, etc.
 
190
          var openBrace = tokens.length-1;
 
191
          while (tokens[openBrace].text.substr(-1) != '{') openBrace --;
 
192
          token = combineTokens(tokens.slice(openBrace+1));
 
193
          tokens.splice(openBrace, tokens.length-openBrace+1);
 
194
          tokens.push(token);
 
195
          token.type = '{';
 
196
          token.text = '{ ' + token.text + ' }';
 
197
          var pointingLevelsToAdd = pointingLevels(text) - pointingLevels(token.text);
 
198
          while (pointingLevelsToAdd > 0) {
 
199
            token.text += '*';
 
200
            pointingLevelsToAdd--;
 
201
          }
 
202
          lastToken = token;
 
203
        } else {
 
204
          tokens.push(token);
 
205
          lastToken = token;
 
206
        }
 
207
      }
 
208
      // Split using meaningful characters
 
209
      var lineText = item.lineText + ' ';
 
210
      var re = /[\[\]\(\)<>, "]/g;
 
211
      var segments = lineText.split(re);
 
212
      segments.pop();
 
213
      var len = segments.length;
 
214
      var i = -1;
 
215
      var curr = '';
 
216
      var segment, letter;
 
217
      for (var s = 0; s < len; s++) {
 
218
        segment = segments[s];
 
219
        i += segment.length + 1;
 
220
        letter = lineText[i];
 
221
        curr += segment;
 
222
        switch (letter) {
 
223
          case ' ':
 
224
            if (totalEnclosing == 0 && quotes == 0) {
 
225
              makeToken(curr);
 
226
              curr = '';
 
227
            } else {
 
228
              curr += ' ';
 
229
            }
 
230
            break;
 
231
          case '"':
 
232
            if (totalEnclosing == 0) {
 
233
              if (quotes == 0) {
 
234
                if (curr == '@' || curr == '%') {
 
235
                  curr += '"';
 
236
                } else {
 
237
                  makeToken(curr);
 
238
                  curr = '"';
 
239
                }
 
240
              } else {
 
241
                makeToken(curr + '"');
 
242
                curr = '';
 
243
              }
 
244
            } else {
 
245
              curr += '"';
 
246
            }
 
247
            quotes = 1-quotes;
 
248
            break;
 
249
          case ',':
 
250
            if (totalEnclosing == 0 && quotes == 0) {
 
251
              makeToken(curr);
 
252
              curr = '';
 
253
              tokens.push({ text: ',' });
 
254
            } else {
 
255
              curr += ',';
 
256
            }
 
257
            break;
 
258
          default:
 
259
            assert(letter in enclosers);
 
260
            if (quotes) {
 
261
              curr += letter;
 
262
              break;
 
263
            }
 
264
            if (letter in ENCLOSER_STARTERS) {
 
265
              if (totalEnclosing == 0) {
 
266
                makeToken(curr);
 
267
                curr = '';
 
268
              }
 
269
              curr += letter;
 
270
              enclosers[letter]++;
 
271
              totalEnclosing++;
 
272
            } else {
 
273
              enclosers[enclosers[letter]]--;
 
274
              totalEnclosing--;
 
275
              if (totalEnclosing == 0) {
 
276
                makeToken(curr + letter);
 
277
                curr = '';
 
278
              } else {
 
279
                curr += letter;
 
280
              }
 
281
            }
 
282
        }
 
283
      }
 
284
      var newItem = {
 
285
        tokens: tokens,
 
286
        indent: lineText.search(/[^ ]/),
 
287
        lineNum: item.lineNum
 
288
      };
 
289
      if (inner) {
 
290
        return newItem;
 
291
      } else {
 
292
        this.forwardItem(newItem, 'Triager');
 
293
      }
 
294
      return null;
 
295
    }
 
296
  });
 
297
 
 
298
  substrate.addActor('Triager', {
 
299
    processItem: function _triager(item) {
 
300
      function triage() {
 
301
        assert(!item.intertype);
 
302
        var token0Text = item.tokens[0].text;
 
303
        var token1Text = item.tokens[1] ? item.tokens[1].text : null;
 
304
        var tokensLength = item.tokens.length;
 
305
        if (item.indent === 2) {
 
306
          if (tokensLength >= 5 &&
 
307
              (token0Text == 'store' || token1Text == 'store'))
 
308
            return 'Store';
 
309
          if (tokensLength >= 3 && token0Text == 'br')
 
310
            return 'Branch';
 
311
          if (tokensLength >= 2 && token0Text == 'ret')
 
312
            return 'Return';
 
313
          if (tokensLength >= 2 && token0Text == 'switch')
 
314
            return 'Switch';
 
315
          if (token0Text == 'unreachable')
 
316
            return 'Unreachable';
 
317
          if (tokensLength >= 3 && token0Text == 'indirectbr')
 
318
            return 'IndirectBr';
 
319
          if (tokensLength >= 2 && token0Text == 'resume')
 
320
            return 'Resume';
 
321
          if (tokensLength >= 3 &&
 
322
              (token0Text == 'load' || token1Text == 'load'))
 
323
            return 'Load';
 
324
          if (tokensLength >= 3 &&
 
325
              token0Text in MATHOPS)
 
326
            return 'Mathops';
 
327
          if (tokensLength >= 3 && token0Text == 'bitcast')
 
328
            return 'Bitcast';
 
329
          if (tokensLength >= 3 && token0Text == 'getelementptr')
 
330
            return 'GEP';
 
331
          if (tokensLength >= 2 && token0Text == 'alloca')
 
332
            return 'Alloca';
 
333
          if (tokensLength >= 3 && token0Text == 'extractvalue')
 
334
            return 'ExtractValue';
 
335
          if (tokensLength >= 3 && token0Text == 'insertvalue')
 
336
            return 'InsertValue';
 
337
          if (tokensLength >= 3 && token0Text == 'phi')
 
338
            return 'Phi';
 
339
          if (tokensLength >= 3 && token0Text == 'va_arg')
 
340
            return 'va_arg';
 
341
          if (tokensLength >= 3 && token0Text == 'landingpad')
 
342
            return 'Landingpad';
 
343
          if (token0Text == 'fence')
 
344
            return '/dev/null';
 
345
        } else if (item.indent === 0) {
 
346
          if ((tokensLength >= 1 && token0Text.substr(-1) == ':') ||
 
347
              (tokensLength >= 3 && token1Text == '<label>') ||
 
348
              (tokensLength >= 2 && token1Text == ':'))
 
349
            return 'Label';
 
350
          if (tokensLength >= 4 && token0Text == 'declare')
 
351
            return 'External';
 
352
          if (tokensLength >= 3 && token1Text == '=')
 
353
            return 'Global';
 
354
          if (tokensLength >= 4 && token0Text == 'define' &&
 
355
             item.tokens.slice(-1)[0].text == '{')
 
356
            return 'FuncHeader';
 
357
          if (tokensLength >= 1 && token0Text == '}')
 
358
            return 'FuncEnd';
 
359
          if (token0Text == 'module' && token1Text == 'asm') {
 
360
            warn('Ignoring module asm: ' + item.tokens[2].text);
 
361
            return '/dev/null';
 
362
          }
 
363
        }
 
364
        if (tokensLength >= 3 && (token0Text == 'call' || token1Text == 'call'))
 
365
          return 'Call';
 
366
        if (token0Text == 'target')
 
367
          return '/dev/null';
 
368
        if (token0Text == ';')
 
369
          return '/dev/null';
 
370
        if (tokensLength >= 3 && token0Text == 'invoke')
 
371
          return 'Invoke';
 
372
        if (tokensLength >= 3 && token0Text == 'atomicrmw' || token0Text == 'cmpxchg')
 
373
          return 'Atomic';
 
374
        throw 'Invalid token, cannot triage: ' + dump(item);
 
375
      }
 
376
      var eq;
 
377
      if (item.indent == 2 && (eq = findTokenText(item, '=')) >= 0) {
 
378
        item.assignTo = toNiceIdent(combineTokens(item.tokens.slice(0, eq)).text);
 
379
        item.tokens = item.tokens.slice(eq+1);
 
380
      }
 
381
      this.forwardItem(item, triage());
 
382
    }
 
383
  });
 
384
 
 
385
  // Line parsers to intermediate form
 
386
 
 
387
  // globals: type or variable
 
388
  substrate.addActor('Global', {
 
389
    processItem: function _global(item) {
 
390
      function scanConst(value, type) {
 
391
        // Gets an array of constant items, separated by ',' tokens
 
392
        function handleSegments(tokens) {
 
393
          // Handle a single segment (after comma separation)
 
394
          function handleSegment(segment) {
 
395
            if (segment[1].text == 'null') {
 
396
              return { intertype: 'value', ident: '0', type: 'i32' };
 
397
            } else if (segment[1].text == 'zeroinitializer') {
 
398
              Types.needAnalysis[segment[0].text] = 0;
 
399
              return { intertype: 'emptystruct', type: segment[0].text };
 
400
            } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) {
 
401
              return parseLLVMFunctionCall(segment);
 
402
            } else if (segment[1].type && segment[1].type == '{') {
 
403
              Types.needAnalysis[segment[0].text] = 0;
 
404
              return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].tokens) };
 
405
            } else if (segment[1].type && segment[1].type == '<') {
 
406
              Types.needAnalysis[segment[0].text] = 0;
 
407
              return { intertype: 'struct', type: segment[0].text, contents: handleSegments(segment[1].item.tokens[0].tokens) };
 
408
            } else if (segment[1].type && segment[1].type == '[') {
 
409
              Types.needAnalysis[segment[0].text] = 0;
 
410
              return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].item.tokens) };
 
411
            } else if (segment.length == 2) {
 
412
              Types.needAnalysis[segment[0].text] = 0;
 
413
              return { intertype: 'value', type: segment[0].text, ident: toNiceIdent(segment[1].text) };
 
414
            } else if (segment[1].text === 'c') {
 
415
              // string
 
416
              var text = segment[2].text;
 
417
              text = text.substr(1, text.length-2);
 
418
              return { intertype: 'string', text: text, type: 'i8*' };
 
419
            } else if (segment[1].text === 'blockaddress') {
 
420
              return parseBlockAddress(segment);
 
421
            } else {
 
422
              throw 'Invalid segment: ' + dump(segment);
 
423
            }
 
424
          };
 
425
          return splitTokenList(tokens).map(handleSegment);
 
426
        }
 
427
 
 
428
        Types.needAnalysis[type] = 0;
 
429
        if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) {
 
430
          return { value: toNiceIdent(value.text), type: type };
 
431
        } else if (value.text in ZEROINIT_UNDEF) { // undef doesn't really need initting, but why not
 
432
          return { intertype: 'emptystruct', type: type };
 
433
        } else if (value.text && value.text[0] == '"') {
 
434
          return { intertype: 'string', text: value.text.substr(1, value.text.length-2) };
 
435
        } else {
 
436
          if (value.type == '<') { // <{ i8 }> etc.
 
437
            value = value.item.tokens;
 
438
          }
 
439
          var contents;
 
440
          if (value.item) {
 
441
            // list of items
 
442
            contents = value.item.tokens;
 
443
          } else if (value.type == '{') {
 
444
            // struct
 
445
            contents = value.tokens;
 
446
          } else if (value[0]) {
 
447
            contents = value[0];
 
448
          } else {
 
449
            throw '// interfailzzzzzzzzzzzzzz ' + dump(value.item) + ' ::: ' + dump(value);
 
450
          }
 
451
          return { intertype: 'segments', contents: handleSegments(contents) };
 
452
        }
 
453
      }
 
454
 
 
455
      cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 2);
 
456
      if (item.tokens[2].text == 'alias') {
 
457
        cleanOutTokens(LLVM.LINKAGES, item.tokens, 3);
 
458
        cleanOutTokens(LLVM.VISIBILITIES, item.tokens, 3);
 
459
        var last = getTokenIndexByText(item.tokens, ';');
 
460
        var ret = {
 
461
          intertype: 'alias',
 
462
          ident: toNiceIdent(item.tokens[0].text),
 
463
          value: parseLLVMSegment(item.tokens.slice(3, last)),
 
464
          lineNum: item.lineNum
 
465
        };
 
466
        ret.type = ret.value.type;
 
467
        Types.needAnalysis[ret.type] = 0;
 
468
        if (!NAMED_GLOBALS) {
 
469
          Variables.globals[ret.ident].type = ret.type;
 
470
        }
 
471
        return [ret];
 
472
      }
 
473
      if (item.tokens[2].text == 'type') {
 
474
        var fields = [];
 
475
        var packed = false;
 
476
        if (Runtime.isNumberType(item.tokens[3].text)) {
 
477
          // Clang sometimes has |= i32| instead of |= { i32 }|
 
478
          fields = [item.tokens[3].text];
 
479
        } else if (item.tokens[3].text != 'opaque') {
 
480
          if (item.tokens[3].type == '<') {
 
481
            packed = true;
 
482
            item.tokens[3] = item.tokens[3].item.tokens[0];
 
483
          }
 
484
          var subTokens = item.tokens[3].tokens;
 
485
          if (subTokens) {
 
486
            subTokens.push({text:','});
 
487
            while (subTokens[0]) {
 
488
              var stop = 1;
 
489
              while ([','].indexOf(subTokens[stop].text) == -1) stop ++;
 
490
              fields.push(combineTokens(subTokens.slice(0, stop)).text);
 
491
              subTokens.splice(0, stop+1);
 
492
            }
 
493
          }
 
494
        }
 
495
        return [{
 
496
          intertype: 'type',
 
497
          name_: item.tokens[0].text,
 
498
          fields: fields,
 
499
          packed: packed,
 
500
          lineNum: item.lineNum
 
501
        }];
 
502
      } else {
 
503
        // variable
 
504
        var ident = item.tokens[0].text;
 
505
        var private_ = findTokenText(item, 'private') >= 0;
 
506
        cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]);
 
507
        var external = false;
 
508
        if (item.tokens[2].text === 'external') {
 
509
          external = true;
 
510
          item.tokens.splice(2, 1);
 
511
        }
 
512
        Types.needAnalysis[item.tokens[2].text] = 0;
 
513
        var ret = {
 
514
          intertype: 'globalVariable',
 
515
          ident: toNiceIdent(ident),
 
516
          type: item.tokens[2].text,
 
517
          external: external,
 
518
          private_: private_,
 
519
          lineNum: item.lineNum
 
520
        };
 
521
        if (!NAMED_GLOBALS) {
 
522
          Variables.globals[ret.ident].type = ret.type;
 
523
          Variables.globals[ret.ident].external = external;
 
524
        }
 
525
        Types.needAnalysis[ret.type] = 0;
 
526
        if (ident == '@llvm.global_ctors') {
 
527
          ret.ctors = [];
 
528
          if (item.tokens[3].item) {
 
529
            var subTokens = item.tokens[3].item.tokens;
 
530
            splitTokenList(subTokens).forEach(function(segment) {
 
531
              var ctor = toNiceIdent(segment[1].tokens.slice(-1)[0].text);
 
532
              ret.ctors.push(ctor);
 
533
              if (ASM_JS) { // must export the global constructors from asm.js module, so mark as implemented and exported
 
534
                Functions.implementedFunctions[ctor] = 'v';
 
535
                EXPORTED_FUNCTIONS[ctor] = 1;
 
536
              }
 
537
            });
 
538
          }
 
539
        } else if (!external) {
 
540
          if (item.tokens[3].text == 'c')
 
541
            item.tokens.splice(3, 1);
 
542
          if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) {
 
543
            ret.value = parseLLVMFunctionCall(item.tokens.slice(2));
 
544
          } else {
 
545
            ret.value = scanConst(item.tokens[3], ret.type);
 
546
          }
 
547
        }
 
548
        return [ret];
 
549
      }
 
550
    }
 
551
  });
 
552
  // function header
 
553
  var funcHeader = substrate.addActor('FuncHeader', {
 
554
    processItem: function(item) {
 
555
      item.tokens = item.tokens.filter(function(token) {
 
556
        return !(token.text in LLVM.LINKAGES || token.text in LLVM.PARAM_ATTR || token.text in LLVM.FUNC_ATTR || token.text in LLVM.CALLING_CONVENTIONS);
 
557
      });
 
558
      var params = parseParamTokens(item.tokens[2].item.tokens);
 
559
      if (sidePass) dprint('unparsedFunctions', 'Processing function: ' + item.tokens[1].text);
 
560
      return [{
 
561
        intertype: 'function',
 
562
        ident: toNiceIdent(item.tokens[1].text),
 
563
        returnType: item.tokens[0].text,
 
564
        params: params,
 
565
        hasVarArgs: hasVarArgs(params),
 
566
        lineNum: item.lineNum,
 
567
      }];
 
568
    }
 
569
  });
 
570
  // label
 
571
  substrate.addActor('Label', {
 
572
    processItem: function(item) {
 
573
      var rawLabel = item.tokens[0].text.substr(-1) == ':' ?
 
574
            '%' + item.tokens[0].text.substr(0, item.tokens[0].text.length-1) :
 
575
            (item.tokens[1].text == '<label>' ?
 
576
             '%' + item.tokens[2].text.substr(1) :
 
577
             '%' + item.tokens[0].text)
 
578
      var niceLabel = toNiceIdent(rawLabel);
 
579
      return [{
 
580
        intertype: 'label',
 
581
        ident: niceLabel,
 
582
        lineNum: item.lineNum
 
583
      }];
 
584
    }
 
585
  });
 
586
 
 
587
  // TODO: remove dis
 
588
  substrate.addActor('Reintegrator', {
 
589
    processItem: function(item) {
 
590
      this.forwardItem(item, '/dev/stdout');
 
591
    }
 
592
  });
 
593
 
 
594
  // 'load'
 
595
  substrate.addActor('Load', {
 
596
    processItem: function(item) {
 
597
      item.intertype = 'load';
 
598
      cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]);
 
599
      item.pointerType = item.tokens[1].text;
 
600
      item.valueType = item.type = removePointing(item.pointerType);
 
601
      Types.needAnalysis[item.type] = 0;
 
602
      var last = getTokenIndexByText(item.tokens, ';');
 
603
      var segments = splitTokenList(item.tokens.slice(1, last));
 
604
      item.pointer = parseLLVMSegment(segments[0]);
 
605
      if (segments.length > 1) {
 
606
        assert(segments[1][0].text == 'align');
 
607
        item.align = parseInt(segments[1][1].text) || QUANTUM_SIZE; // 0 means preferred arch align
 
608
      } else {
 
609
        item.align = QUANTUM_SIZE;
 
610
      }
 
611
      item.ident = item.pointer.ident || null;
 
612
      this.forwardItem(item, 'Reintegrator');
 
613
    }
 
614
  });
 
615
  // 'extractvalue'
 
616
  substrate.addActor('ExtractValue', {
 
617
    processItem: function(item) {
 
618
      var last = getTokenIndexByText(item.tokens, ';');
 
619
      item.intertype = 'extractvalue';
 
620
      item.type = item.tokens[1].text; // Of the origin aggregate - not what we extract from it. For that, can only infer it later
 
621
      Types.needAnalysis[item.type] = 0;
 
622
      item.ident = toNiceIdent(item.tokens[2].text);
 
623
      item.indexes = splitTokenList(item.tokens.slice(4, last));
 
624
      this.forwardItem(item, 'Reintegrator');
 
625
    }
 
626
  });
 
627
  // 'insertvalue'
 
628
  substrate.addActor('InsertValue', {
 
629
    processItem: function(item) {
 
630
      var last = getTokenIndexByText(item.tokens, ';');
 
631
      item.intertype = 'insertvalue';
 
632
      item.type = item.tokens[1].text; // Of the origin aggregate, as well as the result
 
633
      Types.needAnalysis[item.type] = 0;
 
634
      item.ident = toNiceIdent(item.tokens[2].text);
 
635
      var segments = splitTokenList(item.tokens.slice(4, last));
 
636
      item.value = parseLLVMSegment(segments[0]);
 
637
      item.indexes = segments.slice(1);
 
638
      this.forwardItem(item, 'Reintegrator');
 
639
    }
 
640
  });
 
641
  // 'bitcast'
 
642
  substrate.addActor('Bitcast', {
 
643
    processItem: function(item) {
 
644
      item.intertype = 'bitcast';
 
645
      item.type = item.tokens[4].text; // The final type
 
646
      Types.needAnalysis[item.type] = 0;
 
647
      var to = getTokenIndexByText(item.tokens, 'to');
 
648
      item.params = [parseLLVMSegment(item.tokens.slice(1, to))];
 
649
      item.ident = item.params[0].ident;
 
650
      item.type2 = item.tokens[1].text; // The original type
 
651
      Types.needAnalysis[item.type2] = 0;
 
652
      this.forwardItem(item, 'Reintegrator');
 
653
    }
 
654
  });
 
655
  // 'getelementptr'
 
656
  substrate.addActor('GEP', {
 
657
    processItem: function(item) {
 
658
      var first = 0;
 
659
      while (!isType(item.tokens[first].text)) first++;
 
660
      Types.needAnalysis[item.tokens[first].text] = 0;
 
661
      var last = getTokenIndexByText(item.tokens, ';');
 
662
      var segment = [ item.tokens[first], { text: 'getelementptr' }, null, { item: {
 
663
        tokens: item.tokens.slice(first, last)
 
664
      } } ];
 
665
      var data = parseLLVMFunctionCall(segment);
 
666
      item.intertype = 'getelementptr';
 
667
      item.type = '*'; // We need type info to determine this - all we know is it's a pointer
 
668
      item.params = data.params;
 
669
      item.ident = data.ident;
 
670
      this.forwardItem(item, 'Reintegrator');
 
671
    }
 
672
  });
 
673
  // 'call', 'invoke'
 
674
  function makeCall(item, type) {
 
675
    item.intertype = type;
 
676
    if (['tail'].indexOf(item.tokens[0].text) != -1) {
 
677
      item.tokens.splice(0, 1);
 
678
    }
 
679
    while (item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) {
 
680
      item.tokens.splice(1, 1);
 
681
    }
 
682
    item.type = item.tokens[1].text;
 
683
    Types.needAnalysis[item.type] = 0;
 
684
    while (['@', '%'].indexOf(item.tokens[2].text[0]) == -1 && !(item.tokens[2].text in PARSABLE_LLVM_FUNCTIONS) &&
 
685
           item.tokens[2].text != 'null' && item.tokens[2].text != 'asm' && item.tokens[2].text != 'undef') {
 
686
      assert(item.tokens[2].text != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
 
687
      item.tokens.splice(2, 1);
 
688
    }
 
689
    var tokensLeft = item.tokens.slice(2);
 
690
    item.ident = eatLLVMIdent(tokensLeft);
 
691
    if (item.ident == 'asm') {
 
692
      // Inline assembly is just JavaScript that we paste into the code
 
693
      item.intertype = 'value';
 
694
      if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1);
 
695
      item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly
 
696
      return { forward: null, ret: [item], item: item };
 
697
    } 
 
698
    if (item.ident.substr(-2) == '()') {
 
699
      // See comment in isStructType()
 
700
      item.ident = item.ident.substr(0, item.ident.length-2);
 
701
      // Also, we remove some spaces which might occur.
 
702
      while (item.ident[item.ident.length-1] == ' ') {
 
703
        item.ident = item.ident.substr(0, item.ident.length-1);
 
704
      }
 
705
      item.params = [];
 
706
    } else {
 
707
      item.params = parseParamTokens(tokensLeft[0].item.tokens);
 
708
    }
 
709
    item.ident = toNiceIdent(item.ident);
 
710
    if (type === 'invoke') {
 
711
      var toIndex = findTokenText(item, 'to');
 
712
      item.toLabel = toNiceIdent(item.tokens[toIndex+2].text);
 
713
      item.unwindLabel = toNiceIdent(item.tokens[toIndex+5].text);
 
714
      assert(item.toLabel && item.unwindLabel);
 
715
    }
 
716
    if (item.indent == 2) {
 
717
      // standalone call - not in assign
 
718
      item.standalone = true;
 
719
      return { forward: null, ret: [item], item: item };
 
720
    }
 
721
    return { forward: item, ret: [], item: item };
 
722
  }
 
723
  substrate.addActor('Call', {
 
724
    processItem: function(item) {
 
725
      var result = makeCall.call(this, item, 'call');
 
726
      if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
 
727
      return result.ret;
 
728
    }
 
729
  });
 
730
  substrate.addActor('Invoke', {
 
731
    processItem: function(item) {
 
732
      var result = makeCall.call(this, item, 'invoke');
 
733
      if (DISABLE_EXCEPTION_CATCHING == 1) {
 
734
        result.item.intertype = 'call';
 
735
        result.ret.push({
 
736
          intertype: 'branch',
 
737
          label: result.item.toLabel,
 
738
          lineNum: (result.forward ? item.parentLineNum : item.lineNum) + 0.5
 
739
        });
 
740
      }
 
741
      if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
 
742
      return result.ret;
 
743
    }
 
744
  });
 
745
  substrate.addActor('Atomic', {
 
746
    processItem: function(item) {
 
747
      item.intertype = 'atomic';
 
748
      if (item.tokens[0].text == 'atomicrmw') {
 
749
        if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
 
750
        item.op = item.tokens[1].text;
 
751
        item.tokens.splice(1, 1);
 
752
      } else {
 
753
        assert(item.tokens[0].text == 'cmpxchg')
 
754
        if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1);
 
755
        item.op = 'cmpxchg';
 
756
      }
 
757
      var last = getTokenIndexByText(item.tokens, ';');
 
758
      item.params = splitTokenList(item.tokens.slice(1, last)).map(parseLLVMSegment);
 
759
      item.type = item.params[1].type;
 
760
      this.forwardItem(item, 'Reintegrator');
 
761
    }
 
762
  });
 
763
  // 'landingpad'
 
764
  substrate.addActor('Landingpad', {
 
765
    processItem: function(item) {
 
766
      item.intertype = 'landingpad';
 
767
      item.type = item.tokens[1].text;
 
768
      item.catchables = [];
 
769
      var catchIdx = findTokenText(item, "catch");
 
770
      if (catchIdx != -1) {
 
771
        do {
 
772
          var nextCatchIdx = findTokenTextAfter(item, "catch", catchIdx+1);
 
773
          if (nextCatchIdx == -1)
 
774
            nextCatchIdx = item.tokens.length;
 
775
          item.catchables.push(parseLLVMSegment(item.tokens.slice(catchIdx+2, nextCatchIdx)));
 
776
          catchIdx = nextCatchIdx;
 
777
        } while (catchIdx != item.tokens.length);
 
778
      }
 
779
      Types.needAnalysis[item.type] = 0;
 
780
      this.forwardItem(item, 'Reintegrator');
 
781
    }
 
782
  });
 
783
  // 'alloca'
 
784
  var allocaPossibleVars = ['allocatedNum'];
 
785
  substrate.addActor('Alloca', {
 
786
    processItem: function(item) {
 
787
      item.intertype = 'alloca';
 
788
      item.allocatedType = item.tokens[1].text;
 
789
      if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) {
 
790
        item.allocatedNum = toNiceIdent(item.tokens[4].text);
 
791
        item.possibleVars = allocaPossibleVars;
 
792
      } else {
 
793
        item.allocatedNum = 1;
 
794
      }
 
795
      item.type = addPointing(item.tokens[1].text); // type of pointer we will get
 
796
      Types.needAnalysis[item.type] = 0;
 
797
      item.type2 = item.tokens[1].text; // value we will create, and get a pointer to
 
798
      Types.needAnalysis[item.type2] = 0;
 
799
      this.forwardItem(item, 'Reintegrator');
 
800
    }
 
801
  });
 
802
  // 'phi'
 
803
  substrate.addActor('Phi', {
 
804
    processItem: function(item) {
 
805
      item.intertype = 'phi';
 
806
      item.type = item.tokens[1].text;
 
807
      var typeToken = [item.tokens[1]];
 
808
      Types.needAnalysis[item.type] = 0;
 
809
      var last = getTokenIndexByText(item.tokens, ';');
 
810
      item.params = splitTokenList(item.tokens.slice(2, last)).map(function(segment) {
 
811
        var subSegments = splitTokenList(segment[0].item.tokens);
 
812
        var ret = {
 
813
          intertype: 'phiparam',
 
814
          label: toNiceIdent(subSegments[1][0].text),
 
815
          value: parseLLVMSegment(typeToken.concat(subSegments[0]))
 
816
        };
 
817
        return ret;
 
818
      }).filter(function(param) { return param.value && param.value.ident != 'undef' });
 
819
      this.forwardItem(item, 'Reintegrator');
 
820
    }
 
821
  });
 
822
  // 'phi'
 
823
  substrate.addActor('va_arg', {
 
824
    processItem: function(item) {
 
825
      item.intertype = 'va_arg';
 
826
      var segments = splitTokenList(item.tokens.slice(1));
 
827
      item.type = segments[1][0].text;
 
828
      item.value = parseLLVMSegment(segments[0]);
 
829
      this.forwardItem(item, 'Reintegrator');
 
830
    }
 
831
  });
 
832
  // mathops
 
833
  substrate.addActor('Mathops', {
 
834
    processItem: function(item) {
 
835
      item.intertype = 'mathop';
 
836
      item.op = item.tokens[0].text;
 
837
      item.variant = null;
 
838
      while (item.tokens[1].text in NSW_NUW) item.tokens.splice(1, 1);
 
839
      if (['icmp', 'fcmp'].indexOf(item.op) != -1) {
 
840
        item.variant = item.tokens[1].text;
 
841
        item.tokens.splice(1, 1);
 
842
      }
 
843
      if (item.tokens[1].text == 'exact') item.tokens.splice(1, 1); // TODO: Implement trap values
 
844
      var segments = splitTokenList(item.tokens.slice(1));
 
845
      item.params = [];
 
846
      for (var i = 1; i <= 4; i++) {
 
847
        if (segments[i-1]) {
 
848
          if (i > 1 && segments[i-1].length == 1 && segments[0].length > 1 && !isType(segments[i-1][0].text)) {
 
849
            segments[i-1].unshift(segments[0][0]); // Add the type from the first segment, they are all alike
 
850
          }
 
851
          item.params[i-1] = parseLLVMSegment(segments[i-1]);
 
852
        }
 
853
      }
 
854
      var setParamTypes = true;
 
855
      if (item.op === 'select') {
 
856
        assert(item.params[1].type === item.params[2].type);
 
857
        item.type = item.params[1].type;
 
858
      } else if (item.op in LLVM.CONVERSIONS) {
 
859
        item.type = item.params[1].type;
 
860
        setParamTypes = false;
 
861
      } else {
 
862
        item.type = item.params[0].type;
 
863
      }
 
864
      if (setParamTypes) {
 
865
        for (var i = 0; i < 4; i++) {
 
866
          if (item.params[i]) item.params[i].type = item.type; // All params have the same type, normally
 
867
        }
 
868
      }
 
869
      if (item.op in LLVM.EXTENDS) {
 
870
        item.type = item.params[1].ident;
 
871
        item.params[0].type = item.params[1].type;
 
872
        // TODO: also remove 2nd param?
 
873
      } else if (item.op in LLVM.COMPS) {
 
874
        item.type = 'i1';
 
875
      }
 
876
      if (USE_TYPED_ARRAYS == 2) {
 
877
        // Some specific corrections, since 'i64' is special
 
878
        if (item.op in LLVM.SHIFTS) {
 
879
          item.params[1].type = 'i32';
 
880
        } else if (item.op == 'select') {
 
881
          item.params[0].type = 'i1';
 
882
        }
 
883
      }
 
884
      Types.needAnalysis[item.type] = 0;
 
885
      this.forwardItem(item, 'Reintegrator');
 
886
    }
 
887
  });
 
888
  // 'store'
 
889
  substrate.addActor('Store', {
 
890
    processItem: function(item) {
 
891
      cleanOutTokens(LLVM.ACCESS_OPTIONS, item.tokens, [0, 1]);
 
892
      var segments = splitTokenList(item.tokens.slice(1));
 
893
      var ret = {
 
894
        intertype: 'store',
 
895
        valueType: item.tokens[1].text,
 
896
        value: parseLLVMSegment(segments[0]),
 
897
        pointer: parseLLVMSegment(segments[1]),
 
898
        lineNum: item.lineNum
 
899
      };
 
900
      Types.needAnalysis[ret.valueType] = 0;
 
901
      ret.ident = toNiceIdent(ret.pointer.ident);
 
902
      ret.pointerType = ret.pointer.type;
 
903
      Types.needAnalysis[ret.pointerType] = 0;
 
904
      if (segments.length > 2) {
 
905
        assert(segments[2][0].text == 'align');
 
906
        ret.align = parseInt(segments[2][1].text) || QUANTUM_SIZE; // 0 means preferred arch align
 
907
      } else {
 
908
        ret.align = QUANTUM_SIZE;
 
909
      }
 
910
      return [ret];
 
911
    }
 
912
  });
 
913
  // 'br'
 
914
  substrate.addActor('Branch', {
 
915
    processItem: function(item) {
 
916
      if (item.tokens[1].text == 'label') {
 
917
        return [{
 
918
          intertype: 'branch',
 
919
          label: toNiceIdent(item.tokens[2].text),
 
920
          lineNum: item.lineNum
 
921
        }];
 
922
      } else {
 
923
        var commaIndex = findTokenText(item, ',');
 
924
        return [{
 
925
          intertype: 'branch',
 
926
          value: parseLLVMSegment(item.tokens.slice(1, commaIndex)),
 
927
          labelTrue: toNiceIdent(item.tokens[commaIndex+2].text),
 
928
          labelFalse: toNiceIdent(item.tokens[commaIndex+5].text),
 
929
          lineNum: item.lineNum
 
930
        }];
 
931
      }
 
932
    }
 
933
  });
 
934
  // 'ret'
 
935
  substrate.addActor('Return', {
 
936
    processItem: function(item) {
 
937
      var type = item.tokens[1].text;
 
938
      Types.needAnalysis[type] = 0;
 
939
      return [{
 
940
        intertype: 'return',
 
941
        type: type,
 
942
        value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(1)) : null,
 
943
        lineNum: item.lineNum
 
944
      }];
 
945
    }
 
946
  });
 
947
  // 'resume' - partial implementation
 
948
  substrate.addActor('Resume', {
 
949
    processItem: function(item) {
 
950
      return [{
 
951
        intertype: 'resume',
 
952
        ident: toNiceIdent(item.tokens[2].text),
 
953
        lineNum: item.lineNum
 
954
      }];
 
955
    }
 
956
  });
 
957
  // 'switch'
 
958
  substrate.addActor('Switch', {
 
959
    processItem: function(item) {
 
960
      function parseSwitchLabels(item) {
 
961
        var ret = [];
 
962
        var tokens = item.item.tokens;
 
963
        while (tokens.length > 0) {
 
964
          ret.push({
 
965
            value: tokens[1].text,
 
966
            label: toNiceIdent(tokens[4].text)
 
967
          });
 
968
          tokens = tokens.slice(5);
 
969
        }
 
970
        return ret;
 
971
      }
 
972
      var type = item.tokens[1].text;
 
973
      Types.needAnalysis[type] = 0;
 
974
      return [{
 
975
        intertype: 'switch',
 
976
        type: type,
 
977
        ident: toNiceIdent(item.tokens[2].text),
 
978
        defaultLabel: toNiceIdent(item.tokens[5].text),
 
979
        switchLabels: parseSwitchLabels(item.tokens[6]),
 
980
        lineNum: item.lineNum
 
981
      }];
 
982
    }
 
983
  });
 
984
  // function end
 
985
  substrate.addActor('FuncEnd', {
 
986
    processItem: function(item) {
 
987
      return [{
 
988
        intertype: 'functionEnd',
 
989
        lineNum: item.lineNum
 
990
      }];
 
991
    }
 
992
  });
 
993
  // external function stub
 
994
  substrate.addActor('External', {
 
995
    processItem: function(item) {
 
996
      while (item.tokens[1].text in LLVM.LINKAGES || item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.VISIBILITIES || item.tokens[1].text in LLVM.CALLING_CONVENTIONS) {
 
997
        item.tokens.splice(1, 1);
 
998
      }
 
999
      var params = parseParamTokens(item.tokens[3].item.tokens);
 
1000
      return [{
 
1001
        intertype: 'functionStub',
 
1002
        ident: toNiceIdent(item.tokens[2].text),
 
1003
        returnType: item.tokens[1],
 
1004
        params: params,
 
1005
        hasVarArgs: hasVarArgs(params),
 
1006
        lineNum: item.lineNum
 
1007
      }];
 
1008
    }
 
1009
  });
 
1010
  // 'unreachable'
 
1011
  substrate.addActor('Unreachable', {
 
1012
    processItem: function(item) {
 
1013
      return [{
 
1014
        intertype: 'unreachable',
 
1015
        lineNum: item.lineNum
 
1016
      }];
 
1017
    }
 
1018
  });
 
1019
  // 'indirectbr'
 
1020
  substrate.addActor('IndirectBr', {
 
1021
    processItem: function(item) {
 
1022
      var ret = {
 
1023
        intertype: 'indirectbr',
 
1024
        value: parseLLVMSegment(splitTokenList(item.tokens.slice(1))[0]),
 
1025
        type: item.tokens[1].text,
 
1026
        lineNum: item.lineNum
 
1027
      };
 
1028
      Types.needAnalysis[ret.type] = 0;
 
1029
      return [ret];
 
1030
    }
 
1031
  });
 
1032
 
 
1033
  // Input
 
1034
 
 
1035
  substrate.addItem({
 
1036
    llvmLines: data
 
1037
  }, 'LineSplitter');
 
1038
 
 
1039
  substrate.onResult = function(result) {
 
1040
    if (result.tokens) result.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here.
 
1041
  };
 
1042
 
 
1043
  return substrate.solve();
 
1044
}
 
1045