1 // Copyright 2014 Wouter van Oortmerssen. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "lobster/stdafx.h"
16 #include "lobster/disasm.h"
17 
18 namespace lobster {
19 
LookupLine(const int * ip,const int * code,const bytecode::BytecodeFile * bcf)20 const bytecode::LineInfo *LookupLine(const int *ip, const int *code,
21                                             const bytecode::BytecodeFile *bcf) {
22     auto lineinfo = bcf->lineinfo();
23     int pos = int(ip - code);
24     int start = 0;
25     auto size = lineinfo->size();
26     assert(size);
27     for (;;) {  // quick hardcoded binary search
28         if (size == 1) return lineinfo->Get(start);
29         auto nsize = size / 2;
30         if (lineinfo->Get(start + nsize)->bytecodestart() <= pos) {
31             start += nsize;
32             size -= nsize;
33         } else {
34             size = nsize;
35         }
36     }
37 }
38 
DisAsmIns(NativeRegistry & nfr,ostringstream & ss,const int * ip,const int * code,const type_elem_t * typetable,const bytecode::BytecodeFile * bcf)39 const int *DisAsmIns(NativeRegistry &nfr, ostringstream &ss, const int *ip, const int *code,
40                             const type_elem_t *typetable, const bytecode::BytecodeFile *bcf) {
41     auto ilnames = ILNames();
42     auto ilarity = ILArity();
43     auto li = LookupLine(ip, code, bcf);
44     // FIXME: some indication of the filename, maybe with a table index?
45     ss << "I " << int(ip - code) << " \tL " << li->line() << " \t";
46     if (*ip < 0 || *ip >= IL_MAX_OPS) {
47         ss << "ILLEGAL INSTRUCTION: " << *ip;
48         return nullptr;
49     }
50     ss << ilnames[*ip] << ' ';
51     auto arity = ilarity[*ip];
52     auto ins_start = ip;
53     int opc = *ip++;
54     if (opc < 0 || opc >= IL_MAX_OPS) {
55         ss << opc << " ?";
56         return ip;
57     }
58     int is_struct = 0;
59     switch(opc) {
60         case IL_PUSHINT64:
61         case IL_PUSHFLT64: {
62             auto a = *ip++;
63             auto v = Int64FromInts(a, *ip++);
64             if (opc == IL_PUSHINT64) ss << v;
65             else {
66                 int2float64 i2f;
67                 i2f.i = v;
68                 ss << i2f.f;
69             }
70             break;
71         }
72 
73         case IL_LOGWRITE:
74         case IL_KEEPREF:
75             ss << *ip++ << ' ';
76             ss << *ip++;
77             break;
78 
79         case IL_RETURN: {
80             auto id = *ip++;
81             ip++;  // retvals
82             ss << bcf->functions()->Get(id)->name()->string_view();
83             break;
84         }
85 
86         case IL_CALL: {
87             auto bc = *ip++;
88             assert(code[bc] == IL_FUNSTART);
89             auto id = code[bc + 1];
90             auto nargs = code[bc];
91             ss << nargs << ' ' << bcf->functions()->Get(id)->name()->string_view();
92             ss << ' ' << bc;
93             break;
94         }
95 
96         case IL_NEWVEC: {
97             ip++;  // ti
98             auto nargs = *ip++;
99             ss << "vector " << nargs;
100             break;
101         }
102         case IL_ST2S:
103         case IL_NEWOBJECT: {
104             auto ti = (TypeInfo *)(typetable + *ip++);
105             ss << bcf->udts()->Get(ti->structidx)->name()->string_view();
106             break;
107         }
108 
109         case IL_BCALLRETV:
110         case IL_BCALLRET0:
111         case IL_BCALLRET1:
112         case IL_BCALLRET2:
113         case IL_BCALLRET3:
114         case IL_BCALLRET4:
115         case IL_BCALLRET5:
116         case IL_BCALLRET6:
117         case IL_BCALLREFV:
118         case IL_BCALLREF0:
119         case IL_BCALLREF1:
120         case IL_BCALLREF2:
121         case IL_BCALLREF3:
122         case IL_BCALLREF4:
123         case IL_BCALLREF5:
124         case IL_BCALLREF6:
125         case IL_BCALLUNBV:
126         case IL_BCALLUNB0:
127         case IL_BCALLUNB1:
128         case IL_BCALLUNB2:
129         case IL_BCALLUNB3:
130         case IL_BCALLUNB4:
131         case IL_BCALLUNB5:
132         case IL_BCALLUNB6: {
133             int a = *ip++;
134             ss << nfr.nfuns[a]->name;
135             break;
136         }
137 
138         #undef LVAL
139         #define LVAL(N, V) case IL_VAR_##N: is_struct = V; goto var;
140             LVALOPNAMES
141         #undef LVAL
142         case IL_PUSHVARV:
143             is_struct = 1;
144         case IL_PUSHVAR:
145         var:
146             ss << IdName(bcf, *ip++);
147             if (is_struct) ss << ' ' << *ip++;
148             break;
149 
150         case IL_PUSHFLT:
151             ss << *(float *)ip;
152             ip++;
153             break;
154 
155         case IL_PUSHSTR:
156             EscapeAndQuote(bcf->stringtable()->Get(*ip++)->string_view(), ss);
157             break;
158 
159         case IL_FUNSTART: {
160             auto fidx = *ip++;
161             ss << (fidx >= 0 ? bcf->functions()->Get(fidx)->name()->string_view() : "__dummy");
162             ss << "(";
163             int n = *ip++;
164             while (n--) ss << IdName(bcf, *ip++) << ' ';
165             n = *ip++;
166             ss << "=> ";
167             while (n--) ss << IdName(bcf, *ip++) << ' ';
168             auto keepvars = *ip++;
169             if (keepvars) ss << "K:" << keepvars << ' ';
170             n = *ip++;  // owned
171             while (n--) ss << "O:" << IdName(bcf, *ip++) << ' ';
172             ss << ")";
173             break;
174         }
175 
176         case IL_CORO: {
177             ss << *ip++;
178             ip++;  // typeinfo
179             int n = *ip++;
180             for (int i = 0; i < n; i++) ss <<" v" << *ip++;
181             break;
182         }
183 
184         default:
185             for (int i = 0; i < arity; i++) {
186                 if (i) ss << ' ';
187                 ss << *ip++;
188             }
189             break;
190     }
191     assert(arity == ILUNKNOWNARITY || ip - ins_start == arity + 1);
192     (void)ins_start;
193     return ip;
194 }
195 
DisAsm(NativeRegistry & nfr,ostringstream & ss,string_view bytecode_buffer)196 void DisAsm(NativeRegistry &nfr, ostringstream &ss, string_view bytecode_buffer) {
197     auto bcf = bytecode::GetBytecodeFile(bytecode_buffer.data());
198     assert(FLATBUFFERS_LITTLEENDIAN);
199     auto code = (const int *)bcf->bytecode()->Data();  // Assumes we're on a little-endian machine.
200     auto typetable = (const type_elem_t *)bcf->typetable()->Data();  // Same.
201     auto len = bcf->bytecode()->Length();
202     const int *ip = code;
203     while (ip < code + len) {
204         ip = DisAsmIns(nfr, ss, ip, code, typetable, bcf);
205         ss << "\n";
206         if (!ip) break;
207     }
208 }
209 
210 }  // namespace lobster
211