aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js29
-rw-r--r--src/compiler.js7
-rw-r--r--src/compiler_funcs.html28
-rw-r--r--src/library.js19
-rw-r--r--src/library_gl.js56
-rw-r--r--src/parseTools.js56
6 files changed, 166 insertions, 29 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 750f2a4c..3fb20253 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -30,6 +30,8 @@ function analyzer(data, sidePass) {
var item = { items: data };
var data = item;
+ var newTypes = {};
+
// Gather
// Single-liners
['globalVariable', 'functionStub', 'unparsedFunction', 'unparsedGlobals', 'unparsedTypes', 'alias'].forEach(function(intertype) {
@@ -42,6 +44,7 @@ function analyzer(data, sidePass) {
temp.splitOut.forEach(function(type) {
//dprint('types', 'adding defined type: ' + type.name_);
Types.types[type.name_] = type;
+ newTypes[type.name_] = 1;
if (QUANTUM_SIZE === 1) {
Types.fatTypes[type.name_] = copy(type);
}
@@ -897,7 +900,7 @@ function analyzer(data, sidePass) {
});
}
- function addTypeInternal(type, data) {
+ function addTypeInternal(type) {
if (type.length == 1) return;
if (Types.types[type]) return;
if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return;
@@ -908,8 +911,9 @@ function analyzer(data, sidePass) {
// to look at the underlying type - it was not defined explicitly
// anywhere else.
var nonPointing = removeAllPointing(type);
+ if (Types.types[nonPointing]) return;
var check = /^\[(\d+)\ x\ (.*)\]$/.exec(nonPointing);
- if (check && !Types.types[nonPointing]) {
+ if (check) {
var num = parseInt(check[1]);
num = Math.max(num, 1); // [0 x something] is used not for allocations and such of course, but
// for indexing - for an |array of unknown length|, basically. So we
@@ -917,7 +921,7 @@ function analyzer(data, sidePass) {
// check that we never allocate with this (either as a child structure
// in the analyzer, or in calcSize in alloca).
var subType = check[2];
- addTypeInternal(subType, data); // needed for anonymous structure definitions (see below)
+ addTypeInternal(subType); // needed for anonymous structure definitions (see below)
// Huge structural types are represented very inefficiently, both here and in generated JS. Best to avoid them - for example static char x[10*1024*1024]; is bad, while static char *x = malloc(10*1024*1024) is fine.
if (num >= 10*1024*1024) warnOnce('warning: very large fixed-size structural type: ' + type + ' - can you reduce it? (compilation may be slow)');
@@ -926,6 +930,7 @@ function analyzer(data, sidePass) {
fields: range(num).map(function() { return subType }),
lineNum: '?'
};
+ newTypes[nonPointing] = 1;
// Also add a |[0 x type]| type
var zerod = '[0 x ' + subType + ']';
if (!Types.types[zerod]) {
@@ -934,6 +939,7 @@ function analyzer(data, sidePass) {
fields: [subType, subType], // Two, so we get the flatFactor right. We care about the flatFactor, not the size here
lineNum: '?'
};
+ newTypes[zerod] = 1;
}
return;
}
@@ -964,6 +970,7 @@ function analyzer(data, sidePass) {
packed: packed,
lineNum: '?'
};
+ newTypes[type] = 1;
return;
}
@@ -975,13 +982,14 @@ function analyzer(data, sidePass) {
flatSize: 1,
lineNum: '?'
};
+ newTypes[type] = 1;
}
- function addType(type, data) {
- addTypeInternal(type, data);
+ function addType(type) {
+ addTypeInternal(type);
if (QUANTUM_SIZE === 1) {
Types.flipTypes();
- addTypeInternal(type, data);
+ addTypeInternal(type);
Types.flipTypes();
}
}
@@ -992,7 +1000,7 @@ function analyzer(data, sidePass) {
// which handles type definitions, and later. Doing so before the first side pass will result in
// making bad guesses about types which are actually defined
for (var type in Types.needAnalysis) {
- if (type) addType(type, data);
+ if (type) addType(type);
}
Types.needAnalysis = {};
}
@@ -1021,17 +1029,18 @@ function analyzer(data, sidePass) {
var more = true;
while (more) {
more = false;
- for (var typeName in types) {
+ for (var typeName in newTypes) {
var type = types[typeName];
if (type.flatIndexes) continue;
var ready = true;
type.fields.forEach(function(field) {
if (isStructType(field)) {
if (!types[field]) {
- addType(field, item);
+ addType(field);
ready = false;
} else {
if (!types[field].flatIndexes) {
+ newTypes[field] = 1;
ready = false;
}
}
@@ -1058,6 +1067,8 @@ function analyzer(data, sidePass) {
Runtime.QUANTUM_SIZE = trueQuantumSize;
Types.flipTypes();
}
+
+ newTypes = null;
}
// Variable analyzer
diff --git a/src/compiler.js b/src/compiler.js
index 4f16986c..77abd53b 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -215,7 +215,12 @@ load('analyzer.js');
load('jsifier.js');
if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase
RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) };
- load(RELOOPER);
+ try {
+ load(RELOOPER);
+ } catch(e) {
+ printErr('cannot find relooper at ' + RELOOPER + ', trying in current dir');
+ load('relooper.js');
+ }
assert(typeof Relooper != 'undefined');
}
globalEval(processMacros(preprocess(read('runtime.js'))));
diff --git a/src/compiler_funcs.html b/src/compiler_funcs.html
new file mode 100644
index 00000000..a769955b
--- /dev/null
+++ b/src/compiler_funcs.html
@@ -0,0 +1,28 @@
+<html>
+<body>
+<h2>Run the emscripten compiler in a web page, just for laughs</h2>
+Open the web console to see stderr output
+<hr>
+<pre id="output"></pre>
+<script>
+ arguments = ['tmp/emscripten_temp/tmpgo4sBT.txt', 'tmp/emscripten_temp/tmph9FP9W.func_0.ll', 'funcs', 'tmp/emscripten_temp/tmp0Aj1Vk.json']; // copy from emscripten.py output
+
+ var outputElement = document.getElementById('output');
+ print = function(x) {
+ outputElement.innerHTML += 'output hidden, profiling mode';
+ print = function(){};
+ //outputElement.innerHTML += x;
+ };
+
+ // For generated code
+ var Module = {
+ print: function(x) {
+ throw 'what?'
+ }
+ };
+</script>
+<script src="compiler.js">
+</script>
+</body>
+</html>
+
diff --git a/src/library.js b/src/library.js
index ee38cf5d..326369c5 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3812,6 +3812,7 @@ LibraryManager.library = {
},
// We always assume ASCII locale.
strcoll: 'strcmp',
+ strcoll_l: 'strcmp',
strcasecmp__asm: true,
strcasecmp__sig: 'iii',
@@ -4078,6 +4079,7 @@ LibraryManager.library = {
}
},
_toupper: 'toupper',
+ toupper_l: 'toupper',
tolower__asm: true,
tolower__sig: 'ii',
@@ -4088,54 +4090,65 @@ LibraryManager.library = {
return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0;
},
_tolower: 'tolower',
+ tolower_l: 'tolower',
// The following functions are defined as macros in glibc.
islower: function(chr) {
return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}};
},
+ islower_l: 'islower',
isupper: function(chr) {
return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}};
},
+ isupper_l: 'isupper',
isalpha: function(chr) {
return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
(chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
},
+ isalpha_l: 'isalpha',
isdigit: function(chr) {
return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}};
},
- isdigit_l: 'isdigit', // no locale support yet
+ isdigit_l: 'isdigit',
isxdigit: function(chr) {
return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
(chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) ||
(chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}});
},
- isxdigit_l: 'isxdigit', // no locale support yet
+ isxdigit_l: 'isxdigit',
isalnum: function(chr) {
return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
(chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
(chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
},
+ isalnum_l: 'isalnum',
ispunct: function(chr) {
return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) ||
(chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) ||
(chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) ||
(chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
},
+ ispunct_l: 'ispunct',
isspace: function(chr) {
return (chr == 32) || (chr >= 9 && chr <= 13);
},
+ isspace_l: 'isspace',
isblank: function(chr) {
return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
},
+ isblank_l: 'isblank',
iscntrl: function(chr) {
return (0 <= chr && chr <= 0x1F) || chr === 0x7F;
},
+ iscntrl_l: 'iscntrl',
isprint: function(chr) {
return 0x1F < chr && chr < 0x7F;
},
+ isprint_l: 'isprint',
isgraph: function(chr) {
return 0x20 < chr && chr < 0x7F;
},
+ isgraph_l: 'isgraph',
// Lookup tables for glibc ctype implementation.
__ctype_b_loc: function() {
// http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html
@@ -8878,7 +8891,7 @@ function autoAddDeps(object, name) {
}
// Add aborting stubs for various libc stuff needed by libc++
-['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'swprintf', 'pthread_join', 'pthread_detach', 'strcoll_l', 'strxfrm_l', 'wcscoll_l', 'toupper_l', 'tolower_l', 'iswspace_l', 'iswprint_l', 'iswcntrl_l', 'iswupper_l', 'iswlower_l', 'iswalpha_l', 'iswdigit_l', 'iswpunct_l', 'iswxdigit_l', 'iswblank_l', 'wcsxfrm_l', 'towupper_l', 'towlower_l', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) {
+['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) {
LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter };
});
diff --git a/src/library_gl.js b/src/library_gl.js
index a4d35aff..83e68777 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -310,6 +310,9 @@ var LibraryGL = {
Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER,
0,
HEAPU8.subarray(cb.ptr, cb.ptr + size));
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(cb.size, cb.type, cb.stride, 0);
+#endif
Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0);
}
},
@@ -331,6 +334,44 @@ var LibraryGL = {
}
}
},
+ // Validates that user obeys GL spec #6.4: http://www.khronos.org/registry/webgl/specs/latest/1.0/#6.4
+ validateVertexAttribPointer: function(dimension, dataType, stride, offset) {
+ var sizeBytes = 1;
+ switch(dataType) {
+ case 0x1400 /* GL_BYTE */:
+ case 0x1401 /* GL_UNSIGNED_BYTE */:
+ sizeBytes = 1;
+ break;
+ case 0x1402 /* GL_SHORT */:
+ case 0x1403 /* GL_UNSIGNED_SHORT */:
+ sizeBytes = 2;
+ break;
+ case 0x1404 /* GL_INT */:
+ case 0x1405 /* GL_UNSIGNED_INT */:
+ case 0x1406 /* GL_FLOAT */:
+ sizeBytes = 4;
+ break;
+ case 0x140A /* GL_DOUBLE */:
+ sizeBytes = 8;
+ break;
+ default:
+ console.error('Invalid vertex attribute data type GLenum ' + dataType + ' passed to GL function!');
+ }
+ if (dimension == 0x80E1 /* GL_BGRA */) {
+ console.error('WebGL does not support size=GL_BGRA in a call to glVertexAttribPointer! Please use size=4 and type=GL_UNSIGNED_BYTE instead!');
+ } else if (dimension < 1 || dimension > 4) {
+ console.error('Invalid dimension='+dimension+' in call to glVertexAttribPointer, must be 1,2,3 or 4.');
+ }
+ if (stride < 0 || stride > 255) {
+ console.error('Invalid stride='+stride+' in call to glVertexAttribPointer. Note that maximum supported stride in WebGL is 255!');
+ }
+ if (offset % sizeBytes != 0) {
+ console.error('GL spec section 6.4 error: vertex attribute data offset of ' + offset + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!');
+ }
+ if (stride % sizeBytes != 0) {
+ console.error('GL spec section 6.4 error: vertex attribute data stride of ' + stride + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!');
+ }
+ },
#endif
initExtensions: function() {
@@ -3273,6 +3314,9 @@ var LibraryGL = {
var clientAttributes = GL.immediate.clientAttributes;
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(positionSize, positionType, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
+#endif
Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, positionType, false,
GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
Module.ctx.enableVertexAttribArray(this.positionLocation);
@@ -3285,6 +3329,9 @@ var LibraryGL = {
if (attribLoc === undefined || attribLoc < 0) continue;
if (texUnitID < textureSizes.length && textureSizes[texUnitID]) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(textureSizes[texUnitID], textureTypes[texUnitID], GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset);
+#endif
Module.ctx.vertexAttribPointer(attribLoc, textureSizes[texUnitID], textureTypes[texUnitID], false,
GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset);
Module.ctx.enableVertexAttribArray(attribLoc);
@@ -3301,6 +3348,9 @@ var LibraryGL = {
}
}
if (colorSize) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(colorSize, colorType, GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset);
+#endif
Module.ctx.vertexAttribPointer(this.colorLocation, colorSize, colorType, true,
GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset);
Module.ctx.enableVertexAttribArray(this.colorLocation);
@@ -3309,6 +3359,9 @@ var LibraryGL = {
Module.ctx.vertexAttrib4fv(this.colorLocation, GL.immediate.clientColor);
}
if (this.hasNormal) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(normalSize, normalType, GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset);
+#endif
Module.ctx.vertexAttribPointer(this.normalLocation, normalSize, normalType, true,
GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset);
Module.ctx.enableVertexAttribArray(this.normalLocation);
@@ -4288,6 +4341,9 @@ var LibraryGL = {
}
cb.clientside = false;
#endif
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(size, type, stride, ptr);
+#endif
Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr);
},
diff --git a/src/parseTools.js b/src/parseTools.js
index 8ce83adf..7d5ca315 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1473,17 +1473,38 @@ var TWO_TWENTY = Math.pow(2, 20);
// Tries to do as much as possible at compile time.
// Leaves overflows etc. unhandled, *except* for integer multiply, in order to be efficient with Math.imul
function getFastValue(a, op, b, type) {
- a = a.toString();
- b = b.toString();
+ //return '(' + a + ')' + op + '(' + b + ')';
a = a == 'true' ? '1' : (a == 'false' ? '0' : a);
b = b == 'true' ? '1' : (b == 'false' ? '0' : b);
- if (isNumber(a) && isNumber(b)) {
- if (op == 'pow') {
- return Math.pow(a, b).toString();
- } else {
- var value = eval(a + op + '(' + b + ')'); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12"
- if (op == '/' && type in Runtime.INT_TYPES) value = value|0; // avoid emitting floats
- return value.toString();
+
+ var aNumber = null, bNumber = null;
+ if (typeof a === 'number') {
+ aNumber = a;
+ a = a.toString();
+ } else if (isNumber(a)) aNumber = parseFloat(a);
+ if (typeof b === 'number') {
+ bNumber = b;
+ b = b.toString();
+ } else if (isNumber(b)) bNumber = parseFloat(b);
+
+ if (aNumber !== null && bNumber !== null) {
+ switch (op) {
+ case '+': return (aNumber + bNumber).toString();
+ case '-': return (aNumber - bNumber).toString();
+ case '*': return (aNumber * bNumber).toString();
+ case '/': {
+ if (type in Runtime.INT_TYPES) {
+ return ((aNumber / bNumber)|0).toString();
+ } else {
+ return (aNumber / bNumber).toString();
+ }
+ }
+ case '%': return (aNumber % bNumber).toString();
+ case '|': return (aNumber | bNumber).toString();
+ case '>>>': return (aNumber >>> bNumber).toString();
+ case '&': return (aNumber & bNumber).toString();
+ case 'pow': return Math.pow(aNumber, bNumber).toString();
+ default: throw 'need to implement getFastValue pn ' + op;
}
}
if (op == 'pow') {
@@ -1492,23 +1513,26 @@ function getFastValue(a, op, b, type) {
}
return 'Math.pow(' + a + ', ' + b + ')';
}
- if (op in PLUS_MUL && isNumber(a)) { // if one of them is a number, keep it last
+ if (op in PLUS_MUL && aNumber !== null) { // if one of them is a number, keep it last
var c = b;
b = a;
a = c;
+ var cNumber = bNumber;
+ bNumber = aNumber;
+ aNumber = cNumber;
}
if (op in MUL_DIV) {
if (op == '*') {
// We can't eliminate where a or b are 0 as that would break things for creating
// a negative 0.
- if ((a == 0 || b == 0) && !(type in Runtime.FLOAT_TYPES)) {
+ if ((aNumber == 0 || bNumber == 0) && !(type in Runtime.FLOAT_TYPES)) {
return '0';
- } else if (a == 1) {
+ } else if (aNumber == 1) {
return b;
- } else if (b == 1) {
+ } else if (bNumber == 1) {
return a;
- } else if (isNumber(b) && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) {
- var shifts = Math.log(parseFloat(b))/Math.LN2;
+ } else if (bNumber !== null && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) {
+ var shifts = Math.log(bNumber)/Math.LN2;
if (shifts % 1 == 0) {
return '(' + a + '<<' + shifts + ')';
}
@@ -1517,7 +1541,7 @@ function getFastValue(a, op, b, type) {
// if guaranteed small enough to not overflow into a double, do a normal multiply
var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes
// Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there
- if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
+ if ((aNumber !== null && Math.abs(a) < TWO_TWENTY) || (bNumber !== null && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) {
return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this
}
return '(Math.imul(' + a + ',' + b + ')|0)';