aboutsummaryrefslogtreecommitdiff
path: root/src/runtime.js
blob: d6ce9c3d862ba954c07c66d38cd3c4b22cdbe62b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
////////////QUANTUM_SIZE = GUARD_STACK = 1;
// Generates code that can be placed inline in generated code.
// This is not the cleanest way to write this kind of code - it is
// optimized for generating fast inline code.
RuntimeGenerator = {
  alloc: function(size, type) {
    var ret = type + 'TOP';
    if (GUARD_MEMORY) {
      ret += '; assert(' + size + ' > 0)';
    }
    var initMemory = 'for (var i = 0; i < ' + size + '; i++) ' + (
      USE_TYPED_ARRAYS ?
        'IHEAP[' + type + 'TOP+i] = FHEAP[' + type + 'TOP+i] = 0' :
        'HEAP[' + type + 'TOP+i] = 0'
    );
    ret += '; ' + initMemory;
    ret += '; ' + type + 'TOP += ' + size;
    if (QUANTUM_SIZE > 1) {
      ret += ';' + RuntimeGenerator.alignMemory(type + 'TOP', QUANTUM_SIZE);
    }
    return ret;
  },

  // An allocation that lives as long as the current function call
  stackAlloc: function(size) {
    var ret = RuntimeGenerator.alloc(size, 'STACK');
    if (GUARD_MEMORY) {
      ret += '; assert(STACKTOP < STACK_ROOT + STACK_MAX)';
    }
    return ret;
  },

  stackEnter: function(initial) {
    if (initial === 0) return ''; // XXX Note that we don't even push the stack! This is faster, but
                                  // means that we don't clear stack allocations done in this function
                                  // until the parent unwinds its stack. So potentially if we are in
                                  // a loop, we can use a lot of memory.
    var ret = 'var __stackBase__  = STACKTOP; STACKTOP += ' + initial;
    if (GUARD_MEMORY) {
      ret += '; assert(STACKTOP < STACK_MAX)';
    }
    var initMemory = 'for (var i = __stackBase__; i < STACKTOP; i++) ' + (
      USE_TYPED_ARRAYS ?
        'IHEAP[i] = FHEAP[i] = 0' :
        'HEAP[i] = 0'
    );
    ret += '; ' + initMemory;
    return ret;
  },

  stackExit: function(initial) {
    if (initial === 0) return ''; // XXX See comment in stackEnter
    var ret = '';
    if (SAFE_HEAP) {
      ret += 'for (var i = __stackBase__; i < STACKTOP; i++) SAFE_HEAP_CLEAR(i);';
    }
    return ret += 'STACKTOP = __stackBase__';
  },

  // An allocation that cannot be free'd
  staticAlloc: function(size) {
    return RuntimeGenerator.alloc(size, 'STATIC');
  },

  alignMemory: function(target, quantum) {
    if (typeof quantum !== 'number') {
      quantum = '(quantum ? quantum : QUANTUM_SIZE)';
    }
    return target + ' = Math.ceil(' + target + '/' + quantum + ')*' + quantum + ';';
  }
};

function unInline(name_, params) {
  var src = '(function ' + name_ + '(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })';
  //print('src: ' + src);
  return eval(src);
}

// Uses the RuntimeGenerator during compilation, in order to
//  1. Let the compiler access and run those functions during compilation
//  2. We expose the entire Runtime object to generated code, so it can
//     use that functionality in a non-inline manner.
Runtime = {
  stackAlloc: unInline('stackAlloc', ['size']),
  staticAlloc: unInline('staticAlloc', ['size']),
  alignMemory: unInline('alignMemory', ['size', 'quantum']),

  getFunctionIndex: function getFunctionIndex(func, ident) {
    var key = FUNCTION_TABLE.length;
    FUNCTION_TABLE[key] = func;
    FUNCTION_TABLE[key+1] = null; // Need to have keys be even numbers, see |polymorph| test
    Module[ident] = func; // Export using full name, for Closure Compiler
    return key;
  },

  // TODO: cleanup
  isNumberType: function(type) {
    return type in Runtime.INT_TYPES || type in Runtime.FLOAT_TYPES;
  },

  isPointerType: isPointerType,
  isStructType: isStructType,

  INT_TYPES: set('i1', 'i8', 'i16', 'i32', 'i64'),
  FLOAT_TYPES: set('float', 'double'),

  getNativeFieldSize: getNativeFieldSize,
  dedup: dedup,

  set: set,

  // Calculate aligned size, just like C structs should be. TODO: Consider
  // requesting that compilation be done with #pragma pack(push) /n #pragma pack(1),
  // which would remove much of the complexity here.
  calculateStructAlignment: function calculateStructAlignment(type, otherTypes) {
    type.flatSize = 0;
    var diffs = [];
    var prev = -1, maxSize = -1;
    type.flatIndexes = type.fields.map(function(field) {
      var size;
      if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) {
        size = Runtime.getNativeFieldSize(field, true); // pack char; char; in structs, also char[X]s.
        maxSize = Math.max(maxSize, size);
      } else if (Runtime.isStructType(field)) {
        size = otherTypes[field].flatSize;
        maxSize = Math.max(maxSize, QUANTUM_SIZE);
      } else {
        dprint('Unclear type in struct: ' + field + ', in ' + type.name_);
        assert(0);
      }
      var curr = Runtime.alignMemory(type.flatSize, Math.min(QUANTUM_SIZE, size)); // if necessary, place this on aligned memory
      type.flatSize = curr + size;
      if (prev >= 0) {
        diffs.push(curr-prev);
      }
      prev = curr;
      return curr;
    });
    type.flatSize = Runtime.alignMemory(type.flatSize, maxSize);
    if (diffs.length == 0) {
      type.flatFactor = type.flatSize;
    } else if (Runtime.dedup(diffs).length == 1) {
      type.flatFactor = diffs[0];
    }
    type.needsFlattening = (type.flatFactor != 1);
    return type.flatIndexes;
  }

};

function getRuntime() {
  var ret = 'Runtime = {\n';
  for (i in Runtime) {
    var item = Runtime[i];
    ret += '  ' + i + ': ';
    if (typeof item === 'function') {
      ret += item.toString();
    } else {
      ret += JSON.stringify(item);
    }
    ret += ',\n';
  }
  return ret + '  __dummy__: 0\n}\n';
}