-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmalloc_support_lib.js
300 lines (266 loc) · 10.3 KB
/
malloc_support_lib.js
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Object to fit to WebAssembly Runtime
let Module = {};
/**
* MEMORY CONSTANTS
*/
const WASM_PAGE_SIZE = 65536;
const MIN_TOTAL_MEMORY = 16777216;
let TOTAL_STACK = 5242880;
let TOTAL_MEMORY = 16777216;
let HEAP,
/** @type {ArrayBuffer} */
buffer,
/** @type {Int8Array} */
HEAP8,
/** @type {Uint8Array} */
HEAPU8,
/** @type {Int16Array} */
HEAP16,
/** @type {Uint16Array} */
HEAPU16,
/** @type {Int32Array} */
HEAP32,
/** @type {Uint32Array} */
HEAPU32,
/** @type {Float32Array} */
HEAPF32,
/** @type {Float64Array} */
HEAPF64;
// Memory management values
let STATIC_BASE, STATICTOP, staticSealed; // static area
let STACK_BASE, STACKTOP, STACK_MAX; // stack area
let DYNAMIC_BASE, DYNAMICTOP_PTR; // dynamic area handled by sbrk
// Initializing memory constants
STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0;
staticSealed = false;
// Set the reallocBuffer function used by `growMemory` which is called my _sbrk
Module['reallocBuffer'] = wasmReallocBuffer;
if (TOTAL_MEMORY < TOTAL_STACK)
console.warn('TOTAL_MEMORY should be larger than TOTAL_STACK, was '
+ TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')');
// Initializing buffer
// Use a provided buffer, if there is one, or else allocate a new one
// Use a WebAssembly memory where available
assert(TOTAL_MEMORY % WASM_PAGE_SIZE === 0);
Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE});
buffer = Module['wasmMemory'].buffer;
assert(buffer.byteLength === TOTAL_MEMORY);
Module['buffer'] = buffer;
updateGlobalBufferViews();
/**
* Memory constants
*/
staticSealed = false;
const GLOBAL_BASE = 1024;
const ERROR_LOCATION_WASM = 1520; // This location was generated dynamically by the Emscripten compiler
// (Check the `__errno_location()` function in Wasm)
// Inside the Wasm code, it contains certain globals and static information
// to run malloc/free, stackAlloc, stackRestore. What is saved there exactly,
// Since we are only really interested in using the stack and heap, we just have
// to make sure we write using malloc/free/stackAlloc/stackRestore.
const STATICBUMP = 1536; // Static size coming from the wasm module memory set-up from Emscripten
const STACK_ALIGN = 16; // Stack is 16 byte aligned as the meaning allocation is normally 16 bytes for mallocs.
STATIC_BASE = GLOBAL_BASE; // Global_base is at 1024, I think in wasm there is some pointers "hardcoded"
// in those addresses.
STATICTOP = STATIC_BASE + STATICBUMP; // STATIC END
staticAlloc(1032); // This manually adds the static offset occupied by my WebAssembly module string errors.
// Check the data segments in my `matmachjs.wat` module.
DYNAMICTOP_PTR = staticAlloc(4); // Allocate address to store DYNAMIC_PTR
STACK_BASE = STACKTOP = alignMemory(STATICTOP);
STACK_MAX = STACK_BASE + TOTAL_STACK;
DYNAMIC_BASE = alignMemory(STACK_MAX);
staticSealed = true; // seal the static portion of memory
HEAP32[DYNAMICTOP_PTR>>2] = DYNAMIC_BASE;
// Sanity check for dynamic base.
assert(DYNAMIC_BASE < TOTAL_MEMORY, "TOTAL_MEMORY not big enough for stack");
/**
* Aligns memory given a factor, if the factor is not defined, it uses STACK_ALIGN
* @param {number} size
* @param {number} factor
*/
function alignMemory(size, factor) {
if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default
var ret = size = Math.ceil(size / factor) * factor;
return ret;
}
/**
* Allocates a given size on the global static, makes sure it is 16 byte aligned
* @param {number} size
*/
function staticAlloc(size) {
assert(!staticSealed);
var ret = STATICTOP;
STATICTOP = (STATICTOP + size + 15) & -16;
return ret;
}
/**
* Fails with stack overflow error
* @param {size} allocSize
*/
function abortStackOverflow(allocSize) {
abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - stackSave() + allocSize) + ' bytes available!');
}
/**
* Abort function from Emscripten
* Modified(dherr3): I have removed the printing with decoractors
* part of it and simple now it simple throws an error.
* @param {string} what
*/
function abort(what) {
if (what !== undefined) {
console.log(what);
console.warn(what); // Replace
what = JSON.stringify(what)
} else {
what = '';
}
ABORT = true;
EXITSTATUS = 1;
const output = new Error(what);
throw output;
}
/**
* Assert function used throughout the module
* @param {boolean} condition
* @param {string} text
*/
function assert(condition, text) {
if (!condition) {
abort('Assertion failed: ' + text);
}
}
/**
* Error thrown by Wasm's `_sbrk()`
*/
function abortOnCannotGrowMemory() {
abort('Out-of-memory: Cannot enlarge memory arrays.');
}
// __errno_location imported from the wasm module. I used `ERROR_LOCATION_WASM` since it is already
// to this value in my wasm module
Module["___errno_location"] = ERROR_LOCATION_WASM;
/**
* Sets the error, this is call in
* TODO (dherre3): Still need to understand how this works
* @param {number} value
*/
function ___setErrNo(value) {
if (Module['___errno_location']) HEAP32[((Module['___errno_location']())>>2)]=value;
else console.warn('failed to set errno from JS');
return value;
}
/**
* Updating the big module buffer. Call by `enlargeMemory()`
* @param {Array<byte>} buf
*/
function updateGlobalBuffer(buf) {
Module['buffer'] = buffer = buf;
}
/**
* Gets total memory for the sytem. Called by wasm module
*/
function getTotalMemory() {
return TOTAL_MEMORY;
}
/**
* Updating global buffer views
*/
function updateGlobalBufferViews() {
Module['HEAP8'] = HEAP8 = new Int8Array(buffer);
Module['HEAP16'] = HEAP16 = new Int16Array(buffer);
Module['HEAP32'] = HEAP32 = new Int32Array(buffer);
Module['HEAPU8'] = HEAPU8 = new Uint8Array(buffer);
Module['HEAPU16'] = HEAPU16 = new Uint16Array(buffer);
Module['HEAPU32'] = HEAPU32 = new Uint32Array(buffer);
Module['HEAPF32'] = HEAPF32 = new Float32Array(buffer);
Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer);
}
/**
* Size to use vuffer and reallocate
* @param {number} size
*/
function wasmReallocBuffer(size) {
// Align the size to be a WASM_PAGE_SIZE
size = alignUp(size, WASM_PAGE_SIZE); // round up to wasm page size
var old = Module['buffer'];
var oldSize = old.byteLength;
try {
var result = Module['wasmMemory'].grow((size - oldSize) / WASM_PAGE_SIZE); // .grow() takes a delta compared to the previous size
if (result !== (-1 | 0)) {
// success in native wasm memory growth, get the buffer from the memory
return Module['buffer'] = Module['wasmMemory'].buffer;
} else {
return null;
}
} catch(e) {
console.error('Module.reallocBuffer: Attempted to grow from ' + oldSize + ' bytes to ' + size + ' bytes, but got error: ' + e);
return null;
}
}
/**
* Aligns memory given a factor and a size.
* @param {number} x
* @param {number} multiple
*/
function alignUp(x, multiple) {
if (x % multiple > 0) {
x += multiple - (x % multiple);
}
return x;
}
/**
* Enlarges memory, used by the `_sbrk()` call in WebAssembly
*/
function enlargeMemory() {
// TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top.
assert(HEAP32[DYNAMICTOP_PTR>>2] > TOTAL_MEMORY); // This function should only ever be called after the ceiling of the dynamic heap has already been bumped to exceed the current total size of the asm.js heap.
const PAGE_MULTIPLE = WASM_PAGE_SIZE; // In wasm, heap size must be a multiple of 64KB. In asm.js, they need to be multiples of 16MB.
const LIMIT = 2147483648 - PAGE_MULTIPLE; // We can do one page short of 2GB as theoretical maximum.
if (HEAP32[DYNAMICTOP_PTR>>2] > LIMIT) {
console.warn('Cannot enlarge memory, asked to go up to ' + HEAP32[DYNAMICTOP_PTR>>2] + ' bytes, but the limit is ' + LIMIT + ' bytes!');
return false;
}
let OLD_TOTAL_MEMORY = TOTAL_MEMORY;
TOTAL_MEMORY = Math.max(TOTAL_MEMORY, MIN_TOTAL_MEMORY); // So the loop below will not be infinite, and minimum asm.js memory size is 16MB.
while (TOTAL_MEMORY < HEAP32[DYNAMICTOP_PTR>>2]) { // Keep incrementing the heap size as long as it's less than what is requested.
if (TOTAL_MEMORY <= 536870912) {
TOTAL_MEMORY = alignUp(2 * TOTAL_MEMORY, PAGE_MULTIPLE); // Simple heuristic: double until 1GB...
} else {
TOTAL_MEMORY = Math.min(alignUp((3 * TOTAL_MEMORY + 2147483648) / 4, PAGE_MULTIPLE), LIMIT); // ..., but after that, add smaller increments towards 2GB, which we cannot reach
}
}
const start = Date.now();
let replacement = Module['reallocBuffer'](TOTAL_MEMORY);
if (!replacement || replacement.byteLength != TOTAL_MEMORY) {
console.warn('Failed to grow the heap from ' + OLD_TOTAL_MEMORY + ' bytes to ' + TOTAL_MEMORY + ' bytes, not enough memory!');
if (replacement) {
console.warn('Expected to get back a buffer of size ' + TOTAL_MEMORY + ' bytes, but instead got back a buffer of size ' + replacement.byteLength);
}
// restore the state to before this call, we failed
TOTAL_MEMORY = OLD_TOTAL_MEMORY;
return false;
}
// everything worked
updateGlobalBuffer(replacement);
updateGlobalBufferViews();
console.warn('enlarged memory arrays from ' + OLD_TOTAL_MEMORY + ' to ' + TOTAL_MEMORY + ', took ' + (Date.now() - start) + ' ms (has ArrayBuffer.transfer? ' + (!!ArrayBuffer.transfer) + ')');
return true;
}
/**
* DEFINITION OF MODULE IMPORTS
*/
// Used by memory management in the wasm module
Module.env = {
"DYNAMICTOP_PTR": DYNAMICTOP_PTR,
"STACKTOP": STACKTOP,
"STACK_MAX": STACK_MAX,
"memoryBase": STATIC_BASE, // Not necessary for malloc, but may be useful
"abort": abort,
"assert": assert,
"memory":Module["wasmMemory"],
"enlargeMemory": enlargeMemory,
"getTotalMemory": getTotalMemory,
"abortOnCannotGrowMemory": abortOnCannotGrowMemory,
"abortStackOverflow": abortStackOverflow,
"___setErrNo": ___setErrNo
};
module.exports = Module;