forked from MozillaSecurity/funfuzz
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest-asm.js
More file actions
169 lines (141 loc) · 5.45 KB
/
test-asm.js
File metadata and controls
169 lines (141 loc) · 5.45 KB
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
/***************************
* TEST ASM.JS CORRECTNESS *
***************************/
// asm.js functions should always have the same semantics as JavaScript functions.
//
// We just need to watch out for:
// * Invalid asm.js
// * Foreign imports of impure functions
// * Mixing int and double heap views (NaN bits)
// * Allowing mutable state to diverge (mutable module globals, heap)
// In those cases, we can test that the asm-compiled version matches the normal js version.
// * isAsmJSFunction(f)
// * !isAsmJSFunction(g)
// Because we pass the 'sanePlease' flag to asmJSInterior,
// * We don't expect any parse errors. (testOneAsmJSInterior currently relies on this.)
// * We expect only the following asm.js type errors:
// * "numeric literal out of representable integer range" (https://github.com/dherman/asm.js/issues/67 makes composition hard)
// * "no duplicate case labels" (because asmSwitchStatement doesn't avoid this)
// * And the following, infrequently, due to out-of-range integer literals:
// * "one arg to int multiply must be a small (-2^20, 2^20) int literal"
// * "arguments to / or % must both be double, signed, or unsigned, unsigned and signed are given"
// * "unsigned is not a subtype of signed or doublish" [Math.abs]
var compareAsm = (function() {
function isSameNumber(a, b)
{
if (!(typeof a == "number" && typeof b == "number"))
return false;
// Differentiate between 0 and -0
if (a === 0 && b === 0)
return 1/a === 1/b;
// Don't differentiate between NaNs
return a === b || (a !== a && b !== b);
}
var asmvals = [
1, Math.PI, 42,
// Special float values
0, -0, 0/0, 1/0, -1/0,
// Boundaries of int, signed, unsigned (near +/- 2^31, +/- 2^32)
0x07fffffff, 0x080000000, 0x080000001,
-0x07fffffff, -0x080000000, -0x080000001,
0x0ffffffff, 0x100000000, 0x100000001,
-0x0ffffffff, -0x100000000, 0x100000001,
// Boundaries of double
Number.MIN_VALUE, -Number.MIN_VALUE,
Number.MAX_VALUE, -Number.MAX_VALUE,
];
var asmvalsLen = asmvals.length;
function compareUnaryFunctions(f, g)
{
for (var i = 0; i < asmvalsLen; ++i) {
var x = asmvals[i];
var fr = f(x);
var gr = g(x);
if (!isSameNumber(fr, gr)) {
foundABug("asm mismatch", "(" + uneval(x) + ") -> " + uneval(fr) + " vs " + uneval(gr));
}
}
}
function compareBinaryFunctions(f, g)
{
for (var i = 0; i < asmvalsLen; ++i) {
var x = asmvals[i];
for (var j = 0; j < asmvalsLen; ++j) {
var y = asmvals[j];
var fr = f(x, y);
var gr = g(x, y);
if (!isSameNumber(fr, gr)) {
foundABug("asm mismatch", "(" + uneval(x) + ", " + uneval(y) + ") -> " + uneval(fr) + " vs " + uneval(gr));
}
}
}
}
return {compareUnaryFunctions: compareUnaryFunctions, compareBinaryFunctions: compareBinaryFunctions};
})();
function nanBitsMayBeVisible(s)
{
// Does the code use more than one of {*int*, float32, or float64} views on the same array buffer?
return (s.indexOf("Uint") != -1 || s.indexOf("Int") != -1) + (s.indexOf("Float32Array") != -1) + (s.indexOf("Float64Array") != -1) > 1;
}
var pureForeign = {
identity: function(x) { return x; },
quadruple: function(x) { return x * 4; },
half: function(x) { return x / 2; },
// Test coercion coming back from FFI.
asString: function(x) { return uneval(x); },
asValueOf: function(x) { return { valueOf: function() { return x; } }; },
// Test register arguments vs stack arguments.
sum: function() { var s = 0; for (var i = 0; i < arguments.length; ++i) s += arguments[i]; return s; },
// Will be replaced by calling makeRegisterStompFunction
stomp: function() { },
};
for (var f in unaryMathFunctions) {
pureForeign["Math_" + unaryMathFunctions[f]] = Math[unaryMathFunctions[f]];
}
for (var f in binaryMathFunctions) {
pureForeign["Math_" + binaryMathFunctions[f]] = Math[binaryMathFunctions[f]];
}
var pureMathNames = Object.keys(pureForeign);
function generateAsmDifferential()
{
var foreignFunctions = rnd(10) ? [] : pureMathNames;
return asmJSInterior(foreignFunctions, true);
}
function testAsmDifferential(stdlib, interior)
{
if (nanBitsMayBeVisible(interior)) {
dumpln("Skipping correctness test for asm module that could expose low bits of NaN");
return;
}
var asmJs = "(function(stdlib, foreign, heap) { 'use asm'; " + interior + " })";
var asmModule = eval(asmJs);
if (isAsmJSModule(asmModule)) {
var asmHeap = new ArrayBuffer(4096);
(new Int32Array(asmHeap))[0] = 0x12345678;
var asmFun = asmModule(stdlib, pureForeign, asmHeap);
var normalHeap = new ArrayBuffer(4096);
(new Int32Array(normalHeap))[0] = 0x12345678;
var normalJs = "(function(stdlib, foreign, heap) { " + interior + " })";
var normalModule = eval(normalJs);
var normalFun = normalModule(stdlib, pureForeign, normalHeap);
compareAsm.compareBinaryFunctions(asmFun, normalFun);
}
}
// Call this instead of start() to run asm-differential tests
function startAsmDifferential()
{
var asmFuzzSeed = Math.floor(Math.random() * Math.pow(2,28));
dumpln("asmFuzzSeed: " + asmFuzzSeed);
Random.init(asmFuzzSeed);
while (true) {
var stompStr = makeRegisterStompFunction(8, [], true);
print(stompStr);
pureForeign.stomp = eval(stompStr);
for (var i = 0; i < 100; ++i) {
var interior = generateAsmDifferential();
print(interior);
testAsmDifferential(this, interior);
}
gc();
}
}