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