]> git.proxmox.com Git - rustc.git/blob - src/binaryen/src/wasm2asm.h
New upstream version 1.23.0+dfsg1
[rustc.git] / src / binaryen / src / wasm2asm.h
1 /*
2 * Copyright 2015 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //
18 // WebAssembly-to-asm.js translator. Uses the Emscripten optimizer
19 // infrastructure.
20 //
21
22 #ifndef wasm_wasm2asm_h
23 #define wasm_wasm2asm_h
24
25 #include <cmath>
26 #include <numeric>
27
28 #include "asmjs/shared-constants.h"
29 #include "wasm.h"
30 #include "wasm-builder.h"
31 #include "emscripten-optimizer/optimizer.h"
32 #include "mixed_arena.h"
33 #include "asm_v_wasm.h"
34 #include "ir/utils.h"
35 #include "passes/passes.h"
36
37 namespace wasm {
38
39 using namespace cashew;
40
41 IString ASM_FUNC("asmFunc"),
42 ABORT_FUNC("abort"),
43 FUNCTION_TABLE("FUNCTION_TABLE"),
44 NO_RESULT("wasm2asm$noresult"), // no result at all
45 EXPRESSION_RESULT("wasm2asm$expresult"); // result in an expression, no temp var
46
47 // Appends extra to block, flattening out if extra is a block as well
48 void flattenAppend(Ref ast, Ref extra) {
49 int index;
50 if (ast[0] == BLOCK || ast[0] == TOPLEVEL) index = 1;
51 else if (ast[0] == DEFUN) index = 3;
52 else abort();
53 if (extra->isArray() && extra[0] == BLOCK) {
54 for (size_t i = 0; i < extra[1]->size(); i++) {
55 ast[index]->push_back(extra[1][i]);
56 }
57 } else {
58 ast[index]->push_back(extra);
59 }
60 }
61
62 //
63 // Wasm2AsmBuilder - converts a WebAssembly module into asm.js
64 //
65 // In general, asm.js => wasm is very straightforward, as can
66 // be seen in asm2wasm.h. Just a single pass, plus a little
67 // state bookkeeping (breakStack, etc.), and a few after-the
68 // fact corrections for imports, etc. However, wasm => asm.js
69 // is tricky because wasm has statements == expressions, or in
70 // other words, things like `break` and `if` can show up
71 // in places where asm.js can't handle them, like inside an
72 // a loop's condition check.
73 //
74 // We therefore need the ability to lower an expression into
75 // a block of statements, and we keep statementizing until we
76 // reach a context in which we can emit those statments. This
77 // requires that we create temp variables to store values
78 // that would otherwise flow directly into their targets if
79 // we were an expression (e.g. if a loop's condition check
80 // is a bunch of statements, we execute those statements,
81 // then use the computed value in the loop's condition;
82 // we might also be able to avoid an assign to a temp var
83 // at the end of those statements, and put just that
84 // value in the loop's condition).
85 //
86 // It is possible to do this in a single pass, if we just
87 // allocate temp vars freely. However, pathological cases
88 // can easily show bad behavior here, with many unnecessary
89 // temp vars. We could rely on optimization passes like
90 // Emscripten's eliminate/registerize pair, but we want
91 // wasm2asm to be fairly fast to run, as it might run on
92 // the client.
93 //
94 // The approach taken here therefore performs 2 passes on
95 // each function. First, it finds which expression will need to
96 // be statementized. It also sees which labels can receive a break
97 // with a value. Given that information, in the second pass we can
98 // allocate // temp vars in an efficient manner, as we know when we
99 // need them and when their use is finished. They are allocated
100 // using an RAII class, so that they are automatically freed
101 // when the scope ends. This means that a node cannot allocate
102 // its own temp var; instead, the parent - which knows the
103 // child will return a value in a temp var - allocates it,
104 // and tells the child what temp var to emit to. The child
105 // can then pass forward that temp var to its children,
106 // optimizing away unnecessary forwarding.
107
108
109 class Wasm2AsmBuilder {
110 MixedArena allocator;
111
112 public:
113 struct Flags {
114 bool debug = false;
115 bool pedantic = false;
116 };
117
118 Wasm2AsmBuilder(Flags f) : flags(f) {}
119
120 Ref processWasm(Module* wasm);
121 Ref processFunction(Function* func);
122
123 // The first pass on an expression: scan it to see whether it will
124 // need to be statementized, and note spooky returns of values at
125 // a distance (aka break with a value).
126 void scanFunctionBody(Expression* curr);
127
128 // The second pass on an expression: process it fully, generating
129 // asm.js
130 // @param result Whether the context we are in receives a value,
131 // and its type, or if not, then we can drop our return,
132 // if we have one.
133 Ref processFunctionBody(Function* func, IString result);
134
135 Ref processAsserts(Element& e, SExpressionWasmBuilder& sexpBuilder);
136
137 // Get a temp var.
138 IString getTemp(WasmType type, Function* func) {
139 IString ret;
140 if (frees[type].size() > 0) {
141 ret = frees[type].back();
142 frees[type].pop_back();
143 } else {
144 size_t index = temps[type]++;
145 ret = IString((std::string("wasm2asm_") + printWasmType(type) + "$" +
146 std::to_string(index)).c_str(), false);
147 }
148 if (func->localIndices.find(ret) == func->localIndices.end()) {
149 Builder::addVar(func, ret, type);
150 }
151 return ret;
152 }
153
154 // Free a temp var.
155 void freeTemp(WasmType type, IString temp) {
156 frees[type].push_back(temp);
157 }
158
159 static IString fromName(Name name) {
160 // TODO: more clever name fixing, including checking we do not collide
161 const char* str = name.str;
162 // check the various issues, and recurse so we check the others
163 if (strchr(str, '-')) {
164 char* mod = strdup(str);
165 str = mod;
166 while (*mod) {
167 if (*mod == '-') *mod = '_';
168 mod++;
169 }
170 IString result = fromName(IString(str, false));
171 free((void*)str);
172 return result;
173 }
174 if (isdigit(str[0]) || strcmp(str, "if") == 0) {
175 std::string prefixed = "$$";
176 prefixed += name.str;
177 return fromName(IString(prefixed.c_str(), false));
178 }
179 return name;
180 }
181
182 void setStatement(Expression* curr) {
183 willBeStatement.insert(curr);
184 }
185
186 bool isStatement(Expression* curr) {
187 return curr && willBeStatement.find(curr) != willBeStatement.end();
188 }
189
190 size_t getTableSize() {
191 return tableSize;
192 }
193
194 private:
195 Flags flags;
196
197 // How many temp vars we need
198 std::vector<size_t> temps; // type => num temps
199 // Which are currently free to use
200 std::vector<std::vector<IString>> frees; // type => list of free names
201
202 // Expressions that will be a statement.
203 std::set<Expression*> willBeStatement;
204
205 // All our function tables have the same size TODO: optimize?
206 size_t tableSize;
207
208 void addBasics(Ref ast);
209 void addImport(Ref ast, Import* import);
210 void addTables(Ref ast, Module* wasm);
211 void addExports(Ref ast, Module* wasm);
212 void addWasmCompatibilityFuncs(Module* wasm);
213 bool isAssertHandled(Element& e);
214 Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
215 Builder& wasmBuilder,
216 Element& e, Name testFuncName);
217 Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
218 Builder& wasmBuilder,
219 Element& e, Name testFuncName);
220 Wasm2AsmBuilder() = delete;
221 Wasm2AsmBuilder(const Wasm2AsmBuilder &) = delete;
222 Wasm2AsmBuilder &operator=(const Wasm2AsmBuilder&) = delete;
223 };
224
225 static Function* makeCtzFunc(MixedArena& allocator, UnaryOp op) {
226 assert(op == CtzInt32 || op == CtzInt64);
227 Builder b(allocator);
228 // if eqz(x) then 32 else (32 - clz(x ^ (x - 1)))
229 bool is32Bit = (op == CtzInt32);
230 Name funcName = is32Bit ? Name(WASM_CTZ32) : Name(WASM_CTZ64);
231 BinaryOp subOp = is32Bit ? SubInt32 : SubInt64;
232 BinaryOp xorOp = is32Bit ? XorInt32 : XorInt64;
233 UnaryOp clzOp = is32Bit ? ClzInt32 : ClzInt64;
234 UnaryOp eqzOp = is32Bit ? EqZInt32 : EqZInt64;
235 WasmType argType = is32Bit ? i32 : i64;
236 Binary* xorExp = b.makeBinary(
237 xorOp,
238 b.makeGetLocal(0, i32),
239 b.makeBinary(
240 subOp,
241 b.makeGetLocal(0, i32),
242 b.makeConst(is32Bit ? Literal(int32_t(1)) : Literal(int64_t(1)))
243 )
244 );
245 Binary* subExp = b.makeBinary(
246 subOp,
247 b.makeConst(is32Bit ? Literal(int32_t(32 - 1)) : Literal(int64_t(64 - 1))),
248 b.makeUnary(clzOp, xorExp)
249 );
250 If* body = b.makeIf(
251 b.makeUnary(
252 eqzOp,
253 b.makeGetLocal(0, i32)
254 ),
255 b.makeConst(is32Bit ? Literal(int32_t(32)) : Literal(int64_t(64))),
256 subExp
257 );
258 return b.makeFunction(
259 funcName,
260 std::vector<NameType>{NameType("x", argType)},
261 argType,
262 std::vector<NameType>{},
263 body
264 );
265 }
266
267 static Function* makePopcntFunc(MixedArena& allocator, UnaryOp op) {
268 assert(op == PopcntInt32 || op == PopcntInt64);
269 Builder b(allocator);
270 // popcnt implemented as:
271 // int c; for (c = 0; x != 0; c++) { x = x & (x - 1) }; return c
272 bool is32Bit = (op == PopcntInt32);
273 Name funcName = is32Bit ? Name(WASM_POPCNT32) : Name(WASM_POPCNT64);
274 BinaryOp addOp = is32Bit ? AddInt32 : AddInt64;
275 BinaryOp subOp = is32Bit ? SubInt32 : SubInt64;
276 BinaryOp andOp = is32Bit ? AndInt32 : AndInt64;
277 UnaryOp eqzOp = is32Bit ? EqZInt32 : EqZInt64;
278 WasmType argType = is32Bit ? i32 : i64;
279 Name loopName("l");
280 Name blockName("b");
281 Break* brIf = b.makeBreak(
282 blockName,
283 b.makeGetLocal(1, i32),
284 b.makeUnary(
285 eqzOp,
286 b.makeGetLocal(0, argType)
287 )
288 );
289 SetLocal* update = b.makeSetLocal(
290 0,
291 b.makeBinary(
292 andOp,
293 b.makeGetLocal(0, argType),
294 b.makeBinary(
295 subOp,
296 b.makeGetLocal(0, argType),
297 b.makeConst(is32Bit ? Literal(int32_t(1)) : Literal(int64_t(1)))
298 )
299 )
300 );
301 SetLocal* inc = b.makeSetLocal(
302 1,
303 b.makeBinary(
304 addOp,
305 b.makeGetLocal(1, argType),
306 b.makeConst(Literal(1))
307 )
308 );
309 Break* cont = b.makeBreak(loopName);
310 Loop* loop = b.makeLoop(loopName, b.blockify(brIf, update, inc, cont));
311 Block* loopBlock = b.blockifyWithName(loop, blockName);
312 SetLocal* initCount = b.makeSetLocal(1, b.makeConst(Literal(0)));
313 return b.makeFunction(
314 funcName,
315 std::vector<NameType>{NameType("x", argType)},
316 argType,
317 std::vector<NameType>{NameType("count", argType)},
318 b.blockify(initCount, loopBlock)
319 );
320 }
321
322 Function* makeRotFunc(MixedArena& allocator, BinaryOp op) {
323 assert(op == RotLInt32 || op == RotRInt32 ||
324 op == RotLInt64 || op == RotRInt64);
325 Builder b(allocator);
326 // left rotate is:
327 // (((((~0) >>> k) & x) << k) | ((((~0) << (w - k)) & x) >>> (w - k)))
328 // where k is shift modulo w. reverse shifts for right rotate
329 bool is32Bit = (op == RotLInt32 || op == RotRInt32);
330 bool isLRot = (op == RotLInt32 || op == RotLInt64);
331 static Name names[2][2] = {{Name(WASM_ROTR64), Name(WASM_ROTR32)},
332 {Name(WASM_ROTL64), Name(WASM_ROTL32)}};
333 static BinaryOp shifters[2][2] = {{ShrUInt64, ShrUInt32},
334 {ShlInt64, ShlInt32}};
335 Name funcName = names[isLRot][is32Bit];
336 BinaryOp lshift = shifters[isLRot][is32Bit];
337 BinaryOp rshift = shifters[!isLRot][is32Bit];
338 BinaryOp orOp = is32Bit ? OrInt32 : OrInt64;
339 BinaryOp andOp = is32Bit ? AndInt32 : AndInt64;
340 BinaryOp subOp = is32Bit ? SubInt32 : SubInt64;
341 WasmType argType = is32Bit ? i32 : i64;
342 Literal widthMask =
343 is32Bit ? Literal(int32_t(32 - 1)) : Literal(int64_t(64 - 1));
344 Literal width =
345 is32Bit ? Literal(int32_t(32)) : Literal(int64_t(64));
346 auto shiftVal = [&]() {
347 return b.makeBinary(
348 andOp,
349 b.makeGetLocal(1, argType),
350 b.makeConst(widthMask)
351 );
352 };
353 auto widthSub = [&]() {
354 return b.makeBinary(subOp, b.makeConst(width), shiftVal());
355 };
356 auto fullMask = [&]() {
357 return b.makeConst(is32Bit ? Literal(~int32_t(0)) : Literal(~int64_t(0)));
358 };
359 Binary* maskRShift = b.makeBinary(rshift, fullMask(), shiftVal());
360 Binary* lowMask = b.makeBinary(andOp, maskRShift, b.makeGetLocal(0, argType));
361 Binary* lowShift = b.makeBinary(lshift, lowMask, shiftVal());
362 Binary* maskLShift = b.makeBinary(lshift, fullMask(), widthSub());
363 Binary* highMask =
364 b.makeBinary(andOp, maskLShift, b.makeGetLocal(0, argType));
365 Binary* highShift = b.makeBinary(rshift, highMask, widthSub());
366 Binary* body = b.makeBinary(orOp, lowShift, highShift);
367 return b.makeFunction(
368 funcName,
369 std::vector<NameType>{NameType("x", argType),
370 NameType("k", argType)},
371 argType,
372 std::vector<NameType>{},
373 body
374 );
375 }
376
377 void Wasm2AsmBuilder::addWasmCompatibilityFuncs(Module* wasm) {
378 wasm->addFunction(makeCtzFunc(wasm->allocator, CtzInt32));
379 wasm->addFunction(makePopcntFunc(wasm->allocator, PopcntInt32));
380 wasm->addFunction(makeRotFunc(wasm->allocator, RotLInt32));
381 wasm->addFunction(makeRotFunc(wasm->allocator, RotRInt32));
382 }
383
384 Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
385 addWasmCompatibilityFuncs(wasm);
386 PassRunner runner(wasm);
387 runner.add<AutoDrop>();
388 runner.add("i64-to-i32-lowering");
389 runner.add("flatten");
390 runner.add("simplify-locals-notee-nostructure");
391 runner.add("vacuum");
392 runner.setDebug(flags.debug);
393 runner.run();
394 Ref ret = ValueBuilder::makeToplevel();
395 Ref asmFunc = ValueBuilder::makeFunction(ASM_FUNC);
396 ret[1]->push_back(asmFunc);
397 ValueBuilder::appendArgumentToFunction(asmFunc, GLOBAL);
398 ValueBuilder::appendArgumentToFunction(asmFunc, ENV);
399 ValueBuilder::appendArgumentToFunction(asmFunc, BUFFER);
400 asmFunc[3]->push_back(ValueBuilder::makeStatement(ValueBuilder::makeString(USE_ASM)));
401 // create heaps, etc
402 addBasics(asmFunc[3]);
403 for (auto& import : wasm->imports) {
404 addImport(asmFunc[3], import.get());
405 }
406 // figure out the table size
407 tableSize = std::accumulate(wasm->table.segments.begin(),
408 wasm->table.segments.end(),
409 0, [&](size_t size, Table::Segment seg) -> size_t {
410 return size + seg.data.size();
411 });
412 size_t pow2ed = 1;
413 while (pow2ed < tableSize) {
414 pow2ed <<= 1;
415 }
416 tableSize = pow2ed;
417 // functions
418 for (auto& func : wasm->functions) {
419 asmFunc[3]->push_back(processFunction(func.get()));
420 }
421 addTables(asmFunc[3], wasm);
422 // memory XXX
423 addExports(asmFunc[3], wasm);
424 return ret;
425 }
426
427 void Wasm2AsmBuilder::addBasics(Ref ast) {
428 // heaps, var HEAP8 = new global.Int8Array(buffer); etc
429 auto addHeap = [&](IString name, IString view) {
430 Ref theVar = ValueBuilder::makeVar();
431 ast->push_back(theVar);
432 ValueBuilder::appendToVar(theVar,
433 name,
434 ValueBuilder::makeNew(
435 ValueBuilder::makeCall(
436 ValueBuilder::makeDot(
437 ValueBuilder::makeName(GLOBAL),
438 view
439 ),
440 ValueBuilder::makeName(BUFFER)
441 )
442 )
443 );
444 };
445 addHeap(HEAP8, INT8ARRAY);
446 addHeap(HEAP16, INT16ARRAY);
447 addHeap(HEAP32, INT32ARRAY);
448 addHeap(HEAPU8, UINT8ARRAY);
449 addHeap(HEAPU16, UINT16ARRAY);
450 addHeap(HEAPU32, UINT32ARRAY);
451 addHeap(HEAPF32, FLOAT32ARRAY);
452 addHeap(HEAPF64, FLOAT64ARRAY);
453 // core asm.js imports
454 auto addMath = [&](IString name, IString base) {
455 Ref theVar = ValueBuilder::makeVar();
456 ast->push_back(theVar);
457 ValueBuilder::appendToVar(theVar,
458 name,
459 ValueBuilder::makeDot(
460 ValueBuilder::makeDot(
461 ValueBuilder::makeName(GLOBAL),
462 MATH
463 ),
464 base
465 )
466 );
467 };
468 addMath(MATH_IMUL, IMUL);
469 addMath(MATH_FROUND, FROUND);
470 addMath(MATH_ABS, ABS);
471 addMath(MATH_CLZ32, CLZ32);
472 }
473
474 void Wasm2AsmBuilder::addImport(Ref ast, Import* import) {
475 Ref theVar = ValueBuilder::makeVar();
476 ast->push_back(theVar);
477 Ref module = ValueBuilder::makeName(ENV); // TODO: handle nested module imports
478 ValueBuilder::appendToVar(theVar,
479 fromName(import->name),
480 ValueBuilder::makeDot(
481 module,
482 fromName(import->base)
483 )
484 );
485 }
486
487 void Wasm2AsmBuilder::addTables(Ref ast, Module* wasm) {
488 std::map<std::string, std::vector<IString>> tables; // asm.js tables, sig => contents of table
489 for (Table::Segment& seg : wasm->table.segments) {
490 for (size_t i = 0; i < seg.data.size(); i++) {
491 Name name = seg.data[i];
492 auto func = wasm->getFunction(name);
493 std::string sig = getSig(func);
494 auto& table = tables[sig];
495 if (table.size() == 0) {
496 // fill it with the first of its type seen. we have to fill with something; and for asm2wasm output, the first is the null anyhow
497 table.resize(tableSize);
498 for (size_t j = 0; j < tableSize; j++) {
499 table[j] = fromName(name);
500 }
501 } else {
502 table[i] = fromName(name);
503 }
504 }
505 }
506 for (auto& pair : tables) {
507 auto& sig = pair.first;
508 auto& table = pair.second;
509 std::string stable = std::string("FUNCTION_TABLE_") + sig;
510 IString asmName = IString(stable.c_str(), false);
511 // add to asm module
512 Ref theVar = ValueBuilder::makeVar();
513 ast->push_back(theVar);
514 Ref theArray = ValueBuilder::makeArray();
515 ValueBuilder::appendToVar(theVar, asmName, theArray);
516 for (auto& name : table) {
517 ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name));
518 }
519 }
520 }
521
522 void Wasm2AsmBuilder::addExports(Ref ast, Module* wasm) {
523 Ref exports = ValueBuilder::makeObject();
524 for (auto& export_ : wasm->exports) {
525 ValueBuilder::appendToObject(
526 exports,
527 fromName(export_->name),
528 ValueBuilder::makeName(fromName(export_->value))
529 );
530 }
531 ast->push_back(ValueBuilder::makeStatement(ValueBuilder::makeReturn(exports)));
532 }
533
534 Ref Wasm2AsmBuilder::processFunction(Function* func) {
535 if (flags.debug) {
536 static int fns = 0;
537 std::cerr << "processFunction " << (fns++) << " " << func->name
538 << std::endl;
539 }
540 Ref ret = ValueBuilder::makeFunction(fromName(func->name));
541 frees.clear();
542 frees.resize(std::max(i32, std::max(f32, f64)) + 1);
543 temps.clear();
544 temps.resize(std::max(i32, std::max(f32, f64)) + 1);
545 temps[i32] = temps[f32] = temps[f64] = 0;
546 // arguments
547 for (Index i = 0; i < func->getNumParams(); i++) {
548 IString name = fromName(func->getLocalNameOrGeneric(i));
549 ValueBuilder::appendArgumentToFunction(ret, name);
550 ret[3]->push_back(
551 ValueBuilder::makeStatement(
552 ValueBuilder::makeBinary(
553 ValueBuilder::makeName(name), SET,
554 makeAsmCoercion(
555 ValueBuilder::makeName(name),
556 wasmToAsmType(func->getLocalType(i))
557 )
558 )
559 )
560 );
561 }
562 Ref theVar = ValueBuilder::makeVar();
563 size_t theVarIndex = ret[3]->size();
564 ret[3]->push_back(theVar);
565 // body
566 auto appendFinalReturn = [&] (Ref retVal) {
567 flattenAppend(
568 ret,
569 ValueBuilder::makeReturn(
570 makeAsmCoercion(retVal, wasmToAsmType(func->result))
571 )
572 );
573 };
574 scanFunctionBody(func->body);
575 bool isBodyBlock = (func->body->_id == Expression::BlockId);
576 ExpressionList* stats = isBodyBlock ?
577 &static_cast<Block*>(func->body)->list : nullptr;
578 bool endsInReturn =
579 (isBodyBlock && ((*stats)[stats->size()-1]->_id == Expression::ReturnId));
580 if (endsInReturn) {
581 // return already taken care of
582 flattenAppend(ret, processFunctionBody(func, NO_RESULT));
583 } else if (isStatement(func->body)) {
584 // store result in variable then return it
585 IString result =
586 func->result != none ? getTemp(func->result, func) : NO_RESULT;
587 flattenAppend(ret, processFunctionBody(func, result));
588 if (func->result != none) {
589 appendFinalReturn(ValueBuilder::makeName(result));
590 freeTemp(func->result, result);
591 }
592 } else if (func->result != none) {
593 // whole thing is an expression, just return body
594 appendFinalReturn(processFunctionBody(func, EXPRESSION_RESULT));
595 } else {
596 // func has no return
597 flattenAppend(ret, processFunctionBody(func, NO_RESULT));
598 }
599 // vars, including new temp vars
600 for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
601 ValueBuilder::appendToVar(
602 theVar,
603 fromName(func->getLocalNameOrGeneric(i)),
604 makeAsmCoercedZero(wasmToAsmType(func->getLocalType(i)))
605 );
606 }
607 if (theVar[1]->size() == 0) {
608 ret[3]->splice(theVarIndex, 1);
609 }
610 // checks
611 assert(frees[i32].size() == temps[i32]); // all temp vars should be free at the end
612 assert(frees[f32].size() == temps[f32]); // all temp vars should be free at the end
613 assert(frees[f64].size() == temps[f64]); // all temp vars should be free at the end
614 // cleanups
615 willBeStatement.clear();
616 return ret;
617 }
618
619 void Wasm2AsmBuilder::scanFunctionBody(Expression* curr) {
620 struct ExpressionScanner : public PostWalker<ExpressionScanner> {
621 Wasm2AsmBuilder* parent;
622
623 ExpressionScanner(Wasm2AsmBuilder* parent) : parent(parent) {}
624
625 // Visitors
626
627 void visitBlock(Block* curr) {
628 parent->setStatement(curr);
629 }
630 void visitIf(If* curr) {
631 parent->setStatement(curr);
632 }
633 void visitLoop(Loop* curr) {
634 parent->setStatement(curr);
635 }
636 void visitBreak(Break* curr) {
637 parent->setStatement(curr);
638 }
639 void visitSwitch(Switch* curr) {
640 parent->setStatement(curr);
641 }
642 void visitCall(Call* curr) {
643 for (auto item : curr->operands) {
644 if (parent->isStatement(item)) {
645 parent->setStatement(curr);
646 break;
647 }
648 }
649 }
650 void visitCallImport(CallImport* curr) {
651 for (auto item : curr->operands) {
652 if (parent->isStatement(item)) {
653 parent->setStatement(curr);
654 break;
655 }
656 }
657 }
658 void visitCallIndirect(CallIndirect* curr) {
659 if (parent->isStatement(curr->target)) {
660 parent->setStatement(curr);
661 return;
662 }
663 for (auto item : curr->operands) {
664 if (parent->isStatement(item)) {
665 parent->setStatement(curr);
666 break;
667 }
668 }
669 }
670 void visitSetLocal(SetLocal* curr) {
671 if (parent->isStatement(curr->value)) {
672 parent->setStatement(curr);
673 }
674 }
675 void visitLoad(Load* curr) {
676 if (parent->isStatement(curr->ptr)) {
677 parent->setStatement(curr);
678 }
679 }
680 void visitStore(Store* curr) {
681 if (parent->isStatement(curr->ptr) || parent->isStatement(curr->value)) {
682 parent->setStatement(curr);
683 }
684 }
685 void visitUnary(Unary* curr) {
686 if (parent->isStatement(curr->value)) {
687 parent->setStatement(curr);
688 }
689 }
690 void visitBinary(Binary* curr) {
691 if (parent->isStatement(curr->left) || parent->isStatement(curr->right)) {
692 parent->setStatement(curr);
693 }
694 }
695 void visitSelect(Select* curr) {
696 if (parent->isStatement(curr->ifTrue) || parent->isStatement(curr->ifFalse) || parent->isStatement(curr->condition)) {
697 parent->setStatement(curr);
698 }
699 }
700 void visitReturn(Return* curr) {
701 parent->setStatement(curr);
702 }
703 void visitHost(Host* curr) {
704 for (auto item : curr->operands) {
705 if (parent->isStatement(item)) {
706 parent->setStatement(curr);
707 break;
708 }
709 }
710 }
711 };
712 ExpressionScanner(this).walk(curr);
713 }
714
715 Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
716 struct ExpressionProcessor : public Visitor<ExpressionProcessor, Ref> {
717 Wasm2AsmBuilder* parent;
718 IString result;
719 Function* func;
720 MixedArena allocator;
721 ExpressionProcessor(Wasm2AsmBuilder* parent, Function* func) : parent(parent), func(func) {}
722
723 // A scoped temporary variable.
724 struct ScopedTemp {
725 Wasm2AsmBuilder* parent;
726 WasmType type;
727 IString temp;
728 bool needFree;
729 // @param possible if provided, this is a variable we can use as our temp. it has already been
730 // allocated in a higher scope, and we can just assign to it as our result is
731 // going there anyhow.
732 ScopedTemp(WasmType type, Wasm2AsmBuilder* parent, Function* func,
733 IString possible = NO_RESULT) : parent(parent), type(type) {
734 assert(possible != EXPRESSION_RESULT);
735 if (possible == NO_RESULT) {
736 temp = parent->getTemp(type, func);
737 needFree = true;
738 } else {
739 temp = possible;
740 needFree = false;
741 }
742 }
743 ~ScopedTemp() {
744 if (needFree) {
745 parent->freeTemp(type, temp);
746 }
747 }
748
749 IString getName() {
750 return temp;
751 }
752 Ref getAstName() {
753 return ValueBuilder::makeName(temp);
754 }
755 };
756
757 Ref visit(Expression* curr, IString nextResult) {
758 IString old = result;
759 result = nextResult;
760 Ref ret = Visitor::visit(curr);
761 result = old; // keep it consistent for the rest of this frame, which may call visit on multiple children
762 return ret;
763 }
764
765 Ref visit(Expression* curr, ScopedTemp& temp) {
766 return visit(curr, temp.temp);
767 }
768
769 // this result is for an asm expression slot, but it might be a statement
770 Ref visitForExpression(Expression* curr, WasmType type, IString& tempName) {
771 if (isStatement(curr)) {
772 ScopedTemp temp(type, parent, func);
773 tempName = temp.temp;
774 return visit(curr, temp);
775 } else {
776 return visit(curr, EXPRESSION_RESULT);
777 }
778 }
779
780 Ref visitAndAssign(Expression* curr, IString result) {
781 Ref ret = visit(curr, result);
782 // if it's not already a statement, then it's an expression, and we need to assign it
783 // (if it is a statement, it already assigns to the result var)
784 if (!isStatement(curr) && result != NO_RESULT) {
785 ret = ValueBuilder::makeStatement(
786 ValueBuilder::makeBinary(ValueBuilder::makeName(result), SET, ret));
787 }
788 return ret;
789 }
790
791 Ref visitAndAssign(Expression* curr, ScopedTemp& temp) {
792 return visitAndAssign(curr, temp.getName());
793 }
794
795 bool isStatement(Expression* curr) {
796 return parent->isStatement(curr);
797 }
798
799 // Expressions with control flow turn into a block, which we must
800 // then handle, even if we are an expression.
801 bool isBlock(Ref ast) {
802 return !!ast && ast->isArray() && ast[0] == BLOCK;
803 }
804
805 Ref blockify(Ref ast) {
806 if (isBlock(ast)) return ast;
807 Ref ret = ValueBuilder::makeBlock();
808 ret[1]->push_back(ValueBuilder::makeStatement(ast));
809 return ret;
810 }
811
812 // For spooky return-at-a-distance/break-with-result, this tells us
813 // what the result var is for a specific label.
814 std::map<Name, IString> breakResults;
815
816 // Breaks to the top of a loop should be emitted as continues, to that loop's main label
817 std::unordered_set<Name> continueLabels;
818
819 IString fromName(Name name) {
820 return parent->fromName(name);
821 }
822
823 // Visitors
824
825 Ref visitBlock(Block* curr) {
826 breakResults[curr->name] = result;
827 Ref ret = ValueBuilder::makeBlock();
828 size_t size = curr->list.size();
829 auto noResults = result == NO_RESULT ? size : size-1;
830 for (size_t i = 0; i < noResults; i++) {
831 flattenAppend(ret, ValueBuilder::makeStatement(visit(curr->list[i], NO_RESULT)));
832 }
833 if (result != NO_RESULT) {
834 flattenAppend(ret, visitAndAssign(curr->list[size-1], result));
835 }
836 if (curr->name.is()) {
837 ret = ValueBuilder::makeLabel(fromName(curr->name), ret);
838 }
839 return ret;
840 }
841
842 Ref visitIf(If* curr) {
843 IString temp;
844 Ref condition = visitForExpression(curr->condition, i32, temp);
845 Ref ifTrue = ValueBuilder::makeStatement(visitAndAssign(curr->ifTrue, result));
846 Ref ifFalse;
847 if (curr->ifFalse) {
848 ifFalse = ValueBuilder::makeStatement(visitAndAssign(curr->ifFalse, result));
849 }
850 if (temp.isNull()) {
851 return ValueBuilder::makeIf(condition, ifTrue, ifFalse); // simple if
852 }
853 condition = blockify(condition);
854 // just add an if to the block
855 condition[1]->push_back(ValueBuilder::makeIf(ValueBuilder::makeName(temp), ifTrue, ifFalse));
856 return condition;
857 }
858
859 Ref visitLoop(Loop* curr) {
860 Name asmLabel = curr->name;
861 continueLabels.insert(asmLabel);
862 Ref body = blockify(visit(curr->body, result));
863 flattenAppend(body, ValueBuilder::makeBreak(fromName(asmLabel)));
864 Ref ret = ValueBuilder::makeDo(body, ValueBuilder::makeInt(1));
865 return ValueBuilder::makeLabel(fromName(asmLabel), ret);
866 }
867
868 Ref visitBreak(Break* curr) {
869 if (curr->condition) {
870 // we need an equivalent to an if here, so use that code
871 Break fakeBreak = *curr;
872 fakeBreak.condition = nullptr;
873 If fakeIf(allocator);
874 fakeIf.condition = curr->condition;
875 fakeIf.ifTrue = &fakeBreak;
876 return visit(&fakeIf, result);
877 }
878 Ref theBreak;
879 auto iter = continueLabels.find(curr->name);
880 if (iter == continueLabels.end()) {
881 theBreak = ValueBuilder::makeBreak(fromName(curr->name));
882 } else {
883 theBreak = ValueBuilder::makeContinue(fromName(curr->name));
884 }
885 if (!curr->value) return theBreak;
886 // generate the value, including assigning to the result, and then do the break
887 Ref ret = visitAndAssign(curr->value, breakResults[curr->name]);
888 ret = blockify(ret);
889 ret[1]->push_back(theBreak);
890 return ret;
891 }
892
893 Expression* defaultBody = nullptr; // default must be last in asm.js
894
895 Ref visitSwitch(Switch* curr) {
896 assert(!curr->value);
897 Ref ret = ValueBuilder::makeBlock();
898 Ref condition;
899 if (isStatement(curr->condition)) {
900 ScopedTemp temp(i32, parent, func);
901 flattenAppend(ret[2], visit(curr->condition, temp));
902 condition = temp.getAstName();
903 } else {
904 condition = visit(curr->condition, EXPRESSION_RESULT);
905 }
906 Ref theSwitch =
907 ValueBuilder::makeSwitch(makeAsmCoercion(condition, ASM_INT));
908 ret[1]->push_back(theSwitch);
909 for (size_t i = 0; i < curr->targets.size(); i++) {
910 ValueBuilder::appendCaseToSwitch(theSwitch, ValueBuilder::makeNum(i));
911 ValueBuilder::appendCodeToSwitch(theSwitch, blockify(ValueBuilder::makeBreak(fromName(curr->targets[i]))), false);
912 }
913 ValueBuilder::appendDefaultToSwitch(theSwitch);
914 ValueBuilder::appendCodeToSwitch(theSwitch, blockify(ValueBuilder::makeBreak(fromName(curr->default_))), false);
915 return ret;
916 }
917
918 Ref makeStatementizedCall(ExpressionList& operands, Ref ret, Ref theCall, IString result, WasmType type) {
919 std::vector<ScopedTemp*> temps; // TODO: utility class, with destructor?
920 for (auto& operand : operands) {
921 temps.push_back(new ScopedTemp(operand->type, parent, func));
922 IString temp = temps.back()->temp;
923 flattenAppend(ret, visitAndAssign(operand, temp));
924 theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type)));
925 }
926 theCall = makeAsmCoercion(theCall, wasmToAsmType(type));
927 if (result != NO_RESULT) {
928 theCall = ValueBuilder::makeStatement(
929 ValueBuilder::makeBinary(
930 ValueBuilder::makeName(result), SET, theCall));
931 }
932 flattenAppend(ret, theCall);
933 for (auto temp : temps) {
934 delete temp;
935 }
936 return ret;
937 }
938
939 Ref visitGenericCall(Expression* curr, Name target,
940 ExpressionList& operands) {
941 Ref theCall = ValueBuilder::makeCall(fromName(target));
942 if (!isStatement(curr)) {
943 // none of our operands is a statement; go right ahead and create a
944 // simple expression
945 for (auto operand : operands) {
946 theCall[2]->push_back(
947 makeAsmCoercion(visit(operand, EXPRESSION_RESULT),
948 wasmToAsmType(operand->type)));
949 }
950 return makeAsmCoercion(theCall, wasmToAsmType(curr->type));
951 }
952 // we must statementize them all
953 return makeStatementizedCall(operands, ValueBuilder::makeBlock(), theCall,
954 result, curr->type);
955 }
956
957 Ref visitCall(Call* curr) {
958 return visitGenericCall(curr, curr->target, curr->operands);
959 }
960
961 Ref visitCallImport(CallImport* curr) {
962 return visitGenericCall(curr, curr->target, curr->operands);
963 }
964
965 Ref visitCallIndirect(CallIndirect* curr) {
966 std::string stable = std::string("FUNCTION_TABLE_") + curr->fullType.c_str();
967 IString table = IString(stable.c_str(), false);
968 auto makeTableCall = [&](Ref target) {
969 return ValueBuilder::makeCall(ValueBuilder::makeSub(
970 ValueBuilder::makeName(table),
971 ValueBuilder::makeBinary(target, AND, ValueBuilder::makeInt(parent->getTableSize()-1))
972 ));
973 };
974 if (!isStatement(curr)) {
975 // none of our operands is a statement; go right ahead and create a simple expression
976 Ref theCall = makeTableCall(visit(curr->target, EXPRESSION_RESULT));
977 for (auto operand : curr->operands) {
978 theCall[2]->push_back(makeAsmCoercion(visit(operand, EXPRESSION_RESULT), wasmToAsmType(operand->type)));
979 }
980 return makeAsmCoercion(theCall, wasmToAsmType(curr->type));
981 }
982 // we must statementize them all
983 Ref ret = ValueBuilder::makeBlock();
984 ScopedTemp temp(i32, parent, func);
985 flattenAppend(ret, visit(curr->target, temp));
986 Ref theCall = makeTableCall(temp.getAstName());
987 return makeStatementizedCall(curr->operands, ret, theCall, result, curr->type);
988 }
989
990 Ref makeSetVar(Expression* curr, Expression* value, Name name) {
991 if (!isStatement(curr)) {
992 return ValueBuilder::makeBinary(
993 ValueBuilder::makeName(fromName(name)), SET,
994 visit(value, EXPRESSION_RESULT)
995 );
996 }
997 // if result was provided, our child can just assign there.
998 // Otherwise, allocate a temp for it to assign to.
999 ScopedTemp temp(value->type, parent, func, result);
1000 Ref ret = blockify(visit(value, temp));
1001 // the output was assigned to result, so we can just assign it to our target
1002 ret[1]->push_back(
1003 ValueBuilder::makeStatement(
1004 ValueBuilder::makeBinary(
1005 ValueBuilder::makeName(fromName(name)), SET,
1006 temp.getAstName()
1007 )
1008 )
1009 );
1010 return ret;
1011 }
1012
1013 Ref visitGetLocal(GetLocal* curr) {
1014 return ValueBuilder::makeName(
1015 fromName(func->getLocalNameOrGeneric(curr->index))
1016 );
1017 }
1018
1019 Ref visitSetLocal(SetLocal* curr) {
1020 return makeSetVar(curr, curr->value, func->getLocalNameOrGeneric(curr->index));
1021 }
1022
1023 Ref visitGetGlobal(GetGlobal* curr) {
1024 return ValueBuilder::makeName(fromName(curr->name));
1025 }
1026
1027 Ref visitSetGlobal(SetGlobal* curr) {
1028 return makeSetVar(curr, curr->value, curr->name);
1029 }
1030
1031 Ref visitLoad(Load* curr) {
1032 if (isStatement(curr)) {
1033 ScopedTemp temp(i32, parent, func);
1034 GetLocal fakeLocal(allocator);
1035 fakeLocal.index = func->getLocalIndex(temp.getName());
1036 Load fakeLoad = *curr;
1037 fakeLoad.ptr = &fakeLocal;
1038 Ref ret = blockify(visitAndAssign(curr->ptr, temp));
1039 flattenAppend(ret, visitAndAssign(&fakeLoad, result));
1040 return ret;
1041 }
1042 if (curr->align != 0 && curr->align < curr->bytes) {
1043 // set the pointer to a local
1044 ScopedTemp temp(i32, parent, func);
1045 SetLocal set(allocator);
1046 set.index = func->getLocalIndex(temp.getName());
1047 set.value = curr->ptr;
1048 Ref ptrSet = visit(&set, NO_RESULT);
1049 GetLocal get(allocator);
1050 get.index = func->getLocalIndex(temp.getName());
1051 // fake loads
1052 Load load = *curr;
1053 load.ptr = &get;
1054 load.bytes = 1; // do the worst
1055 Ref rest;
1056 switch (curr->type) {
1057 case i32: {
1058 rest = makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT);
1059 for (size_t i = 1; i < curr->bytes; i++) {
1060 ++load.offset;
1061 Ref add = makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT);
1062 add = ValueBuilder::makeBinary(add, LSHIFT, ValueBuilder::makeNum(8*i));
1063 rest = ValueBuilder::makeBinary(rest, OR, add);
1064 }
1065 break;
1066 }
1067 default: {
1068 std::cerr << "Unhandled type in load: " << curr->type << std::endl;
1069 abort();
1070 }
1071 }
1072 return ValueBuilder::makeSeq(ptrSet, rest);
1073 }
1074 // normal load
1075 Ref ptr = visit(curr->ptr, EXPRESSION_RESULT);
1076 if (curr->offset) {
1077 ptr = makeAsmCoercion(
1078 ValueBuilder::makeBinary(ptr, PLUS, ValueBuilder::makeNum(curr->offset)),
1079 ASM_INT);
1080 }
1081 Ref ret;
1082 switch (curr->type) {
1083 case i32: {
1084 switch (curr->bytes) {
1085 case 1:
1086 ret = ValueBuilder::makeSub(
1087 ValueBuilder::makeName(curr->signed_ ? HEAP8 : HEAPU8 ),
1088 ValueBuilder::makePtrShift(ptr, 0));
1089 break;
1090 case 2:
1091 ret = ValueBuilder::makeSub(
1092 ValueBuilder::makeName(curr->signed_ ? HEAP16 : HEAPU16),
1093 ValueBuilder::makePtrShift(ptr, 1));
1094 break;
1095 case 4:
1096 ret = ValueBuilder::makeSub(
1097 ValueBuilder::makeName(curr->signed_ ? HEAP32 : HEAPU32),
1098 ValueBuilder::makePtrShift(ptr, 2));
1099 break;
1100 default: {
1101 std::cerr << "Unhandled number of bytes in i32 load: "
1102 << curr->bytes << std::endl;
1103 abort();
1104 }
1105 }
1106 break;
1107 }
1108 case f32:
1109 ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32),
1110 ValueBuilder::makePtrShift(ptr, 2));
1111 break;
1112 case f64:
1113 ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64),
1114 ValueBuilder::makePtrShift(ptr, 3));
1115 break;
1116 default: {
1117 std::cerr << "Unhandled type in load: " << curr->type << std::endl;
1118 abort();
1119 }
1120 }
1121 return makeAsmCoercion(ret, wasmToAsmType(curr->type));
1122 }
1123
1124 Ref visitStore(Store* curr) {
1125 if (isStatement(curr)) {
1126 ScopedTemp tempPtr(i32, parent, func);
1127 ScopedTemp tempValue(curr->valueType, parent, func);
1128 GetLocal fakeLocalPtr(allocator);
1129 fakeLocalPtr.index = func->getLocalIndex(tempPtr.getName());
1130 GetLocal fakeLocalValue(allocator);
1131 fakeLocalValue.index = func->getLocalIndex(tempValue.getName());
1132 Store fakeStore = *curr;
1133 fakeStore.ptr = &fakeLocalPtr;
1134 fakeStore.value = &fakeLocalValue;
1135 Ref ret = blockify(visitAndAssign(curr->ptr, tempPtr));
1136 flattenAppend(ret, visitAndAssign(curr->value, tempValue));
1137 flattenAppend(ret, visitAndAssign(&fakeStore, result));
1138 return ret;
1139 }
1140 if (curr->align != 0 && curr->align < curr->bytes) {
1141 // set the pointer to a local
1142 ScopedTemp temp(i32, parent, func);
1143 SetLocal set(allocator);
1144 set.index = func->getLocalIndex(temp.getName());
1145 set.value = curr->ptr;
1146 Ref ptrSet = visit(&set, NO_RESULT);
1147 GetLocal get(allocator);
1148 get.index = func->getLocalIndex(temp.getName());
1149 // set the value to a local
1150 ScopedTemp tempValue(curr->value->type, parent, func);
1151 SetLocal setValue(allocator);
1152 setValue.index = func->getLocalIndex(tempValue.getName());
1153 setValue.value = curr->value;
1154 Ref valueSet = visit(&setValue, NO_RESULT);
1155 GetLocal getValue(allocator);
1156 getValue.index = func->getLocalIndex(tempValue.getName());
1157 // fake stores
1158 Store store = *curr;
1159 store.ptr = &get;
1160 store.bytes = 1; // do the worst
1161 Ref rest;
1162 switch (curr->valueType) {
1163 case i32: {
1164 Const _255(allocator);
1165 _255.value = Literal(int32_t(255));
1166 _255.type = i32;
1167 for (size_t i = 0; i < curr->bytes; i++) {
1168 Const shift(allocator);
1169 shift.value = Literal(int32_t(8*i));
1170 shift.type = i32;
1171 Binary shifted(allocator);
1172 shifted.op = ShrUInt32;
1173 shifted.left = &getValue;
1174 shifted.right = &shift;
1175 shifted.type = i32;
1176 Binary anded(allocator);
1177 anded.op = AndInt32;
1178 anded.left = i > 0 ? static_cast<Expression*>(&shifted) : static_cast<Expression*>(&getValue);
1179 anded.right = &_255;
1180 anded.type = i32;
1181 store.value = &anded;
1182 Ref part = visit(&store, NO_RESULT);
1183 if (i == 0) {
1184 rest = part;
1185 } else {
1186 rest = ValueBuilder::makeSeq(rest, part);
1187 }
1188 ++store.offset;
1189 }
1190 break;
1191 }
1192 default: {
1193 std::cerr << "Unhandled type in store: " << curr->valueType
1194 << std::endl;
1195 abort();
1196 }
1197 }
1198 return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), rest);
1199 }
1200 // normal store
1201 Ref ptr = visit(curr->ptr, EXPRESSION_RESULT);
1202 if (curr->offset) {
1203 ptr = makeAsmCoercion(ValueBuilder::makeBinary(ptr, PLUS, ValueBuilder::makeNum(curr->offset)), ASM_INT);
1204 }
1205 Ref value = visit(curr->value, EXPRESSION_RESULT);
1206 Ref ret;
1207 switch (curr->valueType) {
1208 case i32: {
1209 switch (curr->bytes) {
1210 case 1: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP8), ValueBuilder::makePtrShift(ptr, 0)); break;
1211 case 2: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP16), ValueBuilder::makePtrShift(ptr, 1)); break;
1212 case 4: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), ValueBuilder::makePtrShift(ptr, 2)); break;
1213 default: abort();
1214 }
1215 break;
1216 }
1217 case f32: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), ValueBuilder::makePtrShift(ptr, 2)); break;
1218 case f64: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), ValueBuilder::makePtrShift(ptr, 3)); break;
1219 default: {
1220 std::cerr << "Unhandled type in store: " << curr->valueType
1221 << std::endl;
1222 abort();
1223 }
1224 }
1225 return ValueBuilder::makeBinary(ret, SET, value);
1226 }
1227
1228 Ref visitDrop(Drop* curr) {
1229 assert(!isStatement(curr));
1230 return visitAndAssign(curr->value, result);
1231 }
1232
1233 Ref visitConst(Const* curr) {
1234 switch (curr->type) {
1235 case i32: return ValueBuilder::makeInt(curr->value.geti32());
1236 case f32: {
1237 Ref ret = ValueBuilder::makeCall(MATH_FROUND);
1238 Const fake(allocator);
1239 fake.value = Literal(double(curr->value.getf32()));
1240 fake.type = f64;
1241 ret[2]->push_back(visitConst(&fake));
1242 return ret;
1243 }
1244 case f64: {
1245 double d = curr->value.getf64();
1246 if (d == 0 && std::signbit(d)) { // negative zero
1247 return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeUnary(MINUS, ValueBuilder::makeDouble(0)));
1248 }
1249 return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeDouble(curr->value.getf64()));
1250 }
1251 default: abort();
1252 }
1253 }
1254
1255 Ref visitUnary(Unary* curr) {
1256 if (isStatement(curr)) {
1257 ScopedTemp temp(curr->value->type, parent, func);
1258 GetLocal fakeLocal(allocator);
1259 fakeLocal.index = func->getLocalIndex(temp.getName());
1260 Unary fakeUnary = *curr;
1261 fakeUnary.value = &fakeLocal;
1262 Ref ret = blockify(visitAndAssign(curr->value, temp));
1263 flattenAppend(ret, visitAndAssign(&fakeUnary, result));
1264 return ret;
1265 }
1266 // normal unary
1267 switch (curr->type) {
1268 case i32: {
1269 switch (curr->op) {
1270 case ClzInt32:
1271 return ValueBuilder::makeCall(
1272 MATH_CLZ32,
1273 visit(curr->value, EXPRESSION_RESULT)
1274 );
1275 case CtzInt32:
1276 return makeSigning(
1277 ValueBuilder::makeCall(
1278 WASM_CTZ32,
1279 visit(curr->value, EXPRESSION_RESULT)
1280 ),
1281 ASM_SIGNED
1282 );
1283 case PopcntInt32:
1284 return makeSigning(
1285 ValueBuilder::makeCall(
1286 WASM_POPCNT32,
1287 visit(curr->value, EXPRESSION_RESULT)
1288 ),
1289 ASM_SIGNED
1290 );
1291 case EqZInt32:
1292 return ValueBuilder::makeBinary(
1293 makeAsmCoercion(visit(curr->value,
1294 EXPRESSION_RESULT), ASM_INT), EQ,
1295 makeAsmCoercion(ValueBuilder::makeInt(0), ASM_INT));
1296 default: {
1297 std::cerr << "Unhandled unary i32 operator: " << curr
1298 << std::endl;
1299 abort();
1300 }
1301 }
1302 }
1303 case f32:
1304 case f64: {
1305 Ref ret;
1306 switch (curr->op) {
1307 case NegFloat32:
1308 case NegFloat64:
1309 ret = ValueBuilder::makeUnary(
1310 MINUS,
1311 visit(curr->value, EXPRESSION_RESULT)
1312 );
1313 break;
1314 case AbsFloat32:
1315 case AbsFloat64:
1316 ret = ValueBuilder::makeCall(
1317 MATH_ABS,
1318 visit(curr->value, EXPRESSION_RESULT)
1319 );
1320 break;
1321 case CeilFloat32:
1322 case CeilFloat64:
1323 ret = ValueBuilder::makeCall(
1324 MATH_CEIL,
1325 visit(curr->value, EXPRESSION_RESULT)
1326 );
1327 break;
1328 case FloorFloat32:
1329 case FloorFloat64:
1330 ret = ValueBuilder::makeCall(
1331 MATH_FLOOR,
1332 visit(curr->value, EXPRESSION_RESULT)
1333 );
1334 break;
1335 case TruncFloat32:
1336 case TruncFloat64:
1337 ret = ValueBuilder::makeCall(
1338 MATH_TRUNC,
1339 visit(curr->value, EXPRESSION_RESULT)
1340 );
1341 break;
1342 case NearestFloat32:
1343 case NearestFloat64:
1344 ret = ValueBuilder::makeCall(
1345 MATH_NEAREST,
1346 visit(curr->value,EXPRESSION_RESULT)
1347 );
1348 break;
1349 case SqrtFloat32:
1350 case SqrtFloat64:
1351 ret = ValueBuilder::makeCall(
1352 MATH_SQRT,
1353 visit(curr->value, EXPRESSION_RESULT)
1354 );
1355 break;
1356 // TODO: more complex unary conversions
1357 default:
1358 std::cerr << "Unhandled unary float operator: " << curr
1359 << std::endl;
1360 abort();
1361 }
1362 if (curr->type == f32) { // doubles need much less coercing
1363 return makeAsmCoercion(ret, ASM_FLOAT);
1364 }
1365 return ret;
1366 }
1367 default: {
1368 std::cerr << "Unhandled type in unary: " << curr << std::endl;
1369 abort();
1370 }
1371 }
1372 }
1373
1374 Ref visitBinary(Binary* curr) {
1375 if (isStatement(curr)) {
1376 ScopedTemp tempLeft(curr->left->type, parent, func);
1377 GetLocal fakeLocalLeft(allocator);
1378 fakeLocalLeft.index = func->getLocalIndex(tempLeft.getName());
1379 ScopedTemp tempRight(curr->right->type, parent, func);
1380 GetLocal fakeLocalRight(allocator);
1381 fakeLocalRight.index = func->getLocalIndex(tempRight.getName());
1382 Binary fakeBinary = *curr;
1383 fakeBinary.left = &fakeLocalLeft;
1384 fakeBinary.right = &fakeLocalRight;
1385 Ref ret = blockify(visitAndAssign(curr->left, tempLeft));
1386 flattenAppend(ret, visitAndAssign(curr->right, tempRight));
1387 flattenAppend(ret, visitAndAssign(&fakeBinary, result));
1388 return ret;
1389 }
1390 // normal binary
1391 Ref left = visit(curr->left, EXPRESSION_RESULT);
1392 Ref right = visit(curr->right, EXPRESSION_RESULT);
1393 Ref ret;
1394 switch (curr->op) {
1395 case AddInt32:
1396 ret = ValueBuilder::makeBinary(left, PLUS, right);
1397 break;
1398 case SubInt32:
1399 ret = ValueBuilder::makeBinary(left, MINUS, right);
1400 break;
1401 case MulInt32: {
1402 if (curr->type == i32) {
1403 // TODO: when one operand is a small int, emit a multiply
1404 return ValueBuilder::makeCall(MATH_IMUL, left, right);
1405 } else {
1406 return ValueBuilder::makeBinary(left, MUL, right);
1407 }
1408 }
1409 case DivSInt32:
1410 ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), DIV,
1411 makeSigning(right, ASM_SIGNED));
1412 break;
1413 case DivUInt32:
1414 ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), DIV,
1415 makeSigning(right, ASM_UNSIGNED));
1416 break;
1417 case RemSInt32:
1418 ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), MOD,
1419 makeSigning(right, ASM_SIGNED));
1420 break;
1421 case RemUInt32:
1422 ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), MOD,
1423 makeSigning(right, ASM_UNSIGNED));
1424 break;
1425 case AndInt32:
1426 ret = ValueBuilder::makeBinary(left, AND, right);
1427 break;
1428 case OrInt32:
1429 ret = ValueBuilder::makeBinary(left, OR, right);
1430 break;
1431 case XorInt32:
1432 ret = ValueBuilder::makeBinary(left, XOR, right);
1433 break;
1434 case ShlInt32:
1435 ret = ValueBuilder::makeBinary(left, LSHIFT, right);
1436 break;
1437 case ShrUInt32:
1438 ret = ValueBuilder::makeBinary(left, TRSHIFT, right);
1439 break;
1440 case ShrSInt32:
1441 ret = ValueBuilder::makeBinary(left, RSHIFT, right);
1442 break;
1443 case MinFloat32:
1444 ret = ValueBuilder::makeCall(MATH_MIN, left, right);
1445 break;
1446 case MaxFloat32:
1447 ret = ValueBuilder::makeCall(MATH_MAX, left, right);
1448 break;
1449 case EqInt32: {
1450 // TODO: check if this condition is still valid/necessary
1451 if (curr->left->type == i32) {
1452 return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), EQ,
1453 makeSigning(right, ASM_SIGNED));
1454 } else {
1455 return ValueBuilder::makeBinary(left, EQ, right);
1456 }
1457 }
1458 case NeInt32: {
1459 if (curr->left->type == i32) {
1460 return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), NE,
1461 makeSigning(right, ASM_SIGNED));
1462 } else {
1463 return ValueBuilder::makeBinary(left, NE, right);
1464 }
1465 }
1466 case LtSInt32:
1467 return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LT,
1468 makeSigning(right, ASM_SIGNED));
1469 case LtUInt32:
1470 return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LT,
1471 makeSigning(right, ASM_UNSIGNED));
1472 case LeSInt32:
1473 return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LE,
1474 makeSigning(right, ASM_SIGNED));
1475 case LeUInt32:
1476 return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LE,
1477 makeSigning(right, ASM_UNSIGNED));
1478 case GtSInt32:
1479 return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GT,
1480 makeSigning(right, ASM_SIGNED));
1481 case GtUInt32:
1482 return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GT,
1483 makeSigning(right, ASM_UNSIGNED));
1484 case GeSInt32:
1485 return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GE,
1486 makeSigning(right, ASM_SIGNED));
1487 case GeUInt32:
1488 return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE,
1489 makeSigning(right, ASM_UNSIGNED));
1490 case RotLInt32:
1491 return makeSigning(ValueBuilder::makeCall(WASM_ROTL32, left, right),
1492 ASM_SIGNED);
1493 case RotRInt32:
1494 return makeSigning(ValueBuilder::makeCall(WASM_ROTR32, left, right),
1495 ASM_SIGNED);
1496 default: {
1497 std::cerr << "Unhandled binary operator: " << curr << std::endl;
1498 abort();
1499 }
1500 }
1501 return makeAsmCoercion(ret, wasmToAsmType(curr->type));
1502 }
1503
1504 Ref visitSelect(Select* curr) {
1505 if (isStatement(curr)) {
1506 ScopedTemp tempIfTrue(curr->ifTrue->type, parent, func);
1507 GetLocal fakeLocalIfTrue(allocator);
1508 fakeLocalIfTrue.index = func->getLocalIndex(tempIfTrue.getName());
1509 ScopedTemp tempIfFalse(curr->ifFalse->type, parent, func);
1510 GetLocal fakeLocalIfFalse(allocator);
1511 fakeLocalIfFalse.index = func->getLocalIndex(tempIfFalse.getName());
1512 ScopedTemp tempCondition(i32, parent, func);
1513 GetLocal fakeCondition(allocator);
1514 fakeCondition.index = func->getLocalIndex(tempCondition.getName());
1515 Select fakeSelect = *curr;
1516 fakeSelect.ifTrue = &fakeLocalIfTrue;
1517 fakeSelect.ifFalse = &fakeLocalIfFalse;
1518 fakeSelect.condition = &fakeCondition;
1519 Ref ret = blockify(visitAndAssign(curr->ifTrue, tempIfTrue));
1520 flattenAppend(ret, visitAndAssign(curr->ifFalse, tempIfFalse));
1521 flattenAppend(ret, visitAndAssign(curr->condition, tempCondition));
1522 flattenAppend(ret, visitAndAssign(&fakeSelect, result));
1523 return ret;
1524 }
1525 // normal select
1526 Ref ifTrue = visit(curr->ifTrue, EXPRESSION_RESULT);
1527 Ref ifFalse = visit(curr->ifFalse, EXPRESSION_RESULT);
1528 Ref condition = visit(curr->condition, EXPRESSION_RESULT);
1529 ScopedTemp tempIfTrue(curr->type, parent, func),
1530 tempIfFalse(curr->type, parent, func),
1531 tempCondition(i32, parent, func);
1532 return
1533 ValueBuilder::makeSeq(
1534 ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition),
1535 ValueBuilder::makeSeq(
1536 ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue),
1537 ValueBuilder::makeSeq(
1538 ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse),
1539 ValueBuilder::makeConditional(
1540 tempCondition.getAstName(),
1541 tempIfTrue.getAstName(),
1542 tempIfFalse.getAstName()
1543 )
1544 )
1545 )
1546 );
1547 }
1548
1549 Ref visitReturn(Return* curr) {
1550 Ref val = (curr->value == nullptr) ?
1551 Ref() :
1552 makeAsmCoercion(
1553 visit(curr->value, NO_RESULT),
1554 wasmToAsmType(curr->value->type)
1555 );
1556 return ValueBuilder::makeReturn(val);
1557 }
1558
1559 Ref visitHost(Host* curr) {
1560 abort();
1561 }
1562
1563 Ref visitNop(Nop* curr) {
1564 return ValueBuilder::makeToplevel();
1565 }
1566
1567 Ref visitUnreachable(Unreachable* curr) {
1568 return ValueBuilder::makeCall(ABORT_FUNC);
1569 }
1570 };
1571 return ExpressionProcessor(this, func).visit(func->body, result);
1572 }
1573
1574 static Ref makeInstantiation() {
1575 Ref lib = ValueBuilder::makeObject();
1576 auto insertItem = [&](IString item) {
1577 ValueBuilder::appendToObject(lib, item, ValueBuilder::makeName(item));
1578 };
1579 insertItem(MATH);
1580 insertItem(INT8ARRAY);
1581 insertItem(INT16ARRAY);
1582 insertItem(INT32ARRAY);
1583 insertItem(UINT8ARRAY);
1584 insertItem(UINT16ARRAY);
1585 insertItem(UINT32ARRAY);
1586 insertItem(FLOAT32ARRAY);
1587 insertItem(FLOAT64ARRAY);
1588 Ref env = ValueBuilder::makeObject();
1589 Ref mem = ValueBuilder::makeNew(
1590 ValueBuilder::makeCall(ARRAY_BUFFER, ValueBuilder::makeInt(0x10000)));
1591 Ref call = ValueBuilder::makeCall(IString(ASM_FUNC), lib, env, mem);
1592 Ref ret = ValueBuilder::makeVar();
1593 ValueBuilder::appendToVar(ret, ASM_MODULE, call);
1594 return ret;
1595 }
1596
1597 static void prefixCalls(Ref asmjs) {
1598 if (asmjs->isArray()) {
1599 ArrayStorage& arr = asmjs->getArray();
1600 for (Ref& r : arr) {
1601 prefixCalls(r);
1602 }
1603 if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) {
1604 assert(arr.size() >= 2);
1605 Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(ASM_MODULE),
1606 arr[1]->getIString());
1607 arr[1]->setArray(prefixed->getArray());
1608 }
1609 }
1610 }
1611
1612 Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
1613 Builder& wasmBuilder,
1614 Element& e, Name testFuncName) {
1615 Expression* actual = sexpBuilder.parseExpression(e[1]);
1616 Expression* body = nullptr;
1617 if (e.size() == 2) {
1618 if (actual->type == none) {
1619 body = wasmBuilder.blockify(
1620 actual,
1621 wasmBuilder.makeConst(Literal(uint32_t(1)))
1622 );
1623 } else {
1624 body = actual;
1625 }
1626 } else if (e.size() == 3) {
1627 Expression* expected = sexpBuilder.parseExpression(e[2]);
1628 WasmType resType = expected->type;
1629 actual->type = resType;
1630 BinaryOp eqOp;
1631 switch (resType) {
1632 case i32: eqOp = EqInt32; break;
1633 case i64: eqOp = EqInt64; break;
1634 case f32: eqOp = EqFloat32; break;
1635 case f64: eqOp = EqFloat64; break;
1636 default: {
1637 std::cerr << "Unhandled type in assert: " << resType << std::endl;
1638 abort();
1639 }
1640 }
1641 body = wasmBuilder.makeBinary(eqOp, actual, expected);
1642 } else {
1643 assert(false && "Unexpected number of parameters in assert_return");
1644 }
1645 std::unique_ptr<Function> testFunc(
1646 wasmBuilder.makeFunction(
1647 testFuncName,
1648 std::vector<NameType>{},
1649 body->type,
1650 std::vector<NameType>{},
1651 body
1652 )
1653 );
1654 Ref jsFunc = processFunction(testFunc.get());
1655 prefixCalls(jsFunc);
1656 return jsFunc;
1657 }
1658
1659 Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
1660 Builder& wasmBuilder,
1661 Element& e, Name testFuncName) {
1662 Name innerFuncName("f");
1663 Expression* expr = sexpBuilder.parseExpression(e[1]);
1664 std::unique_ptr<Function> exprFunc(
1665 wasmBuilder.makeFunction(innerFuncName,
1666 std::vector<NameType>{},
1667 expr->type,
1668 std::vector<NameType>{},
1669 expr)
1670 );
1671 IString expectedErr = e[2]->str();
1672 Ref innerFunc = processFunction(exprFunc.get());
1673 Ref outerFunc = ValueBuilder::makeFunction(testFuncName);
1674 outerFunc[3]->push_back(innerFunc);
1675 Ref tryBlock = ValueBuilder::makeBlock();
1676 ValueBuilder::appendToBlock(tryBlock, ValueBuilder::makeCall(innerFuncName));
1677 Ref catchBlock = ValueBuilder::makeBlock();
1678 ValueBuilder::appendToBlock(
1679 catchBlock,
1680 ValueBuilder::makeReturn(
1681 ValueBuilder::makeCall(
1682 ValueBuilder::makeDot(
1683 ValueBuilder::makeName(IString("e")),
1684 ValueBuilder::makeName(IString("message")),
1685 ValueBuilder::makeName(IString("includes"))
1686 ),
1687 ValueBuilder::makeString(expectedErr)
1688 )
1689 )
1690 );
1691 outerFunc[3]->push_back(ValueBuilder::makeTry(
1692 tryBlock,
1693 ValueBuilder::makeName((IString("e"))),
1694 catchBlock));
1695 outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0)));
1696 return outerFunc;
1697 }
1698
1699 bool Wasm2AsmBuilder::isAssertHandled(Element& e) {
1700 return e.isList() && e.size() >= 2 && e[0]->isStr()
1701 && (e[0]->str() == Name("assert_return") ||
1702 (flags.pedantic && e[0]->str() == Name("assert_trap")))
1703 && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr()
1704 && (*e[1])[0]->str() == Name("invoke");
1705 }
1706
1707 Ref Wasm2AsmBuilder::processAsserts(Element& root,
1708 SExpressionWasmBuilder& sexpBuilder) {
1709 Builder wasmBuilder(sexpBuilder.getAllocator());
1710 Ref ret = ValueBuilder::makeBlock();
1711 flattenAppend(ret, makeInstantiation());
1712 for (size_t i = 1; i < root.size(); ++i) {
1713 Element& e = *root[i];
1714 if (!isAssertHandled(e)) {
1715 std::cerr << "skipping " << e << std::endl;
1716 continue;
1717 }
1718 Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false));
1719 bool isReturn = (e[0]->str() == Name("assert_return"));
1720 Element& testOp = *e[1];
1721 // Replace "invoke" with "call"
1722 testOp[0]->setString(IString("call"), false, false);
1723 // Need to claim dollared to get string as function target
1724 testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false);
1725
1726 Ref testFunc = isReturn ?
1727 makeAssertReturnFunc(sexpBuilder, wasmBuilder, e, testFuncName) :
1728 makeAssertTrapFunc(sexpBuilder, wasmBuilder, e, testFuncName);
1729
1730 flattenAppend(ret, testFunc);
1731 std::stringstream failFuncName;
1732 failFuncName << "fail" << std::to_string(i);
1733 flattenAppend(
1734 ret,
1735 ValueBuilder::makeIf(
1736 ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testFuncName)),
1737 ValueBuilder::makeCall(IString(failFuncName.str().c_str(), false)),
1738 Ref()
1739 )
1740 );
1741 }
1742 return ret;
1743 }
1744
1745
1746 } // namespace wasm
1747
1748 #endif // wasm_wasm2asm_h