1 /************************************************************************
2 ************************************************************************
3 FAUST compiler
4 Copyright (C) 2003-2018 GRAME, Centre National de Creation Musicale
5 ---------------------------------------------------------------------
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ************************************************************************
20 ************************************************************************/
21
22 #ifndef _WASM_INSTRUCTIONS_H
23 #define _WASM_INSTRUCTIONS_H
24
25 #include <string.h>
26 #include <cmath>
27 #include <vector>
28
29 #include "fir_to_fir.hh"
30 #include "was_instructions.hh"
31 #include "wasm_binary.hh"
32
33 using namespace std;
34
35 //
36 // We mostly stream into a buffer as we create the binary format, however,
37 // sometimes we need to backtrack and write to a location behind us - wasm
38 // is optimized for reading, not writing.
39 //
40 class BufferWithRandomAccess : public std::vector<uint8_t> {
41 private:
42 bool debug;
43
44 public:
BufferWithRandomAccess(bool dbg=false)45 BufferWithRandomAccess(bool dbg = false) : debug(dbg) {}
46
operator <<(int8_t x)47 BufferWithRandomAccess& operator<<(int8_t x)
48 {
49 if (debug) std::cerr << "writeInt8: " << (int)(uint8_t)x << " (at " << size() << ")" << std::endl;
50 push_back(x);
51 return *this;
52 }
53
operator <<(int16_t x)54 BufferWithRandomAccess& operator<<(int16_t x)
55 {
56 if (debug) std::cerr << "writeInt16: " << x << " (at " << size() << ")" << std::endl;
57 push_back(x & 0xff);
58 push_back(x >> 8);
59 return *this;
60 }
61
operator <<(int32_t x)62 BufferWithRandomAccess& operator<<(int32_t x)
63 {
64 if (debug) std::cerr << "writeInt32: " << x << " (at " << size() << ")" << std::endl;
65 push_back(x & 0xff);
66 x >>= 8;
67 push_back(x & 0xff);
68 x >>= 8;
69 push_back(x & 0xff);
70 x >>= 8;
71 push_back(x & 0xff);
72 return *this;
73 }
74
operator <<(int64_t x)75 BufferWithRandomAccess& operator<<(int64_t x)
76 {
77 if (debug) std::cerr << "writeInt64: " << x << " (at " << size() << ")" << std::endl;
78 push_back(x & 0xff);
79 x >>= 8;
80 push_back(x & 0xff);
81 x >>= 8;
82 push_back(x & 0xff);
83 x >>= 8;
84 push_back(x & 0xff);
85 x >>= 8;
86 push_back(x & 0xff);
87 x >>= 8;
88 push_back(x & 0xff);
89 x >>= 8;
90 push_back(x & 0xff);
91 x >>= 8;
92 push_back(x & 0xff);
93 return *this;
94 }
95
operator <<(U32LEB x)96 BufferWithRandomAccess& operator<<(U32LEB x)
97 {
98 size_t before = -1;
99 if (debug) {
100 before = size();
101 std::cerr << "writeU32LEB: " << x.value << " (at " << before << ")" << std::endl;
102 }
103 x.write(this);
104 if (debug) {
105 for (size_t i = before; i < size(); i++) {
106 std::cerr << " " << (int)at(i) << " (at " << i << ")\n";
107 }
108 }
109 return *this;
110 }
111
operator <<(U64LEB x)112 BufferWithRandomAccess& operator<<(U64LEB x)
113 {
114 size_t before = -1;
115 if (debug) {
116 before = size();
117 std::cerr << "writeU64LEB: " << x.value << " (at " << before << ")" << std::endl;
118 }
119 x.write(this);
120 if (debug) {
121 for (size_t i = before; i < size(); i++) {
122 std::cerr << " " << (int)at(i) << " (at " << i << ")\n";
123 }
124 }
125 return *this;
126 }
127
operator <<(S32LEB x)128 BufferWithRandomAccess& operator<<(S32LEB x)
129 {
130 size_t before = -1;
131 if (debug) {
132 before = size();
133 std::cerr << "writeS32LEB: " << x.value << " (at " << before << ")" << std::endl;
134 }
135 x.write(this);
136 if (debug) {
137 for (size_t i = before; i < size(); i++) {
138 std::cerr << " " << (int)at(i) << " (at " << i << ")\n";
139 }
140 }
141 return *this;
142 }
143
operator <<(S64LEB x)144 BufferWithRandomAccess& operator<<(S64LEB x)
145 {
146 size_t before = -1;
147 if (debug) {
148 before = size();
149 std::cerr << "writeS64LEB: " << x.value << " (at " << before << ")" << std::endl;
150 }
151 x.write(this);
152 if (debug) {
153 for (size_t i = before; i < size(); i++) {
154 std::cerr << " " << (int)at(i) << " (at " << i << ")\n";
155 }
156 }
157 return *this;
158 }
159
operator <<(uint8_t x)160 BufferWithRandomAccess& operator<<(uint8_t x) { return *this << (int8_t)x; }
161
operator <<(uint16_t x)162 BufferWithRandomAccess& operator<<(uint16_t x) { return *this << (int16_t)x; }
163
operator <<(uint32_t x)164 BufferWithRandomAccess& operator<<(uint32_t x) { return *this << (int32_t)x; }
165
operator <<(uint64_t x)166 BufferWithRandomAccess& operator<<(uint64_t x) { return *this << (int64_t)x; }
167
operator <<(float x)168 BufferWithRandomAccess& operator<<(float x)
169 {
170 if (debug) std::cerr << "writeFloat32: " << x << " (at " << size() << ")" << std::endl;
171 return *this << bit_cast<int32_t>(x);
172 }
173
operator <<(double x)174 BufferWithRandomAccess& operator<<(double x)
175 {
176 if (debug) std::cerr << "writeFloat64: " << x << " (at " << size() << ")" << std::endl;
177 return *this << bit_cast<int64_t>(x);
178 }
179
operator <<(const std::string & str)180 BufferWithRandomAccess& operator<<(const std::string& str)
181 {
182 if (debug) std::cerr << "writeString: " << str << " (at " << size() << ")" << std::endl;
183 int32_t size = int32_t(str.size());
184 *this << U32LEB(size);
185 for (int32_t i = 0; i < size; i++) {
186 *this << int8_t(str[i]);
187 }
188 return *this;
189 }
190
writeAt(size_t i,uint16_t x)191 void writeAt(size_t i, uint16_t x)
192 {
193 if (debug) std::cerr << "backpatchInt16: " << x << " (at " << i << ")" << std::endl;
194 (*this)[i] = x & 0xff;
195 (*this)[i + 1] = x >> 8;
196 }
197
writeAt(size_t i,uint32_t x)198 void writeAt(size_t i, uint32_t x)
199 {
200 if (debug) std::cerr << "backpatchInt32: " << x << " (at " << i << ")" << std::endl;
201 (*this)[i] = x & 0xff;
202 x >>= 8;
203 (*this)[i + 1] = x & 0xff;
204 x >>= 8;
205 (*this)[i + 2] = x & 0xff;
206 x >>= 8;
207 (*this)[i + 3] = x & 0xff;
208 }
209
writeAt(size_t i,U32LEB x)210 void writeAt(size_t i, U32LEB x)
211 {
212 if (debug) std::cerr << "backpatchU32LEB: " << x.value << " (at " << i << ")" << std::endl;
213 x.writeAt(this, i, 5); // fill all 5 bytes, we have to do this when backpatching
214 }
215
writeU32LEBPlaceholder()216 int32_t writeU32LEBPlaceholder()
217 {
218 int32_t ret = int32_t(size());
219 *this << int32_t(0);
220 *this << int8_t(0);
221 return ret;
222 }
223
toString()224 string toString()
225 {
226 stringstream str;
227 for (const auto& c : *this) str << c;
228 return str.str();
229 }
230
231 template <typename T>
writeTo(T & o)232 void writeTo(T& o)
233 {
234 for (const auto& c : *this) o << c;
235 }
236 };
237
startSectionAux(BufferWithRandomAccess * out,BinaryConsts::Section code)238 inline int32_t startSectionAux(BufferWithRandomAccess* out, BinaryConsts::Section code)
239 {
240 *out << U32LEB(code);
241 return out->writeU32LEBPlaceholder(); // section size to be filled in later
242 }
243
finishSectionAux(BufferWithRandomAccess * out,int32_t start)244 inline void finishSectionAux(BufferWithRandomAccess* out, int32_t start)
245 {
246 // section size does not include the 5 bytes of the size field itself
247 int32_t size = int32_t(out->size()) - start - 5;
248 out->writeAt(start, U32LEB(size));
249 }
250
type2Binary(Typed::VarType type)251 inline S32LEB type2Binary(Typed::VarType type)
252 {
253 if (isIntOrPtrType(type) || isBoolType(type)) {
254 return S32LEB(BinaryConsts::EncodedType::i32);
255 } else if (type == Typed::kFloat) {
256 return S32LEB(BinaryConsts::EncodedType::f32);
257 } else if (type == Typed::kDouble) {
258 return S32LEB(BinaryConsts::EncodedType::f64);
259 } else {
260 faustassert(false);
261 return S32LEB(BinaryConsts::EncodedType::Empty);
262 }
263 }
264
265 // Local variable counter with their types
266 struct LocalVarDesc {
LocalVarDescLocalVarDesc267 LocalVarDesc() {}
268
LocalVarDescLocalVarDesc269 LocalVarDesc(int index, Typed::VarType type, Address::AccessType access)
270 : fIndex(index), fType(type), fAccess(access)
271 {
272 }
273
274 int fIndex;
275 Typed::VarType fType;
276 Address::AccessType fAccess;
277 };
278
279 // Count local variables (stack/loop) with their types : to be used at the beginning of each block
280 // Funargs variables are indexed first
281 struct LocalVariableCounter : public DispatchVisitor {
282 int fIn32Type;
283 int fF32Type;
284 int fF64Type;
285
286 int fFunArgIndex;
287
288 map<string, LocalVarDesc> fLocalVarTable;
289
LocalVariableCounterLocalVariableCounter290 LocalVariableCounter() : fIn32Type(0), fF32Type(0), fF64Type(0), fFunArgIndex(0) {}
291
visitLocalVariableCounter292 virtual void visit(DeclareVarInst* inst)
293 {
294 string name = inst->fAddress->getName();
295 Typed::VarType type = inst->fType->getType();
296
297 faustassert(fLocalVarTable.find(name) == fLocalVarTable.end());
298
299 // stack/loop variables accessed by [var_num, type] pairs
300 if (inst->fAddress->getAccess() & Address::kStack || inst->fAddress->getAccess() & Address::kLoop) {
301 if (isIntOrPtrType(type)) {
302 fLocalVarTable[name] = LocalVarDesc(fIn32Type++, type, inst->fAddress->getAccess());
303 } else if (type == Typed::kFloat) {
304 fLocalVarTable[name] = LocalVarDesc(fF32Type++, type, inst->fAddress->getAccess());
305 } else if (type == Typed::kDouble) {
306 fLocalVarTable[name] = LocalVarDesc(fF64Type++, type, inst->fAddress->getAccess());
307 } else {
308 faustassert(false);
309 }
310
311 faustassert(inst->fValue == nullptr);
312 }
313 }
314
visitLocalVariableCounter315 virtual void visit(DeclareFunInst* inst)
316 {
317 // funarg variable accessed by [var_num, type] pairs
318 for (const auto& argType : inst->fType->fArgsTypes) {
319 fLocalVarTable[argType->fName] = LocalVarDesc(fFunArgIndex++, argType->fType->getType(), Address::kFunArgs);
320 }
321
322 if (inst->fCode) {
323 inst->fCode->accept(this);
324 }
325 }
326
generateStackMapLocalVariableCounter327 void generateStackMap(BufferWithRandomAccess* out)
328 {
329 // Update stack variable index depending of 1) number of stack variables of different type 2) funarg variables
330 // number
331 for (auto& var : fLocalVarTable) {
332 if (var.second.fAccess != Address::kFunArgs) {
333 if (isIntOrPtrType(var.second.fType)) {
334 var.second.fIndex = var.second.fIndex + fFunArgIndex;
335 } else if (var.second.fType == Typed::kFloat) {
336 var.second.fIndex = var.second.fIndex + fFunArgIndex + fIn32Type;
337 } else if (var.second.fType == Typed::kDouble) {
338 var.second.fIndex = var.second.fIndex + fFunArgIndex + fIn32Type + fF32Type;
339 } else {
340 faustassert(false);
341 }
342 }
343 }
344
345 *out << U32LEB((fIn32Type ? 1 : 0) + (fF32Type ? 1 : 0) + (fF64Type ? 1 : 0));
346 if (fIn32Type) *out << U32LEB(fIn32Type) << S32LEB(BinaryConsts::EncodedType::i32);
347 if (fF32Type) *out << U32LEB(fF32Type) << S32LEB(BinaryConsts::EncodedType::f32);
348 if (fF64Type) *out << U32LEB(fF64Type) << S32LEB(BinaryConsts::EncodedType::f64);
349 }
350
dumpLocalVariableCounter351 void dump()
352 {
353 std::cout << "===== LocalVariableCounter begin =====" << std::endl;
354 for (const auto& varDesc : fLocalVarTable) {
355 std::cout << "varDesc " << varDesc.first << " index = " << varDesc.second.fIndex
356 << " type = " << Typed::gTypeString[varDesc.second.fType] << std::endl;
357 }
358 std::cout << "===== LocalVariableCounter end =====" << std::endl;
359 }
360 };
361
362 // Counter of functions with their types and global variable offset
363 struct FunAndTypeCounter : public DispatchVisitor, public WASInst {
364 std::map<string, FunTyped*> fFunTypes; // function name, function type
365 std::map<string, pair<string, string> > fFunImports; // function name, [module, base]
366
367 using DispatchVisitor::visit;
368
FunAndTypeCounterFunAndTypeCounter369 FunAndTypeCounter() : DispatchVisitor(), WASInst()
370 {
371 // Additional functions defined in the module
372 {
373 list<NamedTyped*> args;
374 args.push_back(InstBuilder::genNamedTyped("arg1", Typed::kInt32));
375 args.push_back(InstBuilder::genNamedTyped("arg2", Typed::kInt32));
376 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genInt32Typed(), FunTyped::kDefault);
377 fFunTypes["min_i"] = fun_type;
378 fFunTypes["max_i"] = fun_type;
379 }
380
381 // DSP API
382
383 // getNumInputs/getNumOutputs
384 {
385 list<NamedTyped*> args;
386 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
387 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genInt32Typed(), FunTyped::kDefault);
388 fFunTypes["getNumInputs"] = fun_type;
389 fFunTypes["getNumOutputs"] = fun_type;
390 }
391
392 // getSampleRate
393 {
394 list<NamedTyped*> args;
395 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
396 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genInt32Typed(), FunTyped::kDefault);
397 fFunTypes["getSampleRate"] = fun_type;
398 }
399
400 // init/instanceConstants/instanceInit
401 {
402 list<NamedTyped*> args;
403 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
404 args.push_back(InstBuilder::genNamedTyped("sample_rate", Typed::kInt32));
405 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genVoidTyped(), FunTyped::kDefault);
406 fFunTypes["init"] = fun_type;
407 fFunTypes["classInit"] = fun_type;
408 fFunTypes["instanceConstants"] = fun_type;
409 fFunTypes["instanceInit"] = fun_type;
410 }
411
412 // instanceClear/instanceResetUserInterface
413 {
414 list<NamedTyped*> args;
415 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
416 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genVoidTyped(), FunTyped::kDefault);
417 fFunTypes["instanceClear"] = fun_type;
418 fFunTypes["instanceResetUserInterface"] = fun_type;
419 }
420
421 // setParamValue
422 {
423 list<NamedTyped*> args;
424 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
425 args.push_back(InstBuilder::genNamedTyped("index", Typed::kInt32));
426 args.push_back(InstBuilder::genNamedTyped("value", itfloat()));
427 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genVoidTyped(), FunTyped::kDefault);
428 fFunTypes["setParamValue"] = fun_type;
429 }
430
431 // getParamValue
432 {
433 list<NamedTyped*> args;
434 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
435 args.push_back(InstBuilder::genNamedTyped("index", Typed::kInt32));
436 FunTyped* fun_type =
437 InstBuilder::genFunTyped(args, InstBuilder::genBasicTyped(itfloat()), FunTyped::kDefault);
438 fFunTypes["getParamValue"] = fun_type;
439 }
440
441 // compute
442 {
443 list<NamedTyped*> args;
444 args.push_back(InstBuilder::genNamedTyped("dsp", Typed::kObj_ptr));
445 args.push_back(InstBuilder::genNamedTyped("count", Typed::kInt32));
446 args.push_back(
447 InstBuilder::genNamedTyped("inputs", Typed::kVoid_ptr)); // so that fun type is correcty generated
448 args.push_back(
449 InstBuilder::genNamedTyped("outputs", Typed::kVoid_ptr)); // so that fun type is correcty generated
450 FunTyped* fun_type = InstBuilder::genFunTyped(args, InstBuilder::genVoidTyped(), FunTyped::kDefault);
451 fFunTypes["compute"] = fun_type;
452 }
453 }
454
visitFunAndTypeCounter455 virtual void visit(DeclareVarInst* inst)
456 {
457 bool is_struct = (inst->fAddress->getAccess() & Address::kStruct)
458 || (inst->fAddress->getAccess() & Address::kStaticStruct);
459
460 ArrayTyped* array_typed = dynamic_cast<ArrayTyped*>(inst->fType);
461 string name = inst->fAddress->getName();
462
463 if (array_typed && array_typed->fSize > 1) {
464 if (is_struct) {
465 fFieldTable[name] = MemoryDesc(-1, fStructOffset, array_typed->fSize, array_typed->fType->getType());
466 // Always use biggest size so that int/real access are correctly aligned
467 fStructOffset += (array_typed->fSize * gGlobal->audioSampleSize());
468 } else {
469 // Local variables declared by [var_num, type] pairs, separated as (local, set_local instruction)
470 }
471 } else {
472 if (is_struct) {
473 fFieldTable[name] = MemoryDesc(-1, fStructOffset, 1, inst->fType->getType());
474 // Always use biggest size so that int/real access are correctly aligned
475 fStructOffset += gGlobal->audioSampleSize();
476 } else {
477 // Local variables declared by [var_num, type] pairs, separated as (local, set_local instruction)
478 faustassert(inst->fValue == nullptr);
479 }
480 }
481 }
482
visitFunAndTypeCounter483 virtual void visit(DeclareFunInst* inst)
484 {
485 // Already generated
486 if (fFunctionSymbolTable.find(inst->fName) != fFunctionSymbolTable.end()) {
487 return;
488 } else {
489 fFunctionSymbolTable[inst->fName] = true;
490 }
491
492 // Math library functions are part of the 'global' module, 'fmod', 'log10' and 'remainder'
493 // will be manually generated
494 if (fMathLibTable.find(inst->fName) != fMathLibTable.end()) {
495 MathFunDesc desc = fMathLibTable[inst->fName];
496
497 if (desc.fMode == MathFunDesc::Gen::kExtMath || desc.fMode == MathFunDesc::Gen::kExtWAS) {
498
499 // Build function type (args type same as return type)
500 list<NamedTyped*> args;
501 if (desc.fArgs == 1) {
502 args.push_back(InstBuilder::genNamedTyped(gGlobal->getFreshID("v1"), desc.fTypeIn));
503 } else if (desc.fArgs == 2) {
504 args.push_back(InstBuilder::genNamedTyped(gGlobal->getFreshID("v1"), desc.fTypeIn));
505 args.push_back(InstBuilder::genNamedTyped(gGlobal->getFreshID("v2"), desc.fTypeIn));
506 } else {
507 faustassert(false);
508 }
509
510 FunTyped* fun_type =
511 InstBuilder::genFunTyped(args, InstBuilder::genBasicTyped(desc.fTypeOut), FunTyped::kDefault);
512 fFunTypes[inst->fName] = fun_type;
513
514 // Build function import
515 fFunImports[inst->fName] = std::make_pair("env", desc.fName);
516 }
517
518 } else {
519 // Prototype
520 fFunTypes[inst->fName] = inst->fType;
521 }
522 }
523
524 // Get the function index : imported functions are first followed by all module internally defined ones
getFunctionIndexFunAndTypeCounter525 int32_t getFunctionIndex(const string& name)
526 {
527 // If imported function
528 if (fFunImports.find(name) != fFunImports.end()) {
529 int i = 0;
530 for (const auto& import : fFunImports) {
531 if (import.first == name) {
532 return i;
533 }
534 i++;
535 }
536 // Otherwise module defined function
537 } else {
538 int i = int(fFunImports.size());
539 for (const auto& type : fFunTypes) {
540 if (fFunImports.find(type.first) == fFunImports.end()) {
541 if (type.first == name) {
542 return i;
543 }
544 i++; // only count module defined functions
545 }
546 }
547 }
548
549 std::cerr << "getFunctionIndex " << name << std::endl;
550 faustassert(false);
551 return -1;
552 }
553
554 // Get the function type index
getFunctionTypeIndexFunAndTypeCounter555 int32_t getFunctionTypeIndex(const string& name)
556 {
557 int i = 0;
558 for (const auto& type : fFunTypes) {
559 if (type.first == name) {
560 return i;
561 }
562 i++;
563 }
564 std::cerr << "getFunctionTypeIndex " << name << std::endl;
565 faustassert(false);
566 return -1;
567 }
568
generateFunTypesFunAndTypeCounter569 void generateFunTypes(BufferWithRandomAccess* out)
570 {
571 int32_t start = startSectionAux(out, BinaryConsts::Section::Type);
572 *out << U32LEB(uint32_t(fFunTypes.size()));
573
574 for (const auto& type_int : fFunTypes) {
575 FunTyped* type = type_int.second;
576 *out << S32LEB(BinaryConsts::EncodedType::Func);
577 *out << U32LEB(uint32_t(type->fArgsTypes.size()));
578 for (auto param : type->fArgsTypes) {
579 *out << type2Binary(param->getType());
580 }
581 if (type->fResult->getType() == Typed::kVoid) {
582 *out << U32LEB(0);
583 } else {
584 *out << U32LEB(1);
585 *out << type2Binary(type->fResult->getType());
586 }
587 }
588
589 finishSectionAux(out, start);
590 }
591
592 // Generate list of imports
generateImportsFunAndTypeCounter593 void generateImports(BufferWithRandomAccess* out, int channels, bool internal_memory)
594 {
595 int32_t start = startSectionAux(out, BinaryConsts::Section::Import);
596 *out << U32LEB(uint32_t(fFunImports.size()) + ((internal_memory) ? 0 : 1));
597
598 if (!internal_memory) {
599 // Memory
600 *out << "env";
601 *out << "memory";
602 *out << U32LEB(int32_t(ExternalKind::Memory)); // Memory kind
603 *out << U32LEB(0); // Memory flags
604 *out << U32LEB(1); // Memory size set by JS code, so use a minimum value that contains the data segment
605 // size (shoud be OK for any JSON)
606 }
607
608 for (const auto& import : fFunImports) {
609 *out << import.second.first; // module
610 // Possibly map fastmath functions, emcc compiled functions are prefixed with '_'
611 *out << ("_" + gGlobal->getMathFunction(import.first)); // base
612 *out << U32LEB(int32_t(ExternalKind::Function));
613 *out << U32LEB(getFunctionTypeIndex(import.first)); // function type index
614 }
615
616 finishSectionAux(out, start);
617 }
618
619 // Generate internal function export
generateExportFunAndTypeCounter620 void generateExport(BufferWithRandomAccess* out, const string& name)
621 {
622 *out << name;
623 *out << U32LEB(int32_t(ExternalKind::Function));
624 *out << U32LEB(getFunctionIndex(name)); // function index
625 }
626
627 // Generate list of function signatures
generateFuncSignaturesFunAndTypeCounter628 void generateFuncSignatures(BufferWithRandomAccess* out)
629 {
630 int32_t start = startSectionAux(out, BinaryConsts::Section::Function);
631 *out << U32LEB(uint32_t(fFunTypes.size() - fFunImports.size()));
632
633 // Module internally defined functions (those not in FunImports)
634 for (const auto& type : fFunTypes) {
635 if (fFunImports.find(type.first) == fFunImports.end()) {
636 *out << U32LEB(getFunctionTypeIndex(type.first));
637 }
638 }
639 finishSectionAux(out, start);
640 }
641 };
642
643 #define EXPORTED_FUNCTION_NUM 11
644
645 class WASMInstVisitor : public DispatchVisitor, public WASInst {
646 private:
647 map<string, LocalVarDesc> fLocalVarTable;
648 BufferWithRandomAccess* fOut;
649 FunAndTypeCounter fFunAndTypeCounter;
650
generateMemoryAccess(int offset=0)651 void generateMemoryAccess(int offset = 0)
652 {
653 //*fOut << U32LEB(offStrNum); // Makes V8 return: 'invalid alignment; expected maximum alignment is 2, actual
654 // alignment is 3'
655 *fOut << U32LEB(2);
656 *fOut << U32LEB(offset);
657 }
658
659 public:
660 using DispatchVisitor::visit;
661
WASMInstVisitor(BufferWithRandomAccess * out,bool fast_memory)662 WASMInstVisitor(BufferWithRandomAccess* out, bool fast_memory) : WASInst(fast_memory), fOut(out) {}
663
~WASMInstVisitor()664 virtual ~WASMInstVisitor() {}
665
setLocalVarTable(const map<string,LocalVarDesc> & table)666 void setLocalVarTable(const map<string, LocalVarDesc>& table) { fLocalVarTable = table; }
667
getFunAndTypeCounter()668 FunAndTypeCounter* getFunAndTypeCounter() { return &fFunAndTypeCounter; }
669
updateStructOffsetAndFieldTable()670 void updateStructOffsetAndFieldTable()
671 {
672 fStructOffset = fFunAndTypeCounter.fStructOffset;
673 fFieldTable = fFunAndTypeCounter.fFieldTable;
674 }
675
startSection(BinaryConsts::Section code)676 int32_t startSection(BinaryConsts::Section code) { return startSectionAux(fOut, code); }
677
finishSection(int32_t start)678 void finishSection(int32_t start) { return finishSectionAux(fOut, start); }
679
generateFunTypes()680 void generateFunTypes() { fFunAndTypeCounter.generateFunTypes(fOut); }
681
generateImports(int channels,bool internal_memory)682 void generateImports(int channels, bool internal_memory)
683 {
684 fFunAndTypeCounter.generateImports(fOut, channels, internal_memory);
685 }
686
generateExports(bool internal_memory)687 void generateExports(bool internal_memory)
688 {
689 int32_t start = startSection(BinaryConsts::Section::Export);
690 *fOut << U32LEB(EXPORTED_FUNCTION_NUM +
691 ((internal_memory) ? 1 : 0)); // num export = EXPORTED_FUNCTION_NUM functions (+ memory)
692
693 fFunAndTypeCounter.generateExport(fOut, "compute");
694 fFunAndTypeCounter.generateExport(fOut, "getNumInputs");
695 fFunAndTypeCounter.generateExport(fOut, "getNumOutputs");
696 fFunAndTypeCounter.generateExport(fOut, "getParamValue");
697 fFunAndTypeCounter.generateExport(fOut, "getSampleRate");
698 fFunAndTypeCounter.generateExport(fOut, "init");
699 fFunAndTypeCounter.generateExport(fOut, "instanceClear");
700 fFunAndTypeCounter.generateExport(fOut, "instanceConstants");
701 fFunAndTypeCounter.generateExport(fOut, "instanceInit");
702 fFunAndTypeCounter.generateExport(fOut, "instanceResetUserInterface");
703 fFunAndTypeCounter.generateExport(fOut, "setParamValue");
704
705 if (internal_memory) {
706 // Memory
707 *fOut << "memory";
708 *fOut << U32LEB(int32_t(ExternalKind::Memory)); // Memory kind
709 *fOut << U32LEB(0); // Memory index
710 }
711
712 finishSection(start);
713 }
714
715 // Return the stream position where the memory size value will have to be written
generateInternalMemory()716 size_t generateInternalMemory()
717 {
718 int32_t start = startSection(BinaryConsts::Section::Memory);
719 *fOut << U32LEB(1); // num memories
720 *fOut << U32LEB(1); // memory flags, 1 means [min, max]
721 // minimum memory pages number
722 size_t size_pos = fOut->writeU32LEBPlaceholder();
723 // maximum memory pages number, to be extended on JS side for soundfiles
724 fOut->writeU32LEBPlaceholder();
725 finishSection(start);
726 return size_pos;
727 }
728
generateFuncSignatures()729 void generateFuncSignatures() { fFunAndTypeCounter.generateFuncSignatures(fOut); }
730
generateModuleHeader()731 void generateModuleHeader() { *fOut << int32_t(BinaryConsts::Magic) << int32_t(BinaryConsts::Version); }
732
733 // (adhoc generation for now since currently FIR cannot be generated to handle this case)
generateSetParamValue()734 void generateSetParamValue()
735 {
736 size_t size_pos = fOut->writeU32LEBPlaceholder();
737 size_t start = fOut->size();
738
739 // Local variables
740 LocalVariableCounter local_counter;
741 local_counter.generateStackMap(fOut);
742
743 // Index in the dsp
744 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(0); // 0 = dsp
745 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(1); // 1 = index
746 *fOut << int8_t(gBinOpTable[kAdd]->fWasmInt32);
747
748 // Value
749 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(2); // 2 = value
750
751 // Store value at index
752 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::F32StoreMem) : int8_t(BinaryConsts::F64StoreMem));
753 generateMemoryAccess();
754
755 // Generate end
756 *fOut << int8_t(BinaryConsts::End);
757 size_t size = fOut->size() - start;
758 fOut->writeAt(size_pos, U32LEB(uint32_t(size)));
759 }
760
761 // (adhoc generation for now since currently FIR cannot be generated to handle this case)
generateGetParamValue()762 void generateGetParamValue()
763 {
764 size_t size_pos = fOut->writeU32LEBPlaceholder();
765 size_t start = fOut->size();
766
767 // Local variables
768 LocalVariableCounter local_counter;
769 local_counter.generateStackMap(fOut);
770
771 // Index in the dsp
772 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(0); // 0 = dsp
773 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(1); // 1 = index
774 *fOut << int8_t(gBinOpTable[kAdd]->fWasmInt32);
775
776 // Load value from index
777 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::F32LoadMem) : int8_t(BinaryConsts::F64LoadMem));
778 generateMemoryAccess();
779
780 // Return value
781 *fOut << int8_t(BinaryConsts::Return);
782
783 // Generate end
784 *fOut << int8_t(BinaryConsts::End);
785 size_t size = fOut->size() - start;
786 fOut->writeAt(size_pos, U32LEB(uint32_t(size)));
787 }
788
generateJSON(const string & json)789 void generateJSON(const string& json)
790 {
791 // One data segment only
792 int data_segment_num = 1;
793 int32_t start = startSection(BinaryConsts::Section::Data);
794 *fOut << U32LEB(data_segment_num);
795 // For each segment (= 1 here)
796 // Linear memory 0 in the MVP
797 *fOut << U32LEB(0);
798 // Offset defined as an 'initializer expression' is 0
799 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(0);
800 *fOut << int8_t(BinaryConsts::End);
801 // Write the JSON string
802 size_t size = json.size();
803 *fOut << U32LEB(uint32_t(json.size()));
804 for (size_t i = 0; i < size; i++) {
805 *fOut << int8_t(json[i]);
806 }
807 finishSection(start);
808 }
809
visit(AddSoundfileInst * inst)810 virtual void visit(AddSoundfileInst* inst)
811 {
812 // Not supported for now
813 throw faustexception("ERROR : 'soundfile' primitive not yet supported for wasm\n");
814 }
815
visit(DeclareVarInst * inst)816 virtual void visit(DeclareVarInst* inst)
817 {
818 bool is_struct = (inst->fAddress->getAccess() & Address::kStruct)
819 || (inst->fAddress->getAccess() & Address::kStaticStruct);
820
821 ArrayTyped* array_typed = dynamic_cast<ArrayTyped*>(inst->fType);
822
823 // fSampleRate may appear several time (in subcontainers and in main DSP)
824 string name = inst->fAddress->getName();
825 if (name != "fSampleRate") {
826 faustassert(fFieldTable.find(name) == fFieldTable.end());
827 }
828
829 if (array_typed && array_typed->fSize > 1) {
830 if (is_struct) {
831 fFieldTable[name] = MemoryDesc(-1, fStructOffset, array_typed->fSize, array_typed->fType->getType());
832 // Always use biggest size so that int/real access are correctly aligned
833 fStructOffset += (array_typed->fSize * gGlobal->audioSampleSize());
834 } else {
835 // Local variables declared by [var_num, type] pairs, separated as (local, set_local instruction)
836 }
837 } else {
838 if (is_struct) {
839 fFieldTable[name] = MemoryDesc(-1, fStructOffset, 1, inst->fType->getType());
840 // Always use biggest size so that int/real access are correctly aligned
841 fStructOffset += gGlobal->audioSampleSize();
842 } else {
843 // Local variables declared by [var_num, type] pairs, separated as (local, set_local instruction)
844 faustassert(inst->fValue == nullptr);
845 }
846 }
847 }
848
visit(RetInst * inst)849 virtual void visit(RetInst* inst)
850 {
851 if (inst->fResult) {
852 inst->fResult->accept(this);
853 *fOut << int8_t(BinaryConsts::Return);
854 }
855 }
856
857 // Function type definition is done first with FunAndTypeCounter, then the function body is generated here
visit(DeclareFunInst * inst)858 virtual void visit(DeclareFunInst* inst)
859 {
860 // Already generated
861 if (fFunctionSymbolTable.find(inst->fName) != fFunctionSymbolTable.end()) {
862 return;
863 } else {
864 fFunctionSymbolTable[inst->fName] = true;
865 }
866
867 // Generate function body
868 size_t size_pos = fOut->writeU32LEBPlaceholder();
869 size_t start = fOut->size();
870
871 // Generate locals
872 LocalVariableCounter local_counter;
873 inst->accept(&local_counter);
874 local_counter.generateStackMap(fOut);
875 // local_counter.dump();
876 setLocalVarTable(local_counter.fLocalVarTable);
877
878 inst->fCode->accept(this);
879
880 // Generate end
881 *fOut << int8_t(BinaryConsts::End);
882 size_t size = fOut->size() - start;
883 fOut->writeAt(size_pos, U32LEB(uint32_t(size)));
884 }
885
visit(LoadVarInst * inst)886 virtual void visit(LoadVarInst* inst)
887 {
888 fTypingVisitor.visit(inst);
889 Typed::VarType type = fTypingVisitor.fCurType;
890 Address::AccessType access = inst->fAddress->getAccess();
891 string name = inst->fAddress->getName();
892 IndexedAddress* indexed = dynamic_cast<IndexedAddress*>(inst->fAddress);
893
894 if (access & Address::kStruct || access & Address::kStaticStruct || indexed) {
895
896 int offset;
897 if ((offset = getConstantOffset(inst->fAddress)) > 0) {
898 // Generate 0
899 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(0);
900 } else {
901 // Otherwise generate address expression
902 inst->fAddress->accept(this);
903 }
904 if (isRealType(type)) {
905 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::F32LoadMem)
906 : int8_t(BinaryConsts::F64LoadMem));
907 } else if (isInt64Type(type)) {
908 *fOut << int8_t(BinaryConsts::I64LoadMem);
909 } else if (isInt32Type(type) || isPtrType(type)) {
910 *fOut << int8_t(BinaryConsts::I32LoadMem);
911 } else {
912 faustassert(false);
913 }
914 // Possibly used offset (if > 0)
915 generateMemoryAccess(offset);
916
917 } else {
918 faustassert(fLocalVarTable.find(name) != fLocalVarTable.end());
919 LocalVarDesc local = fLocalVarTable[name];
920 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(local.fIndex);
921 }
922 }
923
visit(TeeVarInst * inst)924 virtual void visit(TeeVarInst* inst)
925 {
926 string name = inst->fAddress->getName();
927
928 faustassert(fLocalVarTable.find(name) != fLocalVarTable.end());
929 LocalVarDesc local = fLocalVarTable[name];
930
931 // 'tee_local' is generated the first time the variable is used
932 // All future access simply use a local.get
933 if (fTeeMap.find(name) == fTeeMap.end()) {
934 inst->fValue->accept(this);
935 *fOut << int8_t(BinaryConsts::LocalTee) << U32LEB(local.fIndex);
936 fTeeMap[name] = true;
937 } else {
938 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(local.fIndex);
939 }
940 }
941
visit(StoreVarInst * inst)942 virtual void visit(StoreVarInst* inst)
943 {
944 inst->fValue->accept(&fTypingVisitor);
945 Typed::VarType type = fTypingVisitor.fCurType;
946 string name = inst->fAddress->getName();
947
948 if (inst->fAddress->getAccess() & Address::kStruct || inst->fAddress->getAccess() & Address::kStaticStruct ||
949 dynamic_cast<IndexedAddress*>(inst->fAddress)) {
950 int offset;
951 if ((offset = getConstantOffset(inst->fAddress)) > 0) {
952 // Generate 0
953 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(0);
954 } else {
955 // Otherwise generate address expression
956 inst->fAddress->accept(this);
957 }
958 inst->fValue->accept(this);
959 if (isRealType(type) || isRealPtrType(type)) {
960 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::F32StoreMem)
961 : int8_t(BinaryConsts::F64StoreMem));
962 } else if (isInt64Type(type)) {
963 *fOut << int8_t(BinaryConsts::I64StoreMem);
964 } else if (isInt32Type(type) || isPtrType(type) || isBoolType(type)) {
965 *fOut << int8_t(BinaryConsts::I32StoreMem);
966 } else {
967 faustassert(false);
968 }
969 // Possibly used offset (if > 0)
970 generateMemoryAccess(offset);
971
972 } else {
973 faustassert(fLocalVarTable.find(name) != fLocalVarTable.end());
974 LocalVarDesc local = fLocalVarTable[name];
975 inst->fValue->accept(this);
976 *fOut << int8_t(BinaryConsts::LocalSet) << U32LEB(local.fIndex);
977 }
978 }
979
visit(NamedAddress * named)980 virtual void visit(NamedAddress* named)
981 {
982 if (named->getAccess() & Address::kStruct || named->getAccess() & Address::kStaticStruct) {
983 faustassert(fFieldTable.find(named->getName()) != fFieldTable.end());
984 MemoryDesc tmp = fFieldTable[named->getName()];
985 if (fFastMemory) {
986 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(tmp.fOffset);
987 } else {
988 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(0); // Assuming $dsp is at 0 local variable index
989 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(tmp.fOffset);
990 *fOut << int8_t(WasmOp::I32Add);
991 }
992 } else {
993 faustassert(fLocalVarTable.find(named->getName()) != fLocalVarTable.end());
994 LocalVarDesc local = fLocalVarTable[named->getName()];
995 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(local.fIndex);
996 }
997 }
998
visit(IndexedAddress * indexed)999 virtual void visit(IndexedAddress* indexed)
1000 {
1001 // TO CHECK : size of memory ptr ?
1002
1003 // HACK : completely adhoc code for inputs/outputs...
1004 if ((startWith(indexed->getName(), "inputs") || startWith(indexed->getName(), "outputs"))) {
1005 // Since indexed->fIndex is always a known constant value, offset can be directly generated
1006 Int32NumInst* num = dynamic_cast<Int32NumInst*>(indexed->fIndex);
1007 faustassert(num);
1008 // "inputs" is 'compute' method third parameter, so with index 2
1009 // "outputs" is 'compute' method fourth parameter, so with index 3
1010 *fOut << int8_t(BinaryConsts::LocalGet)
1011 << ((startWith(indexed->getName(), "inputs")) ? U32LEB(2) : U32LEB(3));
1012 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(num->fNum << 2);
1013 *fOut << int8_t(WasmOp::I32Add);
1014 // HACK : completely adhoc code for input/output...
1015 } else if ((startWith(indexed->getName(), "input") || startWith(indexed->getName(), "output"))) {
1016 faustassert(fLocalVarTable.find(indexed->getName()) != fLocalVarTable.end());
1017 LocalVarDesc local = fLocalVarTable[indexed->getName()];
1018 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(local.fIndex);
1019 indexed->fIndex->accept(this);
1020 // If 'i' loop variable moves in bytes, save index code generation of input/output
1021 if (gGlobal->gLoopVarInBytes) {
1022 *fOut << int8_t(WasmOp::I32Add);
1023 } else {
1024 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB((fSubContainerType == kInt) ? 2 : offStrNum);
1025 *fOut << int8_t(WasmOp::I32Shl);
1026 *fOut << int8_t(WasmOp::I32Add);
1027 }
1028 } else {
1029 /*
1030 Fields in DSP struct are accessed using 'dsp' and an offset
1031 IndexedAddress is also used for soundfiles (pointer + field index)
1032 */
1033 if (fFieldTable.find(indexed->getName()) != fFieldTable.end()) {
1034 MemoryDesc tmp = fFieldTable[indexed->getName()];
1035 Int32NumInst* num;
1036 if ((num = dynamic_cast<Int32NumInst*>(indexed->fIndex))) {
1037 // Index can be computed at compile time
1038 if (fFastMemory) {
1039 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB((tmp.fOffset + (num->fNum << offStrNum)));
1040 } else {
1041 *fOut << int8_t(BinaryConsts::LocalGet)
1042 << U32LEB(0); // Assuming $dsp is at 0 local variable index
1043 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB((tmp.fOffset + (num->fNum << offStrNum)));
1044 *fOut << int8_t(WasmOp::I32Add);
1045 }
1046 } else {
1047 // Otherwise generate index computation code
1048 if (fFastMemory) {
1049 // Micro optimization if the field is actually the first one in the structure
1050 if (tmp.fOffset == 0) {
1051 indexed->fIndex->accept(this);
1052 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(offStrNum);
1053 *fOut << int8_t(WasmOp::I32Shl);
1054 } else {
1055 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(tmp.fOffset);
1056 indexed->fIndex->accept(this);
1057 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(offStrNum);
1058 *fOut << int8_t(WasmOp::I32Shl);
1059 *fOut << int8_t(WasmOp::I32Add);
1060 }
1061 } else {
1062 // Micro optimization if the field is actually the first one in the structure
1063 if (tmp.fOffset == 0) {
1064 *fOut << int8_t(BinaryConsts::LocalGet)
1065 << U32LEB(0); // Assuming $dsp is at 0 local variable index
1066 indexed->fIndex->accept(this);
1067 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(offStrNum);
1068 *fOut << int8_t(WasmOp::I32Shl);
1069 *fOut << int8_t(WasmOp::I32Add);
1070 } else {
1071 *fOut << int8_t(BinaryConsts::LocalGet)
1072 << U32LEB(0); // Assuming $dsp is at 0 local variable index
1073 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(tmp.fOffset);
1074 indexed->fIndex->accept(this);
1075 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(offStrNum);
1076 *fOut << int8_t(WasmOp::I32Shl);
1077 *fOut << int8_t(WasmOp::I32Add);
1078 *fOut << int8_t(WasmOp::I32Add);
1079 }
1080 }
1081 }
1082 } else {
1083 // Local variable
1084 LocalVarDesc local = fLocalVarTable[indexed->getName()];
1085 Int32NumInst* num;
1086 if ((num = dynamic_cast<Int32NumInst*>(indexed->fIndex))) {
1087 // Hack for 'soundfile'
1088 DeclareStructTypeInst* struct_type = isStructType(indexed->getName());
1089 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(local.fIndex);
1090 if (struct_type) {
1091 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(struct_type->fType->getOffset(num->fNum));
1092 } else {
1093 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(num->fNum << offStrNum);
1094 }
1095 *fOut << int8_t(WasmOp::I32Add);
1096 } else {
1097 *fOut << int8_t(BinaryConsts::LocalGet) << U32LEB(local.fIndex);
1098 indexed->fIndex->accept(this);
1099 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(offStrNum);
1100 *fOut << int8_t(WasmOp::I32Shl);
1101 *fOut << int8_t(WasmOp::I32Add);
1102 }
1103 }
1104 }
1105 }
1106
visit(LoadVarAddressInst * inst)1107 virtual void visit(LoadVarAddressInst* inst)
1108 {
1109 // Not implemented in WASM
1110 faustassert(false);
1111 }
1112
visit(FloatNumInst * inst)1113 virtual void visit(FloatNumInst* inst)
1114 {
1115 fTypingVisitor.visit(inst);
1116 *fOut << int8_t(BinaryConsts::F32Const) << inst->fNum;
1117 }
1118
visit(DoubleNumInst * inst)1119 virtual void visit(DoubleNumInst* inst)
1120 {
1121 fTypingVisitor.visit(inst);
1122 *fOut << int8_t(BinaryConsts::F64Const) << inst->fNum;
1123 }
1124
visit(BoolNumInst * inst)1125 virtual void visit(BoolNumInst* inst) { faustassert(false); }
1126
visit(Int32NumInst * inst)1127 virtual void visit(Int32NumInst* inst)
1128 {
1129 fTypingVisitor.visit(inst);
1130 *fOut << int8_t(BinaryConsts::I32Const) << S32LEB(inst->fNum);
1131 }
1132
visit(Int64NumInst * inst)1133 virtual void visit(Int64NumInst* inst)
1134 {
1135 fTypingVisitor.visit(inst);
1136 *fOut << int8_t(BinaryConsts::I64Const) << S64LEB(inst->fNum);
1137 }
1138
1139 // Numerical computation
visitAuxInt(BinopInst * inst,Typed::VarType type)1140 void visitAuxInt(BinopInst* inst, Typed::VarType type)
1141 {
1142 inst->fInst1->accept(this);
1143 inst->fInst2->accept(this);
1144 if (isInt32Type(type) || isBoolType(type)) {
1145 *fOut << int8_t(gBinOpTable[inst->fOpcode]->fWasmInt32);
1146 } else if (isInt64Type(type)) {
1147 *fOut << int8_t(gBinOpTable[inst->fOpcode]->fWasmInt64);
1148 } else {
1149 faustassert(false);
1150 }
1151 }
1152
visitAuxReal(BinopInst * inst,Typed::VarType type)1153 void visitAuxReal(BinopInst* inst, Typed::VarType type)
1154 {
1155 inst->fInst1->accept(this);
1156 inst->fInst2->accept(this);
1157 if (isFloatType(type)) {
1158 *fOut << int8_t(gBinOpTable[inst->fOpcode]->fWasmFloat);
1159 } else if (isDoubleType(type)) {
1160 *fOut << int8_t(gBinOpTable[inst->fOpcode]->fWasmDouble);
1161 } else {
1162 faustassert(false);
1163 }
1164 }
1165
visit(BinopInst * inst)1166 virtual void visit(BinopInst* inst)
1167 {
1168 inst->fInst1->accept(&fTypingVisitor);
1169 Typed::VarType type1 = fTypingVisitor.fCurType;
1170
1171 if (isRealType(type1)) {
1172 visitAuxReal(inst, type1);
1173 } else {
1174 // type1 is kInt
1175 inst->fInst2->accept(&fTypingVisitor);
1176 Typed::VarType type2 = fTypingVisitor.fCurType;
1177 if (isRealType(type2)) {
1178 visitAuxReal(inst, type2);
1179 } else if (isIntType(type1) || isIntType(type2)) {
1180 visitAuxInt(inst, type2);
1181 } else if (isBoolType(type1) && isBoolType(type2)) {
1182 visitAuxInt(inst, type1);
1183 } else {
1184 // Should never happen...
1185 faustassert(false);
1186 }
1187 }
1188
1189 fTypingVisitor.visit(inst);
1190 }
1191
visit(::CastInst * inst)1192 virtual void visit(::CastInst* inst)
1193 {
1194 inst->fInst->accept(&fTypingVisitor);
1195 Typed::VarType type = fTypingVisitor.fCurType;
1196
1197 switch (inst->fType->getType()) {
1198 case Typed::kInt32:
1199 if (isInt32Type(type)) {
1200 // std::cout << "CastInst : cast to int, but arg already int !" << std::endl;
1201 inst->fInst->accept(this);
1202 } else if (isInt64Type(type)) {
1203 inst->fInst->accept(this);
1204 *fOut << int8_t(BinaryConsts::I32WrapI64);
1205 } else {
1206 inst->fInst->accept(this);
1207 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::I32STruncF32)
1208 : int8_t(BinaryConsts::I32STruncF64));
1209 }
1210 break;
1211
1212 case Typed::kInt64:
1213 faustassert(false);
1214 break;
1215
1216 case Typed::kFloat:
1217 case Typed::kDouble:
1218 if (isRealType(type)) {
1219 // std::cout << "CastInst : cast to real, but arg already real !" << std::endl;
1220 inst->fInst->accept(this);
1221 } else if (isInt64Type(type)) {
1222 inst->fInst->accept(this);
1223 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::F32SConvertI64)
1224 : int8_t(BinaryConsts::F64SConvertI64));
1225 } else if (isInt32Type(type) || isBoolType(type)) {
1226 inst->fInst->accept(this);
1227 *fOut << ((gGlobal->gFloatSize == 1) ? int8_t(BinaryConsts::F32SConvertI32)
1228 : int8_t(BinaryConsts::F64SConvertI32));
1229 } else {
1230 faustassert(false);
1231 }
1232 break;
1233
1234 default:
1235 faustassert(false);
1236 break;
1237 }
1238
1239 fTypingVisitor.visit(inst);
1240 }
1241
visit(BitcastInst * inst)1242 virtual void visit(BitcastInst* inst)
1243 {
1244 inst->fInst->accept(this);
1245
1246 switch (inst->fType->getType()) {
1247 case Typed::kInt32:
1248 *fOut << int8_t(BinaryConsts::I32ReinterpretF32);
1249 break;
1250 case Typed::kInt64:
1251 *fOut << int8_t(BinaryConsts::I64ReinterpretF64);
1252 break;
1253 case Typed::kFloat:
1254 *fOut << int8_t(BinaryConsts::F32ReinterpretI32);
1255 break;
1256 case Typed::kDouble:
1257 *fOut << int8_t(BinaryConsts::F64ReinterpretI64);
1258 break;
1259 default:
1260 faustassert(false);
1261 break;
1262 }
1263
1264 fTypingVisitor.visit(inst);
1265 }
1266
1267 // Special case for min/max
generateMinMax(const list<ValueInst * > & args,const string & name)1268 void generateMinMax(const list<ValueInst*>& args, const string& name)
1269 {
1270 list<ValueInst*>::iterator it;
1271 ValueInst* arg1 = *(args.begin());
1272 arg1->accept(&fTypingVisitor);
1273 if (isIntType(fTypingVisitor.fCurType)) {
1274 // Using manually generated min/max
1275 *fOut << int8_t(BinaryConsts::CallFunction) << U32LEB(fFunAndTypeCounter.getFunctionIndex(name));
1276
1277 } else {
1278 faustassert(fMathLibTable.find(name) != fMathLibTable.end());
1279 MathFunDesc desc = fMathLibTable[name];
1280 *fOut << int8_t(desc.fWasmOp);
1281 }
1282 }
1283
1284 // Generate standard funcall (not 'method' like funcall...)
visit(FunCallInst * inst)1285 virtual void visit(FunCallInst* inst)
1286 {
1287 // Compile args first
1288 for (const auto& it : inst->fArgs) {
1289 it->accept(this);
1290 }
1291
1292 // Then compile funcall
1293 if (fMathLibTable.find(inst->fName) != fMathLibTable.end()) {
1294 MathFunDesc desc = fMathLibTable[inst->fName];
1295 if (desc.fMode == MathFunDesc::Gen::kWAS) {
1296 // Special case for min/max
1297 if (checkMinMax(desc.fName)) {
1298 generateMinMax(inst->fArgs, inst->fName);
1299 } else {
1300 *fOut << int8_t(desc.fWasmOp);
1301 }
1302 } else {
1303 *fOut << int8_t(BinaryConsts::CallFunction) << U32LEB(fFunAndTypeCounter.getFunctionIndex(inst->fName));
1304 }
1305 } else {
1306 *fOut << int8_t(BinaryConsts::CallFunction) << U32LEB(fFunAndTypeCounter.getFunctionIndex(inst->fName));
1307 }
1308 }
1309
1310 // Select that computes both branches
1311 /*
1312 virtual void visit(Select2Inst* inst)
1313 {
1314 inst->fThen->accept(this);
1315 inst->fElse->accept(this);
1316 // Condition is last item
1317 inst->fCond->accept(this);
1318 // Possibly convert i64 to i32
1319 inst->fCond->accept(&fTypingVisitor);
1320 if (isInt64Type(fTypingVisitor.fCurType)) {
1321 // Compare to 0
1322 *fOut << int8_t(BinaryConsts::I64Const) << S32LEB(0);
1323 *fOut << int8_t(WasmOp::I64Ne);
1324 }
1325 *fOut << int8_t(BinaryConsts::Select);
1326
1327 fTypingVisitor.visit(inst);
1328 }
1329 */
1330
1331 // Select that only computes one branch
visit(Select2Inst * inst)1332 virtual void visit(Select2Inst* inst)
1333 {
1334 // Condition is first item
1335 inst->fCond->accept(this);
1336 // Possibly convert i64 to i32
1337 inst->fCond->accept(&fTypingVisitor);
1338 if (isInt64Type(fTypingVisitor.fCurType)) {
1339 // Compare to 0
1340 *fOut << int8_t(BinaryConsts::I64Const) << S32LEB(0);
1341 *fOut << int8_t(WasmOp::I64Ne);
1342 }
1343 // Result type
1344 inst->fThen->accept(&fTypingVisitor);
1345 *fOut << int8_t(BinaryConsts::If) << S32LEB(type2Binary(fTypingVisitor.fCurType));
1346 // Compile 'then'
1347 inst->fThen->accept(this);
1348 // Compile 'else'
1349 *fOut << int8_t(BinaryConsts::Else);
1350 inst->fElse->accept(this);
1351 // End of if
1352 *fOut << int8_t(BinaryConsts::End);
1353
1354 fTypingVisitor.visit(inst);
1355 }
1356
1357 // Conditional : if (TO CHECK : utilise drop ?)
visit(IfInst * inst)1358 virtual void visit(IfInst* inst)
1359 {
1360 inst->fCond->accept(this);
1361 // Possibly convert i64 to i32
1362 inst->fCond->accept(&fTypingVisitor);
1363 if (isInt64Type(fTypingVisitor.fCurType)) {
1364 // Compare to 0
1365 *fOut << int8_t(BinaryConsts::I64Const) << S32LEB(0);
1366 *fOut << int8_t(WasmOp::I64Ne);
1367 }
1368 *fOut << int8_t(BinaryConsts::If) << S32LEB(BinaryConsts::Empty);
1369 inst->fThen->accept(this);
1370 if (inst->fElse->fCode.size() > 0) {
1371 *fOut << int8_t(BinaryConsts::Else);
1372 inst->fElse->accept(this);
1373 }
1374 // End of if
1375 *fOut << int8_t(BinaryConsts::End);
1376
1377 fTypingVisitor.visit(inst);
1378 }
1379
1380 // Loop : beware: compiled loop don't work with an index of 0
visit(ForLoopInst * inst)1381 virtual void visit(ForLoopInst* inst)
1382 {
1383 // Don't generate empty loops...
1384 if (inst->fCode->size() == 0) return;
1385
1386 // Init loop counter
1387 inst->fInit->accept(this);
1388
1389 // Loop block
1390 *fOut << int8_t(BinaryConsts::Loop) << S32LEB(BinaryConsts::Empty);
1391
1392 // Loop body block
1393 *fOut << int8_t(BinaryConsts::Block) << S32LEB(BinaryConsts::Empty);
1394
1395 // Loop code code
1396 inst->fCode->accept(this);
1397
1398 // Loop counter increment
1399 inst->fIncrement->accept(this);
1400
1401 // Loop counter test and possibly branch out
1402 inst->fEnd->accept(this);
1403 *fOut << int8_t(BinaryConsts::If) << S32LEB(BinaryConsts::Empty);
1404 // Branch to loop
1405 *fOut << int8_t(BinaryConsts::Br) << U32LEB(2);
1406 // Branch out
1407 *fOut << int8_t(BinaryConsts::Br) << U32LEB(1);
1408 // End of if
1409 *fOut << int8_t(BinaryConsts::End);
1410
1411 // End of body block
1412 *fOut << int8_t(BinaryConsts::End);
1413
1414 // End of loop block
1415 *fOut << int8_t(BinaryConsts::End);
1416 }
1417
1418 };
1419
1420 #endif
1421