Files
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

314 lines
9.5 KiB
C++

/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "hm-main.h"
#ifdef VMCFG_HALFMOON
namespace halfmoon {
InstrKind tomodelPunt() {
assert(false && "cant convert models");
return HR_MAX;
}
InstrKind toModelAtom(const Type* t) {
switch (model(t)) {
default: assert(false && "bad model for toModelAtom");
case kModelString: return HR_string2atom;
case kModelDouble: return HR_double2atom;
case kModelScriptObject: return HR_scriptobject2atom;
case kModelNamespace: return HR_ns2atom;
case kModelInt:
return isBoolean(t) ? HR_bool2atom :
isInt(t) ? HR_int2atom :
isUInt(t) ? HR_uint2atom : tomodelPunt();
}
}
InstrKind toModelDouble(const Type* t) {
switch (model(t)) {
default: assert(false && "bad model for toModelDouble");
case kModelAtom: return HR_atom2double;
case kModelInt:
return isInt(t) ? HR_i2d :
isUInt(t) ? HR_u2d :
HR_MAX;
}
}
InstrKind toModelInt(const Type* t, const Type* use_type) {
// todo: double->int
switch (model(t)) {
default: assert(false && "bad input model for toModelInt");
case kModelAtom: {
if (isBoolean(t)) {
return HR_atom2bool;
} else {
/// This must be bad - example src: var x:uint = 10;
/// The type is a uiNumber[a]. the use_type says x MUST be the uint type
/// So we have to change the model from atom to uint
/// but uint and int share the same model kModelInt, BUT are unique types
/// and uint / int are not subtypes of each other
/// so we have to be explicit in which int we choose based on the use_type
assert (isInt(t) || isUInt(t));
if (isUInt(use_type)) {
assert (!isInt(use_type));
return HR_atom2uint;
} else {
assert (isInt(use_type) && !isUInt(use_type));
return HR_atom2int;
}
}
} // end case atom
case kModelDouble:
if (isInt(t))
return HR_d2i;
if (isUInt(t))
return HR_d2u;
assert(false && "nonsense -> int");
break;
} // end switch
return HR_MAX;
}
InstrKind toModelScriptobject(const Type* t) {
switch (model(t)) {
default:
assert(false && "bad input model for toModelScriptobject");
case kModelAtom:
return HR_atom2scriptobject;
}
}
InstrKind toModelKind(const Type* val_type, const Type* use_type) {
switch (model(use_type)) {
default:
printf("unknown conversion %s -> %s\n", typeName(val_type),
typeName(use_type));
assert(false && "bad model");
case kModelNamespace:
return HR_atom2ns;
case kModelAtom:
return toModelAtom(val_type);
case kModelScriptObject:
return toModelScriptobject(val_type);
case kModelString:
return HR_atom2string;
case kModelInt:
return toModelInt(val_type, use_type);
case kModelDouble:
return toModelDouble(val_type);
}
}
/// ModelFixer encapsulates state and helpers for inserting model-changing
/// instructions into the IR.
class ModelFixer {
public:
ModelFixer(Context* cxt, InstrGraph* ir)
: cxt_(cxt), ir_(ir), factory_(ir), analyzer_(ir) {
}
/** change opcodes when possible */
void changeOps();
/** insert conversions after defs */
void fixDefs();
/// Return the constraint for this use. Instructions that have Any or
/// Top in their signature get handled here.
const Type* getConstraint(const Use& u);
private:
Context* cxt_;
InstrGraph* ir_;
InstrFactory factory_;
TypeAnalyzer analyzer_;
};
/**
* Run a pass where we select alternative opcodes to reduce the number
* of model conversions we'll have to insert.
*/
void ModelFixer::changeOps() {
for (AllInstrRange i(ir_); !i.empty();) {
Instr* instr = i.popFront();
switch (kind(instr)) {
case HR_cknull: {
UnaryStmt* cknull = cast<UnaryStmt>(instr);
const Type* ptr_type = type(cknull->value_in());
if (model(ptr_type) == kModelScriptObject) {
// cknull(ptr:ScriptObject) -> cknullobject(ptr)
factory_.toUnaryStmt(HR_cknullobject, cknull);
analyzer_.computeTypes(cknull);
}
break;
}
}
}
}
/**
* Insert model conversions after defs, where needed. If multiple uses
* need to use the same converted def, reuse the conversion instruction.
*/
void ModelFixer::fixDefs() {
const int kConvertsMax = 4;
Def* converts[kConvertsMax]; // space to memoize one conversion per ModelKind.
InstrKind converts_kind[kConvertsMax]; // verify that the conversion is the same
for (PostorderBlockRange b(ir_); !b.empty();) {
for (InstrRange i(b.popFront()); !i.empty();) {
Instr* instr = i.popBack();
SigRange sr = outputSigRange(instr);
for (ArrayRange<Def> dr = defRange(instr); !dr.empty(); sr.popFront()) {
Def* d = &dr.popFront();
int have_count = 0;
const Type* def_type = type(d);
const Type* sig_type = sr.front();
if (isBottom(def_type))
def_type = sig_type;
for (UseRange u(*d); !u.empty(); ) {
Use& use = u.popFront();
const Type* constraint = getConstraint(use);
if (!submodelof(def_type, constraint)) {
// need a conversion. The same model may require different conversions
InstrKind convert_kind = toModelKind(def_type, constraint);
int h = 0;
for (; h < have_count; h++) {
if (converts_kind[h] == convert_kind)
break;
}
if (h >= have_count) {
UnaryExpr* expr = factory_.newUnaryExpr(convert_kind, d);
ir_->addInstrAfter(instr, expr);
converts[h] = expr->value_out();
converts_kind[h] = convert_kind;
have_count++;
assert(have_count <= kConvertsMax);
}
use = converts[h];
}
}
}
}
}
}
/// Return the constraint for this use. Instructions that have Any or
/// Top in their signature get handled here.
const Type* ModelFixer::getConstraint(const Use& u) {
Instr* instr = user(u);
int use_pos = pos(u);
InstrKind k = kind(instr);
switch (k) {
case HR_goto: {
// constraint is the corresponding label's def type.
GotoInstr* go = cast<GotoInstr>(instr);
return type(go->target->params[use_pos]);
}
case HR_if:
case HR_switch:
if (use_pos > 0)
return TOP;
break;
case HR_setslot:
if (use_pos == 3) {
CallStmt2* setslot = cast<CallStmt2>(instr);
const Type* obj_type = type(setslot->object_in());
int slot = ordinalVal(type(setslot->param_in()));
return ir_->lattice.getSlotType(obj_type, slot);
}
break;
case HR_callinterface:
case HR_callmethod:
case HR_callstatic:
if (use_pos >= 2) {
CallStmt2* call = cast<CallStmt2>(instr);
const Type* env_type = type(call->param_in());
if (isEnv(env_type)) {
MethodSignaturep sig = getMethod(type(call->param_in()))->getMethodSignature();
return ir_->lattice.makeParamType(use_pos - 2, sig);
}
}
break;
case HR_return:
if (use_pos == 1) {
// return type is based on method signature
MethodSignaturep sig = cxt_->method->getMethodSignature();
return ir_->lattice.makeType(sig->returnTraits());
}
break;
}
const ShapeRep& rep = shape_reps[shape(instr)];
if (rep.vararg == kVarIn && use_pos > rep.num_uses)
use_pos = rep.num_uses;
return inputSignature(instr)[use_pos];
}
ModelKind promoteModelKind(const Type* t) {
if (isInt(t))
return kModelInt;
if (isUInt(t))
return kModelInt;
if (isNumber(t))
return kModelDouble;
if (isBoolean(t))
return kModelInt;
if (isString(t))
return kModelString;
if (isNamespace(t))
return kModelNamespace;
if (isScriptObject(t))
return kModelScriptObject;
return kModelAtom;
}
/// Run the model fixer once then clean up.
void fixModels(Context* cxt, InstrGraph* ir) {
assert(checkTypes(ir, false));
assert(checkPruned(ir) && checkSSA(ir));
propagateTypes(ir);
ModelFixer fixer(cxt, ir);
fixer.changeOps();
fixer.fixDefs();
propagateTypes(ir);
computeIdentities(ir);
removeDeadCode(cxt, ir);
assert(checkTypes(ir, true));
assert(checkPruned(ir) && checkSSA(ir));
}
ModelKind defaultModelKind(Traits* traits) {
switch (valueStorageType(builtinType(traits))) {
default: assert(false && "bad SlotStorageType");
case SST_atom: return kModelAtom;
case SST_scriptobject: return kModelScriptObject;
case SST_string: return kModelString;
case SST_namespace: return kModelNamespace;
case SST_int32: return kModelInt;
case SST_uint32: return kModelInt;
case SST_bool32: return kModelInt;
case SST_double: return kModelDouble;
}
}
SlotStorageType type2sst(const Type* t) {
assert(isDataType(*t));
switch (model(t)) {
default: assert(false && "bad model");
case kModelInt:
return isInt(t) ? SST_int32 : isUInt(t) ? SST_uint32 : SST_bool32;
case kModelDouble: return SST_double;
case kModelString: return SST_string;
case kModelScriptObject: return SST_scriptobject;
case kModelAtom: return SST_atom;
case kModelNamespace: return SST_namespace;
}
}
}
#endif // VMCFG_HALFMOON