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