kdelibs/kjs/bytecode/opcodes.cpp.in
Ivailo Monev 91ee52c839 khtml: remove dead code
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2015-11-16 04:42:50 +02:00

435 lines
15 KiB
C++

/*
* Opcode data structure and selection routines for KJS/Frostbyte
* This file is part of the KDE libraries
* Copyright (C) 2008 Maksim Orlovich (maksim@kde.org)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "opcodes.h"
#include "CompileState.h"
#include <cstdio>
#if PLATFORM(SOLARIS_OS)
using std::printf;
#endif
// Enable this to dump instruction selection instructions.
// #define TRACE_INSTR_SELECT
namespace KJS {
const int Cost_NoConversion = -100000; // small enough so that adding actual costs doesn't
// make it positive
const int Cost_Checked = -2;
@generate
// Note: costOut will be negative if no conversion is possible
ConvOp computeCast(const OpValue* in, OpType outType, bool outImmediate, int& costOut, bool exact)
{
bool inImmediate = (in->immediate != 0);
OpType inType = in->type;
// Obviously, we can't make a register result into an an immediate value
if (outImmediate && !inImmediate) {
costOut = Cost_NoConversion;
return Conv_NoConversion;
}
if (exact) {
if (inType == outType) {
costOut = 0;
// We want to prefer these even if they require a spill,
// (since the implementation is more immediate),
// but we do want a slight cost to the spill so we don't do it for no good reason
if (!outImmediate && inImmediate)
costOut = 1;
return Conv_NoOp;
}
// as a special case, int32 -> number conversion is always safe; otherwise
// exact matching can't happen
if (inType != OpType_int32 || outType != OpType_number) {
costOut = Cost_NoConversion;
return Conv_NoConversion;
}
}
// In general, if we're converting immediate to register, we first try to
// convert the immediate value to the right type. That will either produce
// a fresh immediate value that be spilled directly, or may even emit the
// conversion for us. If it produces the value --- and hence the basic conversion ---
// all we have to do is spill it.
// Look up in the table..
const ConvInfo* inf = getConversionInfo(inImmediate, inType, outType);
if (inf->costCode == Cost_Checked) {
ASSERT(inImmediate);
ASSERT(outType == OpType_value);
ASSERT(inType == OpType_int32 || inType == OpType_number);
// Where a conversion is checked, we may not be able to get an
// immediate<->immediate match, so we may have to generate
// a special immediate<->register conversion.
bool inlineOK = inType == OpType_int32 ? JSImmediate::from(in->value.narrow.int32Val) :
JSImmediate::from(in->value.wide.numberVal);
if (outImmediate) {
// immediate -> immediate..
if (inlineOK)
costOut = 0;
else
costOut = Cost_NoConversion;
return inf->routine;
} else {
// immediate -> register.
// If we know inline op OK, we follow the normal path below which adds in a spill cost.
// Otherwise we emit the op directly.
if (!inlineOK) {
// cost about the same as register int32-> number conversion
costOut = getConversionInfo(false, OpType_int32, OpType_value)->costCode;
return inType == OpType_int32 ? Conv_I_R_Int32_Value :
Conv_I_R_Number_Value;
}
}
}
costOut = inf->costCode;
// Add some cost for a spill...
if (inImmediate && !outImmediate)
costOut += 25;
return inf->routine;
}
void emitConversion(CompileState* comp, bool outImm,
ConvOp convType, OpValue* original, OpValue& out)
{
if (emitImmediateConversion(convType, original, out)) {
// Emit a spill if needed, after the value gets converted..
if (!outImm && original->immediate) { // Need both checks since NoOp might get here..
OpValue spillVal, spillRef;
comp->requestTemporary(out.type, &spillVal, &spillRef);
CodeGen::emitRegStore(comp, &spillRef, &out);
out = spillVal;
}
return;
}
if (emitSimpleRegisterConversion(comp, convType, original, out))
return;
switch (convType) {
case Conv_I_R_Int32_Value:
CodeGen::emitOp(comp, Op_RInt32_Value_NonImm, &out, original);
break;
case Conv_I_R_Number_Value:
CodeGen::emitOp(comp, Op_RNum_Value_NonImm, &out, original);
break;
default:
fprintf(stderr, "Unable to emit conversion:%s, in:%c, out:%c\n", ConvOpVals[convType],
original->immediate ? 'I' : 'R', outImm ? 'I' : 'R');
CRASH();
};
}
static inline void setArg(unsigned char* argBase, const OpValue& val)
{
if (val.immediate) {
if (opTypeIsAlign8[val.type]) {
*reinterpret_cast<WideArg*>(argBase) = val.value.wide;
} else {
*reinterpret_cast<NarrowArg*>(argBase) = val.value.narrow;
}
} else {
// For arguments, we actually output the byte offset.
reinterpret_cast<NarrowArg*>(argBase)->regVal = val.ownedReg->reg() * sizeof(LocalStorageEntry);
}
}
static void emitArg(unsigned char* basePtr, const Op* opDescr, int pos, const OpValue& val)
{
ASSERT(opDescr->immediateParams[pos] == val.immediate);
ASSERT(opDescr->paramTypes[pos] == val.type);
unsigned char* argBase = basePtr + opDescr->paramOffsets[pos];
setArg(argBase, val);
}
void CodeGen::emitConvertTo(CompileState* comp, OpValue* in,
OpType outType, OpValue* out)
{
int cost;
ConvOp op = computeCast(in, outType, in->immediate, cost, false);
ASSERT(cost >= 0);
emitConversion(comp, in->immediate, op, in, *out);
}
void CodeGen::emitRegStore(CompileState* comp, OpValue* regNum, OpValue* val)
{
ASSERT(regNum->immediate && regNum->type == OpType_reg);
switch (val->type) {
case OpType_bool:
emitOp(comp, Op_RegPutBool, 0, regNum, val);
break;
case OpType_int32:
emitOp(comp, Op_RegPutInt32, 0, regNum, val);
break;
case OpType_value:
emitOp(comp, Op_RegPutValue, 0, regNum, val);
break;
case OpType_number:
emitOp(comp, Op_RegPutNumber, 0, regNum, val);
break;
default:
fprintf(stderr, "Don't know how to store type to register:%s\n", OpTypeVals[val->type]);
CRASH();
}
}
static void printType(const char* prefix, int pos, OpValue* v) {
fprintf(stderr, "%s%d:%s/imm:%d\n", prefix, pos, OpTypeVals[v->type], v->immediate);
}
Addr CodeGen::emitOp(CompileState* comp, OpName baseInstr,
OpValue* retOut, OpValue* a0, OpValue* a1, OpValue* a2, OpValue* a3)
{
CodeBlock& block = comp->codeBlock();
const Op* const* cands = opSpecializations[baseInstr];
const Op* cheapest = 0;
int cheapestCost = 0;
ConvOp cheapestConvOps[4] = {Conv_NoConversion, Conv_NoConversion, Conv_NoConversion};
// Here, we assume that all methods either return or not.
OpType retType = opRetTypes[baseInstr];
OpValue retVal, retReg;
if (retType != OpType_void) {
// Add in a register argument.. For now just a dummy #;
// will fill in w/appropriate type later
ASSERT(!a3);
retReg.immediate = true;
retReg.type = OpType_reg;
a3 = a2;
a2 = a1;
a1 = a0;
a0 = &retReg;
} else {
ASSERT(!retOut);
}
OpValue* args[4] = {a0, a1, a2, a3};
int numArgs = 0;
while (numArgs < 4 && args[numArgs])
++numArgs;
#ifdef TRACE_INSTR_SELECT
fprintf(stderr, "\n\nTrying to select variant for:%s\n", OpNameVals[baseInstr]);
for (int i = 0; i < numArgs; ++i)
printType("\targ", i, args[i]);
#endif
// First, scan through, and determine the cheapest non-align variant.
// We can't select whether to align or not, since we may have to emit
// cast ops, which could change where we are.
for (int c = 0; cands[c]; ++c) {
const Op* cand = cands[c];
ASSERT(cand->baseInstr == baseInstr);
ASSERT(cand->numParams == numArgs);
if (cand->padAlign)
continue;
int costs[4];
ConvOp convOps[4];
int totalCost = cand->cost;
for (int i = 0; i < numArgs; ++i) {
convOps[i] = computeCast(args[i], cand->paramTypes[i],
cand->immediateParams[i], costs[i], cand->exactParams[i]);
totalCost += costs[i];
}
#ifdef TRACE_INSTR_SELECT
fprintf(stderr, "Candidate:%s, totalCost:%d, variant cost:%d\n", OpByteCodeVals[cand->opCode], totalCost, cand->cost);
for (int i = 0; i < numArgs; ++i) {
fprintf(stderr, "\tconv:%s, costs:%d\n", ConvOpVals[convOps[i]], costs[i]);
}
#endif
if (totalCost < 0) // Cost_NoConversion in the sum...
continue;
if (totalCost < cheapestCost || !cheapest) {
cheapest = cand;
cheapestCost = totalCost;
cheapestConvOps[0] = convOps[0];
cheapestConvOps[1] = convOps[1];
cheapestConvOps[2] = convOps[2];
cheapestConvOps[3] = convOps[3];
}
}
if (!cheapest) {
fprintf(stderr, "Unable to find an acceptable conversion for:%s\n", OpNameVals[baseInstr]);
for (int i = 0; i < numArgs; ++i)
printType("\ta", i, args[i]);
CRASH(); // Should never happen!
}
if (cheapest->endsBB)
comp->localFlushAll(block);
// Now that we have a candidate, actually grab a register of the proper return type.
retType = cheapest->retType;
if (retType != OpType_void) {
comp->requestTemporary(retType, &retVal, &retReg);
// Set return value, if needed
if (retOut)
*retOut = retVal;
}
OpValue c[4]; // converted version
for (int i = 0; i < numArgs; ++i)
emitConversion(comp, cheapest->immediateParams[i], cheapestConvOps[i], args[i], c[i]);
// Now figure out if we need to do align.. We need it if the PC is 8-aligned, since
// the instr will mess that up, and the instruction need it..
if (cheapest->hasPadVariant && ((block.size() % 8) == 0)) {
// The padded variant of the instruction always preceeds the unpadded one..
cheapest = &opsForOpCodes[cheapest->opCode - 1];
}
// Phewww. Now we can actually write out stuff.
size_t pos = block.size();
block.resize(pos + cheapest->length);
unsigned char* basePtr = block.data() + pos;
// Write out the opcode..
*reinterpret_cast<OpByteCode*>(basePtr) = cheapest->opCode;
// ... and the args, as needed.
for (int i = 0; i < numArgs; ++i) {
emitArg(basePtr, cheapest, i, c[i]);
}
return basePtr - block.data();
}
Addr CodeGen::nextPC(CompileState* comp)
{
CodeBlock& block = comp->codeBlock();
comp->localFlushAll(block);
return block.size();
}
void CodeGen::patchOpArgument(CodeBlock& block, Addr baseAddr, int pos, OpValue& newVal)
{
OpByteCode* base = reinterpret_cast<OpByteCode*>(block.data() + baseAddr);
const Op& variant = opsForOpCodes[*base];
// We only permit patching immediate arguments for now..
ASSERT(variant.immediateParams[pos] && newVal.immediate);
ASSERT(variant.paramTypes[pos] == newVal.type);
ASSERT(pos < variant.numParams);
unsigned char* argBase = reinterpret_cast<unsigned char*>(base) + variant.paramOffsets[pos];
setArg(argBase, newVal);
}
void CodeGen::patchJumpToNext(CompileState* comp, Addr op, int argNum)
{
OpValue destAddr = OpValue::immAddr(nextPC(comp));
patchOpArgument(comp->codeBlock(), op, argNum, destAddr);
}
static void dumpParam(CodeBlock& block, size_t offset, OpType type, bool wasImm)
{
switch (type) {
case OpType_bool:
if (reinterpret_cast<NarrowArg*>(block.data() + offset)->boolVal)
std::fprintf(stderr, "true");
else
std::fprintf(stderr, "false");
break;
case OpType_int32:
std::fprintf(stderr, "%d", reinterpret_cast<NarrowArg*>(block.data() + offset)->int32Val);
break;
case OpType_value:
// Immediate value -- should go through JSImmediate stuff..
std::fprintf(stderr, "<ival:%s>", reinterpret_cast<WideArg*>(block.data() + offset)->valueVal->toString(0).ascii());
break;
case OpType_ident:
std::fprintf(stderr, "%s", reinterpret_cast<WideArg*>(block.data() + offset)->identVal->ustring().ascii());
break;
case OpType_string:
std::fprintf(stderr, "\"%s\"", reinterpret_cast<WideArg*>(block.data() + offset)->stringVal->ascii());
break;
case OpType_number:
std::fprintf(stderr, "%f", reinterpret_cast<WideArg*>(block.data() + offset)->numberVal);
break;
case OpType_addr:
std::fprintf(stderr, "A%08x", reinterpret_cast<NarrowArg*>(block.data() + offset)->addrVal);
break;
case OpType_reg:
std::fprintf(stderr, "r%lu", reinterpret_cast<NarrowArg*>(block.data() + offset)->regVal / (wasImm ?
1lu : sizeof(LocalStorageEntry)));
break;
case OpType_node:
std::fprintf(stderr,"N%p", (void*)(reinterpret_cast<WideArg*>(block.data() + offset)->nodeVal));
break;
case OpType_cstr:
std::fprintf(stderr, "c\"%s\"", reinterpret_cast<WideArg*>(block.data() + offset)->cstrVal);
break;
default:
std::fprintf(stderr, "???:%s", OpTypeVals[type]);
};
std::fprintf(stderr, " ");
}
void CodeGen::disassembleBlock(CodeBlock& block)
{
size_t pc = 0;
while (pc < block.size()) {
OpByteCode opCode = *reinterpret_cast<OpByteCode*>(block.data() + pc);
const Op& opDescr = opsForOpCodes[opCode];
std::fprintf(stderr, "%08u %s ", pc, OpNameVals[opDescr.baseInstr]);
for (int p = 0; p < opDescr.numParams; ++p) {
dumpParam(block, pc + opDescr.paramOffsets[p],
opDescr.immediateParams[p] ? opDescr.paramTypes[p] : OpType_reg, opDescr.immediateParams[p]);
}
std::fprintf(stderr, "\t\t// %s\n", OpByteCodeVals[opCode]);
pc += opDescr.length;
}
}
} //namespace KJS
// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on; hl c++;