1 /*
2 * A utility for building various tables and specializations for the
3 * KJS Frostbyte bytecode
4 *
5 * Copyright (C) 2007, 2008 Maks Orlovich (maksim@kde.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24 #include "types.h"
25
26 #include <stdlib.h>
27 #include "assert.h"
28 #include <cctype>
29 #include <cstdio>
30 #include <wtf/ASCIICType.h>
31
32 #include "tablebuilder.h"
33
34 using namespace std;
35
36 // # of bits store 'vals' values, e.g. 3 for 8, etc.
neededBits(unsigned vals)37 static unsigned neededBits(unsigned vals)
38 {
39 unsigned bits = 1;
40 while ((1U << bits) < vals) {
41 ++bits;
42 }
43 return bits;
44 }
45
TypeTable(TableBuilder * instrBuilder,CodePrinter & out)46 TypeTable::TypeTable(TableBuilder *instrBuilder, CodePrinter &out):
47 instrBuilder(instrBuilder), out(out)
48 {
49 // Builtin stuff...
50 conversionNames.push_back("NoConversion");
51 conversionNames.push_back("NoOp");
52
53 // Special ones for checked; opcodes.cpp.in has code to generate these.
54 conversionNames.push_back("I_R_Int32_Value");
55 conversionNames.push_back("I_R_Number_Value");
56 }
57
generateCode()58 void TypeTable::generateCode()
59 {
60 // Types... First we just want to list them
61 Enum typesEnum("OpType", "OpType_", typeNames);
62 typesEnum.printDeclaration(out(OpH));
63 typesEnum.printDefinition(out(OpCpp));
64
65 // Also, print out the width array...
66 Array widths(out(OpCpp), "const bool", "opTypeIsAlign8");
67 for (unsigned t = 0; t < typeNames.size(); ++t) {
68 const Type &type = types[typeNames[t]];
69 widths.item(type.alignTo8() ? "true" : "false", type.name);
70 }
71 widths.endArray();
72
73 // Conversion ops. Those go entirely in the .cpp
74 Enum convOps("ConvOp", "Conv_", conversionNames);
75 convOps.printDeclaration(out(OpH));
76 convOps.printDefinition(out(OpCpp));
77
78 out(OpCpp) << "struct ConvInfo {\n";
79 out(OpCpp) << " ConvOp routine;\n";
80 out(OpCpp) << " int costCode;\n";
81 out(OpCpp) << "};\n\n";
82
83 // For conversion info, we use upper bit for register/immediate (immediate is set),
84 // and then enough bits for the from/to types as the index.
85 Array convArr(out(OpCpp), "ConvInfo", "conversions");
86 printConversionInfo(convArr, rgConversions, true);
87 printConversionInfo(convArr, imConversions, false);
88 convArr.endArray();
89
90 int numBits = neededBits(types.size());
91 out(OpCpp) << "static inline const ConvInfo* getConversionInfo(bool immediate, OpType from, OpType to)\n{\n";
92 out(OpCpp) << " return &conversions[((int)immediate << " << (2 * numBits) << ")"
93 << " | ((int)from << " << numBits << ") | (int)to];\n";
94 out(OpCpp) << "}\n\n";
95
96 // Emit inline conversion helpers based on the [[ code ]] specified
97 // in codes.def
98 for (unsigned c = 0; c < imConversionList.size(); ++c) {
99 printConversionRoutine(imConversionList[c]);
100 }
101
102 // Now we generate a helper that invokes those.
103 out(OpCpp) << "static bool emitImmediateConversion(ConvOp convType, OpValue* original, OpValue& out)\n{\n";
104 out(OpCpp) << " out.immediate = true;\n";
105 out(OpCpp) << " switch(convType) {\n";
106 out(OpCpp) << " case Conv_NoOp:\n";
107 out(OpCpp) << " out = *original;\n";
108 out(OpCpp) << " break;\n";
109 for (unsigned c = 0; c < imConversionList.size(); ++c) {
110 const ConversionInfo &inf = imConversionList[c];
111 out(OpCpp) << " case Conv_" << inf.name << ":\n";
112 out(OpCpp) << " out.type = OpType_" << inf.to.name << ";\n";
113 out(OpCpp) << " out.value." << inf.to.field() << " = "
114 << "convert" << inf.name << "(nullptr, "
115 << "original->value." << inf.from.field() << ");\n";
116
117 out(OpCpp) << " break;\n";
118 }
119
120 out(OpCpp) << " default:\n";
121 out(OpCpp) << " return false;\n";
122 out(OpCpp) << " }\n";
123 out(OpCpp) << " return true;\n";
124 out(OpCpp) << "}\n\n";
125
126 // Similar helper for simple register conversions, which actually emits ops
127 out(OpCpp) << "static bool emitSimpleRegisterConversion(CompileState* comp, ConvOp convType, OpValue* original, OpValue& out)\n{\n";
128 out(OpCpp) << " switch(convType) {\n";
129 out(OpCpp) << " case Conv_NoOp:\n";
130 out(OpCpp) << " out = *original;\n";
131 out(OpCpp) << " break;\n";
132 for (unsigned c = 0; c < rgConversionList.size(); ++c) {
133 const ConversionInfo &inf = rgConversionList[c];
134 out(OpCpp) << " case Conv_" << inf.name << ":\n";
135 out(OpCpp) << " CodeGen::emitOp(comp, Op_" << inf.name << ", &out, original);\n";
136 out(OpCpp) << " break;\n";
137 }
138 out(OpCpp) << " default:\n";
139 out(OpCpp) << " return false;\n";
140 out(OpCpp) << " }\n";
141 out(OpCpp) << " return true;\n";
142 out(OpCpp) << "}\n\n";
143 }
144
printConversionInfo(Array & outArr,map<string,map<string,ConversionInfo>> & table,bool reg)145 void TypeTable::printConversionInfo(Array &outArr, map<string, map<string, ConversionInfo> > &table, bool reg)
146 {
147 unsigned numBits = neededBits(types.size());
148 unsigned fullRange = 1 << numBits;
149 for (unsigned from = 0; from < fullRange; ++from) {
150 for (unsigned to = 0; to < fullRange; ++to) {
151 if (from < types.size() && to < types.size()) {
152 string fromName = typeNames[from];
153 string toName = typeNames[to];
154
155 string item;
156
157 // For register conversion, we need it to be possible for source + dest to be in
158 // registers. For immediate, we only require source, since dest will just go
159 // into local value.
160 bool representable;
161 if (reg) {
162 representable = types[fromName].hasReg() && types[toName].hasReg();
163 } else {
164 representable = types[fromName].hasImm();
165 }
166
167 if (from == to) {
168 item = "{Conv_NoOp, 0}";
169 } else if (table[fromName].find(toName) != table[fromName].end() && representable) {
170 const ConversionInfo &inf = table[fromName][toName];
171
172 item = "{Conv_" + inf.name + ", ";
173 if (inf.flags & Conv_Checked) {
174 item += "Cost_Checked";
175 } else {
176 item += CodePrinter::stringFromInt(reg ? inf.cost : 0);
177 }
178 item += "}"; // krazy:exclude=doublequote_chars
179 } else {
180 item = "{Conv_NoConversion, Cost_NoConversion}";
181 }
182
183 outArr.item(item, fromName + " => " + toName);
184 } else {
185 outArr.item("{Conv_NoConversion, Cost_NoConversion}");
186 }
187 } // for to..
188 } // for from..
189 }
190
printConversionRoutine(const ConversionInfo & conversion)191 void TypeTable::printConversionRoutine(const ConversionInfo &conversion)
192 {
193 out(OpH) << "ALWAYS_INLINE " << conversion.to.nativeName << " convert" << conversion.name
194 << "(ExecState* exec, " << conversion.from.nativeName << " in)\n";
195 out(OpH) << "{\n";
196 out(OpH) << " (void)exec;\n";
197 out.printCode(out(OpH), 4, conversion.impl, conversion.codeLine);
198 out(OpH) << "}\n\n";
199 }
200
handleType(const string & type,const string & nativeName,unsigned flags)201 void TypeTable::handleType(const string &type, const string &nativeName, unsigned flags)
202 {
203 typeNames.push_back(type);
204 Type t;
205 t.name = type;
206 t.nativeName = nativeName;
207 t.flags = flags;
208 types[type] = t;
209 }
210
capitalized(const string & in)211 static string capitalized(const string &in)
212 {
213 return WTF::toASCIIUpper(in[0]) + in.substr(1);
214 }
215
handleConversion(const string & code,int codeLine,unsigned flags,const string & from,const string & to,int tileCost,int registerCost)216 void TypeTable::handleConversion(const string &code, int codeLine,
217 unsigned flags, const string &from, const string &to,
218 int tileCost, int registerCost)
219 {
220 // Compute the conversion names. The register one (if any) would also create an operation.
221 string immName = "I" + capitalized(from) + "_" + capitalized(to); // krazy:exclude=doublequote_chars
222 string regName = "R" + capitalized(from) + "_" + capitalized(to); // krazy:exclude=doublequote_chars
223
224 // Register immediate conversion
225 conversionNames.push_back(immName);
226 ConversionInfo inf;
227 inf.name = immName;
228 inf.cost = tileCost;
229 inf.flags = flags;
230 inf.impl = code;
231 inf.codeLine = codeLine;
232 inf.from = types[from];
233 inf.to = types[to];
234
235 imConversions[from][to] = inf;
236 imConversionList.push_back(inf);
237
238 // ... and, if it exists, register one.
239 if (flags & Conv_HaveReg) {
240 conversionNames.push_back(regName);
241 inf.name = regName;
242 inf.cost = registerCost;
243 inf.flags &= ~Conv_Checked; // 'checked' makes no sense here
244 rgConversions[from][to] = inf;
245 rgConversionList.push_back(inf);
246
247 // We also generate the corresponding bytecode routine, using
248 // the immediate conversion helper we'll emit in it.
249 instrBuilder->handleOperation(regName, false);
250
251 vector<Parameter> sig;
252 Parameter param;
253 param.name = "in";
254 param.typeName = from;
255 param.flags = Param_Exact;
256 sig.push_back(param);
257
258 string code = inf.to.nativeName + " out = convertI" + inf.name.substr(1) + "(exec, in);\n";
259 code += "$$ = out;\n";
260 instrBuilder->handleImpl("", code, codeLine, 0, to, sig);
261 }
262 }
263
resolveType(const string & type)264 Type TypeTable::resolveType(const string &type)
265 {
266 if (types.find(type) != types.end()) {
267 return types[type];
268 } else {
269 out.issueError("Unknown type:" + type);
270 }
271
272 // return something in nonvoid function
273 Type t;
274 return t;
275 }
276
resolveSignature(const StringList & in)277 vector<Type> TypeTable::resolveSignature(const StringList &in)
278 {
279 vector<Type> sig;
280 for (unsigned c = 0; c < in.size(); ++c) {
281 sig.push_back(resolveType(in[c]));
282 }
283
284 return sig;
285 }
286
immConv(const Type & from,const Type & to)287 ConversionInfo TypeTable::immConv(const Type &from, const Type &to)
288 {
289 return imConversions[from.name][to.name];
290 }
291
292