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