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 _FIR_INTERPRETER_BYTECODE_H
23 #define _FIR_INTERPRETER_BYTECODE_H
24 
25 #include <math.h>
26 #include <iostream>
27 #include <string>
28 #include <vector>
29 
30 #include "exception.hh"
31 #include "faust/gui/PathBuilder.h"
32 #include "fbc_opcode.hh"
33 
quote1(std::string str)34 static inline std::string quote1(std::string str)
35 {
36     return "\"" + str + "\"";
37 }
38 
unquote1(const std::string & str)39 static inline std::string unquote1(const std::string& str)
40 {
41     return (str[0] == '"') ? str.substr(1, str.size() - 2) : str;
42 }
43 
44 // Bytecode definition
45 
46 template <class REAL>
47 struct FBCBlockInstruction;
48 
49 template <class REAL>
50 struct FBCBasicInstruction : public FBCInstruction {
51     std::string fName;
52     Opcode      fOpcode;
53     int         fIntValue;
54     REAL        fRealValue;
55     int         fOffset1;
56     int         fOffset2;
57 
58     FBCBlockInstruction<REAL>* fBranch1;
59     FBCBlockInstruction<REAL>* fBranch2;
60 
FBCBasicInstructionFBCBasicInstruction61     FBCBasicInstruction(Opcode opcode, const std::string& name, int val_int, REAL val_real, int off1, int off2,
62                         FBCBlockInstruction<REAL>* branch1, FBCBlockInstruction<REAL>* branch2)
63         : fName(""),
64           fOpcode(opcode),
65           fIntValue(val_int),
66           fRealValue(val_real),
67           fOffset1(off1),
68           fOffset2(off2),
69           fBranch1(branch1),
70           fBranch2(branch2)
71     {
72     }
73 
FBCBasicInstructionFBCBasicInstruction74     FBCBasicInstruction(Opcode opcode, int val_int, REAL val_real)
75         : fName(""),
76           fOpcode(opcode),
77           fIntValue(val_int),
78           fRealValue(val_real),
79           fOffset1(-1),
80           fOffset2(-1),
81           fBranch1(nullptr),
82           fBranch2(nullptr)
83     {
84     }
85 
FBCBasicInstructionFBCBasicInstruction86     FBCBasicInstruction(Opcode opcode, int val_int, REAL val_real, int off1, int off2)
87         : fName(""),
88           fOpcode(opcode),
89           fIntValue(val_int),
90           fRealValue(val_real),
91           fOffset1(off1),
92           fOffset2(off2),
93           fBranch1(nullptr),
94           fBranch2(nullptr)
95     {
96     }
97 
FBCBasicInstructionFBCBasicInstruction98     FBCBasicInstruction(Opcode opcode, const std::string& name, int val_int, REAL val_real, int off1, int off2)
99         : fName(name),
100           fOpcode(opcode),
101           fIntValue(val_int),
102           fRealValue(val_real),
103           fOffset1(off1),
104           fOffset2(off2),
105           fBranch1(nullptr),
106           fBranch2(nullptr)
107     {
108     }
109 
FBCBasicInstructionFBCBasicInstruction110     FBCBasicInstruction(Opcode opcode)
111         : fName(""),
112           fOpcode(opcode),
113           fIntValue(0),
114           fRealValue(0),
115           fOffset1(-1),
116           fOffset2(-1),
117           fBranch1(nullptr),
118           fBranch2(nullptr)
119     {
120     }
121 
FBCBasicInstructionFBCBasicInstruction122     FBCBasicInstruction()
123         : fName(""),
124           fOpcode(FBCInstruction::kNop),
125           fIntValue(0),
126           fRealValue(0),
127           fOffset1(-1),
128           fOffset2(-1),
129           fBranch1(nullptr),
130           fBranch2(nullptr)
131     {
132     }
133 
getBranch1FBCBasicInstruction134     FBCBlockInstruction<REAL>* getBranch1() { return (fOpcode == kCondBranch) ? nullptr : fBranch1; }
getBranch2FBCBasicInstruction135     FBCBlockInstruction<REAL>* getBranch2() { return fBranch2; }
136 
~FBCBasicInstructionFBCBasicInstruction137     virtual ~FBCBasicInstruction()
138     {
139         delete getBranch1();
140         delete getBranch2();
141     }
142 
sizeFBCBasicInstruction143     int size()
144     {
145         int branches =
146             std::max(((getBranch1()) ? getBranch1()->size() : 0), ((getBranch2()) ? getBranch2()->size() : 0));
147         return (branches > 0) ? branches : 1;
148     }
149 
writeFBCBasicInstruction150     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
151     {
152         if (small) {
153             *out << "o " << fOpcode << " k "
154                  << " i " << fIntValue << " r " << fRealValue << " o " << fOffset1 << " o " << fOffset2 << std::endl;
155         } else {
156             *out << "opcode " << fOpcode << " " << gFBCInstructionTable[fOpcode] << " int " << fIntValue << " real "
157                  << fRealValue << " offset1 " << fOffset1 << " offset2 " << fOffset2;
158             if (this->fName != "") {
159                 *out << " name " << this->fName;
160             }
161             *out << std::endl;
162         }
163         // If select/if/loop : write branches
164         if (recurse && getBranch1()) {
165             getBranch1()->write(out, binary, small, recurse);
166         }
167         if (recurse && getBranch2()) {
168             getBranch2()->write(out, binary, small, recurse);
169         }
170     }
171 
copyFBCBasicInstruction172     virtual FBCBasicInstruction<REAL>* copy()
173     {
174         return new FBCBasicInstruction<REAL>(fOpcode, fName, fIntValue, fRealValue, fOffset1, fOffset2,
175                                             ((getBranch1()) ? getBranch1()->copy() : nullptr),
176                                             ((getBranch2()) ? getBranch2()->copy() : nullptr));
177     }
178 };
179 
180 template <class REAL>
181 struct FIRBlockStoreRealInstruction : public FBCBasicInstruction<REAL> {
182     std::vector<REAL> fNumTable;
183 
FIRBlockStoreRealInstructionFIRBlockStoreRealInstruction184     FIRBlockStoreRealInstruction(FBCInstruction::Opcode opcode, int offset1, int offset2,
185                                  const std::vector<REAL>& numtable)
186     {
187         this->fOpcode    = opcode;
188         this->fIntValue  = 0;
189         this->fRealValue = 0;
190         this->fOffset1   = offset1;
191         this->fOffset2   = offset2;
192         this->fBranch1   = nullptr;
193         this->fBranch2   = nullptr;
194         this->fNumTable  = numtable;
195     }
196 
copyFIRBlockStoreRealInstruction197     virtual FIRBlockStoreRealInstruction<REAL>* copy()
198     {
199         return new FIRBlockStoreRealInstruction<REAL>(this->fOpcode, this->fOffset1, this->fOffset2, this->fNumTable);
200     }
201 
writeFIRBlockStoreRealInstruction202     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
203     {
204         if (small) {
205             *out << "o " << this->fOpcode << " k "
206                  << " o " << this->fOffset1 << " o " << this->fOffset2 << " s " << this->fNumTable.size() << std::endl;
207         } else {
208             *out << "opcode " << this->fOpcode << " " << gFBCInstructionTable[this->fOpcode] << " offset1 "
209                  << this->fOffset1 << " offset2 " << this->fOffset2 << " size " << this->fNumTable.size();
210             if (this->fName != "") {
211                 *out << " name " << this->fName;
212             }
213             *out << std::endl;
214         }
215         for (size_t i = 0; i < fNumTable.size(); i++) {
216             *out << this->fNumTable[i] << " ";
217         }
218         *out << std::endl;
219     }
220 };
221 
222 template <class REAL>
223 struct FIRBlockStoreIntInstruction : public FBCBasicInstruction<REAL> {
224     std::vector<int> fNumTable;
225 
FIRBlockStoreIntInstructionFIRBlockStoreIntInstruction226     FIRBlockStoreIntInstruction(FBCInstruction::Opcode opcode, int offset1, int offset2,
227                                 const std::vector<int>& numtable)
228     {
229         this->fOpcode    = opcode;
230         this->fIntValue  = 0;
231         this->fRealValue = 0;
232         this->fOffset1   = offset1;
233         this->fOffset2   = offset2;
234         this->fBranch1   = nullptr;
235         this->fBranch2   = nullptr;
236         this->fNumTable  = numtable;
237     }
238 
copyFIRBlockStoreIntInstruction239     virtual FIRBlockStoreIntInstruction<REAL>* copy()
240     {
241         return new FIRBlockStoreIntInstruction<REAL>(this->fOpcode, this->fOffset1, this->fOffset2, this->fNumTable);
242     }
243 
writeFIRBlockStoreIntInstruction244     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
245     {
246         if (small) {
247             *out << "o " << this->fOpcode << " k "
248                  << " o " << this->fOffset1 << " o " << this->fOffset2 << " s " << this->fNumTable.size() << std::endl;
249         } else {
250             *out << "opcode " << this->fOpcode << " " << gFBCInstructionTable[this->fOpcode] << " offset1 "
251                  << this->fOffset1 << " offset2 " << this->fOffset2 << " size " << this->fNumTable.size();
252             if (this->fName != "") {
253                 *out << " name " << this->fName;
254             }
255             *out << std::endl;
256         }
257         for (size_t i = 0; i < fNumTable.size(); i++) {
258             *out << this->fNumTable[i] << " ";
259         }
260         *out << std::endl;
261     }
262 };
263 
264 template <class REAL>
265 struct FIRUserInterfaceInstruction : public FBCInstruction {
266     Opcode      fOpcode;
267     int         fOffset;
268     std::string fLabel;
269     std::string fKey;
270     std::string fValue;
271     REAL        fInit;
272     REAL        fMin;
273     REAL        fMax;
274     REAL        fStep;
275 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction276     FIRUserInterfaceInstruction(Opcode opcode, int offset, const std::string& label, const std::string& key,
277                                 const std::string& value, REAL init, REAL min, REAL max, REAL step)
278         : fOpcode(opcode),
279           fOffset(offset),
280           fLabel(label),
281           fKey(key),
282           fValue(value),
283           fInit(init),
284           fMin(min),
285           fMax(max),
286           fStep(step)
287     {
288     }
289 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction290     FIRUserInterfaceInstruction(Opcode opcode, int offset, const std::string& label, REAL init, REAL min, REAL max, REAL step)
291         : fOpcode(opcode), fOffset(offset), fLabel(label), fInit(init), fMin(min), fMax(max), fStep(step)
292     {
293     }
294 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction295     FIRUserInterfaceInstruction(Opcode opcode)
296         : fOpcode(opcode), fOffset(-1), fLabel(""), fKey(""), fValue(""), fInit(0), fMin(0), fMax(0), fStep(0)
297     {
298     }
299 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction300     FIRUserInterfaceInstruction(Opcode opcode, const std::string& label)
301         : fOpcode(opcode), fOffset(-1), fLabel(label), fKey(""), fValue(""), fInit(0), fMin(0), fMax(0), fStep(0)
302     {
303     }
304 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction305     FIRUserInterfaceInstruction(Opcode opcode, int offset, const std::string& label)
306         : fOpcode(opcode), fOffset(offset), fLabel(label), fKey(""), fValue(""), fInit(0), fMin(0), fMax(0), fStep(0)
307     {
308     }
309 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction310     FIRUserInterfaceInstruction(Opcode opcode, int offset, const std::string& label, REAL min, REAL max)
311         : fOpcode(opcode),
312           fOffset(offset),
313           fLabel(label),
314           fKey(""),
315           fValue(""),
316           fInit(0),
317           fMin(min),
318           fMax(max),
319           fStep(0)
320     {
321     }
322 
FIRUserInterfaceInstructionFIRUserInterfaceInstruction323     FIRUserInterfaceInstruction(Opcode opcode, int offset, const std::string& key, const std::string& value)
324         : fOpcode(opcode), fOffset(offset), fLabel(""), fKey(key), fValue(value), fInit(0), fMin(0), fMax(0), fStep(0)
325     {
326     }
327 
~FIRUserInterfaceInstructionFIRUserInterfaceInstruction328     virtual ~FIRUserInterfaceInstruction() {}
329 
writeFIRUserInterfaceInstruction330     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
331     {
332         if (small) {
333             *out << "o " << fOpcode << " k "
334                  << " o " << fOffset << " l " << quote1(fLabel) << " k " << quote1(fKey) << " v " << quote1(fValue)
335                  << " i " << fInit << " m " << fMin << " m " << fMax << " s " << fStep << std::endl;
336         } else {
337             *out << "opcode " << fOpcode << " " << gFBCInstructionTable[fOpcode] << " offset " << fOffset << " label "
338                  << quote1(fLabel) << " key " << quote1(fKey) << " value " << quote1(fValue) << " init " << fInit
339                  << " min " << fMin << " max " << fMax << " step " << fStep << std::endl;
340         }
341     }
342 };
343 
344 struct FIRMetaInstruction : public FBCInstruction {
345     std::string fKey;
346     std::string fValue;
347 
FIRMetaInstructionFIRMetaInstruction348     FIRMetaInstruction(const std::string& key, const std::string& value) : fKey(key), fValue(value) {}
349 
~FIRMetaInstructionFIRMetaInstruction350     virtual ~FIRMetaInstruction() {}
351 
writeFIRMetaInstruction352     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
353     {
354         if (small) {
355             *out << "m"
356                  << " k " << quote1(fKey) << " v " << quote1(fValue) << std::endl;
357         } else {
358             *out << "meta"
359                  << " key " << quote1(fKey) << " value " << quote1(fValue) << std::endl;
360         }
361     }
362 };
363 
364 #define InstructionIT typename std::vector<FBCBasicInstruction<REAL>*>::iterator
365 #define UIInstructionIT typename std::vector<FIRUserInterfaceInstruction<REAL>*>::iterator
366 #define MetaInstructionIT std::vector<FIRMetaInstruction*>::iterator
367 
368 template <class REAL>
369 struct FIRUserInterfaceBlockInstruction : public FBCInstruction {
370     std::vector<FIRUserInterfaceInstruction<REAL>*> fInstructions;
371     std::map<std::string, int>                      fPathMap;
372 
FIRUserInterfaceBlockInstructionFIRUserInterfaceBlockInstruction373     FIRUserInterfaceBlockInstruction() {}
374 
~FIRUserInterfaceBlockInstructionFIRUserInterfaceBlockInstruction375     virtual ~FIRUserInterfaceBlockInstruction()
376     {
377         for (const auto& it : fInstructions) {
378             delete it;
379         }
380     }
381 
pushFIRUserInterfaceBlockInstruction382     void push(FIRUserInterfaceInstruction<REAL>* inst)
383     {
384         if (inst) fInstructions.push_back(inst);
385     }
386 
writeFIRUserInterfaceBlockInstruction387     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
388     {
389         *out << "block_size " << fInstructions.size() << std::endl;
390         for (const auto& it : fInstructions) {
391             it->write(out, binary, small, recurse);
392         }
393     }
394 
getPathMapFIRUserInterfaceBlockInstruction395     std::map<std::string, int>& getPathMap()
396     {
397         // Build the [path, offset] map
398         PathBuilder path_builder;
399 
400         for (const auto& it : fInstructions) {
401             switch (it->fOpcode) {
402                 case FBCInstruction::kOpenVerticalBox:
403                     path_builder.pushLabel(it->fLabel);
404                     break;
405 
406                 case FBCInstruction::kOpenHorizontalBox:
407                     path_builder.pushLabel(it->fLabel);
408                     break;
409 
410                 case FBCInstruction::kOpenTabBox:
411                     path_builder.pushLabel(it->fLabel);
412                     break;
413 
414                 case FBCInstruction::kCloseBox:
415                     path_builder.popLabel();
416                     break;
417 
418                 case FBCInstruction::kAddButton:
419                 case FBCInstruction::kAddCheckButton:
420                 case FBCInstruction::kAddHorizontalSlider:
421                 case FBCInstruction::kAddVerticalSlider:
422                 case FBCInstruction::kAddNumEntry:
423                     fPathMap[path_builder.buildPath(it->fLabel)] = it->fOffset;
424                     break;
425 
426                 default:
427                     break;
428             }
429         }
430 
431         return fPathMap;
432     }
433 
freezeDefaultValuesFIRUserInterfaceBlockInstruction434     void freezeDefaultValues(std::map<int, REAL>& real_map)
435     {
436         for (const auto& it : fInstructions) {
437             if (it->fOpcode == FBCInstruction::kAddButton
438                 || it->fOpcode == FBCInstruction::kAddCheckButton
439                 || it->fOpcode == FBCInstruction::kAddHorizontalSlider
440                 || it->fOpcode == FBCInstruction::kAddVerticalSlider
441                 || it->fOpcode == FBCInstruction::kAddNumEntry) {
442                 real_map[it->fOffset] = it->fInit;
443             }
444         }
445     }
446 
unFreezeValueFIRUserInterfaceBlockInstruction447     void unFreezeValue(std::map<int, REAL>& real_map, FBCInstruction::Opcode opcode)
448     {
449         for (const auto& it : fInstructions) {
450             if ((it->fOpcode == opcode) && real_map.find(it->fOffset) != real_map.end()) {
451                 real_map.erase(real_map.find(it->fOffset));
452             }
453         }
454     }
455 
unFreezeValueFIRUserInterfaceBlockInstruction456     void unFreezeValue(std::map<int, REAL>& real_map, const std::string& path)
457     {
458         if (fPathMap.find(path) != fPathMap.end()) {
459             real_map.erase(real_map.find(fPathMap[path]));
460         }
461     }
462 
unFreezeValueFIRUserInterfaceBlockInstruction463     void unFreezeValue(std::map<int, REAL>& real_map)
464     {
465         for (const auto& it : fInstructions) {
466             if (real_map.find(it->fOffset) != real_map.end()) {
467                 real_map.erase(real_map.find(it->fOffset));
468             }
469         }
470     }
471 };
472 
473 struct FIRMetaBlockInstruction : public FBCInstruction {
474     std::vector<FIRMetaInstruction*> fInstructions;
475 
~FIRMetaBlockInstructionFIRMetaBlockInstruction476     virtual ~FIRMetaBlockInstruction()
477     {
478         for (const auto& it : fInstructions) {
479             delete it;
480         }
481     }
482 
pushFIRMetaBlockInstruction483     void push(FIRMetaInstruction* inst)
484     {
485         if (inst) fInstructions.push_back(inst);
486     }
487 
writeFIRMetaBlockInstruction488     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
489     {
490         *out << "block_size " << fInstructions.size() << std::endl;
491         for (const auto& it : fInstructions) {
492             it->write(out, binary, small, recurse);
493         }
494     }
495 };
496 
497 template <class REAL>
498 struct FBCBlockInstruction : public FBCInstruction {
499     std::vector<FBCBasicInstruction<REAL>*> fInstructions;
500 
~FBCBlockInstructionFBCBlockInstruction501     virtual ~FBCBlockInstruction()
502     {
503         for (const auto& it : fInstructions) {
504             delete it;
505         }
506     }
507 
508     // Check block coherency
checkFBCBlockInstruction509     void check()
510     {
511         faustassert(fInstructions.back()->fOpcode == FBCInstruction::kReturn);
512     }
513 
pushFBCBlockInstruction514     void push(FBCBasicInstruction<REAL>* inst)
515     {
516         if (inst) fInstructions.push_back(inst);
517     }
518 
mergeFBCBlockInstruction519     void merge(FBCBlockInstruction<REAL>* block)
520     {
521         for (const auto& it : block->fInstructions) {
522             if (it->fOpcode != FBCInstruction::kReturn) {  // kReturn must be removed...
523                 fInstructions.push_back(it);
524             }
525         }
526     }
527 
writeFBCBlockInstruction528     virtual void write(std::ostream* out, bool binary = false, bool small = false, bool recurse = true)
529     {
530         *out << "block_size " << fInstructions.size() << std::endl;
531         for (const auto& it : fInstructions) {
532             it->write(out, binary, small, recurse);
533         }
534     }
535 
stackMoveFBCBlockInstruction536     void stackMove(int& int_index, int& real_index)
537     {
538         std::cout << "FBCBlockInstruction::stackMove" << std::endl;
539         int tmp_int_index  = 0;
540         int tmp_real_index = 0;
541         for (const auto& it : fInstructions) {
542             it->stackMove(tmp_int_index, tmp_real_index);
543             it->write(&std::cout);
544             std::cout << "int_stack_index " << tmp_int_index << " real_stack_index " << tmp_real_index << std::endl;
545             faustassert(tmp_int_index >= 0 && tmp_real_index >= 0);
546             int_index  = std::max(int_index, tmp_int_index);
547             real_index = std::max(real_index, tmp_real_index);
548         }
549     }
550 
copyFBCBlockInstruction551     virtual FBCBlockInstruction<REAL>* copy()
552     {
553         FBCBlockInstruction<REAL>* block = new FBCBlockInstruction<REAL>();
554         for (const auto& it : fInstructions) {
555             FBCBasicInstruction<REAL>* inst_copy = it->copy();
556             if (it->fOpcode == kCondBranch) {  // Special case for loops
557                 inst_copy->fBranch1 = block;
558             }
559             block->push(inst_copy);
560         }
561         return block;
562     }
563 
sizeFBCBlockInstruction564     int size()
565     {
566         int size = 0;
567         for (const auto& it : fInstructions) {
568             size += it->size();
569         }
570         return size;
571     }
572 
isRealInstFBCBlockInstruction573     bool isRealInst() { return isRealType(fInstructions.back()->fOpcode); }
574 };
575 
576 #endif
577