in modules/asc/src/java/adobe/abc/GlobalOptimizer.java [4474:4929]
void sccp_modify(Method m, EdgeMap<Expr> uses, Map<Expr, Object> values, Map<Expr, Typeref> types,
Expr e, TreeSet<Expr> work)
{
sccp_rename(uses, e, e.args);
sccp_rename(uses, e, e.locals);
boolean changed;
do
{
changed = false;
switch (e.op)
{
case OP_newclass:
{
// capture scope chain
Type c = e.c;
c.scopes = copyOf(m.cx.scopes, m.cx.scopes.length+e.scopes.length);
int i = m.cx.scopes.length;
for (Expr s: e.scopes)
c.scopes[i++] = types.get(s);
readyType(c);
Type t = c.itype;
t.scopes = copyOf(c.scopes, c.scopes.length+1);
t.scopes[c.scopes.length] = c.ref.nonnull();
readyType(t);
break;
}
case OP_newfunction:
{
Method f = e.m;
// TODO makeIntoPrototypeFunction()
Type t = new Type(m.getName(), FUNCTION());
t.scopes = copyOf(m.cx.scopes, m.cx.scopes.length+e.scopes.length);
int i = m.cx.scopes.length;
for (Expr s: e.scopes)
t.scopes[i++] = types.get(s);
f.cx = t;
readyMethod(f);
break;
}
case OP_returnvalue:
if (type(types,e.args[0]) == VOID())
{
e.op = OP_returnvoid;
e.args = noexprs;
}
else
{
Type t0 = type(types,e.args[0]);
if (t0.extendsOrIsBase(m.returns.t))
{
Expr a0 = e.args[0];
if (m.returns.t == INT() && a0.op == OP_convert_i)
{
uses.get(a0).remove(e);
a0 = e.args[0] = a0.args[0];
uses.get(a0).add(e);
}
}
}
break;
case OP_getglobalscope:
{
if (m.cx.scopes.length == 0)
{
// global is inner scope 0, so copy that
makeCopy(e, unwrapScope(e, 0));
changed = true;
}
break;
}
case OP_getscopeobject:
{
makeCopy(e, unwrapScope(e, 0));
changed = true;
break;
}
case OP_istypelate:
{
Type t1 = type(types,e.args[1]);
if (t1.itype != null && TypeCache.instance().containsNamedType(t1.itype))
{
e.op = OP_istype;
e.ref = t1.itype.getName();
e.args = new Expr[] { e.args[0] };
e.clearPx();
changed = true;
}
break;
}
case OP_istype:
{
if (TypeCache.instance().containsNamedType(e.ref))
e.clearPx();
break;
}
case OP_pushscope:
case OP_pushwith:
{
if (!types.get(e.args[0]).nullable)
e.clearPx();
break;
}
case OP_coerce:
{
Expr a0 = e.args[0];
Typeref t = types.get(e);
Typeref t0 = types.get(a0);
if (t == t0)
{
// upcast
makeCopy(e, a0);
changed = true;
}
else
{
Object v0 = values.get(a0);
if (v0 == NULL() && t0.nullable && t0.t != VOID())
{
makeCopy(e, a0);
changed = true;
}
else
{
if (TypeCache.instance().namedTypes.get(e.ref) == OBJECT())
{
e.op = OP_coerce_o;
e.clearEffect();
e.ref = null;
e.imm = null;
changed = true;
}
}
}
break;
}
// turn findprop into either getglobalscope, getscopeobject, or finddef to expose redundancies
case OP_findpropstrict:
case OP_findproperty:
{
int i = findInner(e.ref, e.scopes, types);
if (i >= 0)
{
makeCopy(e, unwrapScope(e,i));
for (Expr s: e.scopes)
uses.get(s).remove(e);
changed = true;
}
else if ((i = findOuter(e.ref, m.cx.scopes)) >= 0)
{
if (i == 0) // getglobalscope
{
e.op = OP_getglobalscope;
e.scopes = noexprs;
e.setPure();
for (Expr s: e.scopes)
uses.get(s).remove(e);
changed = true;
}
else
{
// can't change the opcode
e.setPure();
// TODO - early bind e.ref
}
}
else if (TypeCache.instance().globals.contains(e.ref))
{
e.op = OP_finddef;
e.flags = flagTable[e.op];
e.scopes = noexprs;
e.ref = TypeCache.instance().globals.getName(e.ref);
for (Expr s: e.scopes)
uses.get(s).remove(e);
changed = true;
}
/*
* TODO: This transformation is too simplistic.
*
else if (e.op == OP_findproperty)
{
// will return global if nothing found.
if (m.cx.scopes.length == 0)
{
makeCopy(e, unwrapScope(e,0));
for (Expr s: e.scopes)
uses.get(s).remove(e);
changed = true;
}
else
{
e.op = OP_getglobalscope;
for (Expr s: e.scopes)
uses.get(s).remove(e);
e.scopes = noexprs;
e.setPure();
changed = true;
}
}
*/
break;
}
// getsuper should look in base of enclosing type.
// if either bind to a final getter, use callstatic.
case OP_getproperty:
case OP_getsuper:
{
Typeref t0 = types.get(e.args[0]);
Binding bind = t0.findGet(e.ref);
if (isSlot(bind))
{
e.clearEffect();
if (!t0.nullable)
e.clearPx();
e.ref = bind.getName();
if (canEarlyBindSlot(m, bind))
{
if (!isConst(bind) || !constify(e, values.get(e)))
{
e.op = OP_getslot;
e.imm = new int[] { bind.slot };
changed = true;
}
}
}
else if (bind != null)
{
// narrow the binding
e.ref = bind.getName();
}
break;
}
case OP_initproperty:
{
Type t0 = type(types,e.args[0]);
Object v1 = values.get(e.args[1]);
Binding bind = t0.find(e.ref);
if (bind != null)
{
// narrow the binding
e.ref = bind.getName();
// isSlot() is to aggressive, breaks (at least) zlib
if (isConst(bind) && bind.value != null && bind.value.equals(v1))
{
// initializing a slot to it's known default value?
// TODO ensure no intervening writes to non-const values
makeNop(e);
for (Expr a: e.args)
uses.get(a).remove(e);
}
else if (isSlot(bind) && canEarlyBindSlot(m, bind))
{
e.op = OP_setslot;
e.imm = new int[] { bind.slot };
changed = true;
}
}
break;
}
case OP_setproperty:
case OP_setsuper:
{
Type t0 = type(types,e.args[0]);
Binding bind = t0.find(e.ref);
if (isSlot(bind))
{
e.ref = bind.getName();
if (canEarlyBindSlot(m, bind))
{
e.op = OP_setslot;
e.imm = new int[] { bind.slot };
changed = true;
}
}
else if (bind != null)
{
// narrow the binding
e.ref = bind.getName();
}
break;
}
case OP_callproperty:
case OP_callproplex:
case OP_callsuper:
case OP_constructprop:
case OP_callpropvoid:
{
// TODO turn callproplex into callproperty if binding is method
Type t0 = type(types,e.args[0]);
Binding b0 = t0.findGet(e.ref);
if (b0 != null)
{
// narrow the binding
e.ref = b0.getName();
if (e.op == OP_callproperty && isMethod(b0))
{
if (t0.isPrimitive() && e.args.length==1 && e.ref.equals(AS3_TOSTRING) && type(types,e) == STRING())
{
// TODO this may open up further reductions. how to iterate?
// or, need to add similar logic in sccp_eval
e.op = OP_convert_s;
e.setPure();
changed=true;
}
else if (canEarlyBindMethod(m,b0) && (t0.isFinal() || b0.isFinal()))
{
e.op = OP_callstatic;
e.m = b0.method;
changed=true;
}
else if (USE_CALLMETHOD && canEarlyBindMethod(m,b0))
{
e.op = OP_callmethod;
e.imm = new int[] { b0.slot };
changed = true;
}
}
else if (e.op == OP_callproperty && isClass(b0))
{
changed |= convertify(e, b0, types);
}
}
if (uses.get(e).isEmpty())
{
if (e.op == OP_callsuper)
e.op = OP_callsupervoid;
if (e.op == OP_callproperty)
e.op = OP_callpropvoid;
}
break;
}
case OP_convert_u:
if (e.args[0].op == OP_convert_i)
e.args[0] = e.args[0].args[0];
// fall through
case OP_coerce_s:
case OP_convert_s:
case OP_convert_d:
case OP_coerce_o:
case OP_convert_b:
case OP_convert_i:
case OP_coerce_a:
{
Expr a0 = e.args[0];
Type t = type(types,e);
Type t0 = type(types,e.args[0]);
if (t == t0)
{
makeCopy(e,e.args[0]);
changed=true;
}
else if (e.op == OP_convert_i)
{
if (a0.op == OP_negate)
{
// convert_i(negate(x)) => negate_i(x)
e.op = OP_negate_i;
e.args = new Expr[] { a0.args[0] };
changed=true;
}
else if (a0.op == OP_decrement)
{
e.op = OP_decrement_i;
e.args = new Expr[] { a0.args[0] };
changed = true;
}
}
break;
}
case OP_subtract:
{
Object v1 = values.get(e.args[1]);
if (doubleValue(v1) == 1)
{
e.args = new Expr[] { e.args[0] };
e.op = OP_decrement;
changed = true;
}
else if (doubleValue(v1) == -1)
{
e.args = new Expr[] { e.args[0] };
e.op = OP_increment;
changed = true;
}
break;
}
case OP_add:
{
Type t = type(types,e);
Object v0 = values.get(e.args[0]);
Object v1 = values.get(e.args[1]);
if (t.numeric)
{
if (doubleValue(v0)==1)
{
e.args = new Expr[] { e.args[1] };
e.op = OP_increment;
changed = true;
}
else if (doubleValue(v1)==1)
{
e.args = new Expr[] { e.args[0] };
e.op = OP_increment;
changed = true;
}
else if (doubleValue(v0) == -1)
{
e.args = new Expr[] { e.args[1] };
e.op = OP_decrement;
changed = true;
}
else if (doubleValue(v1) == -1)
{
e.args = new Expr[] { e.args[0] };
e.op = OP_decrement;
changed = true;
}
}
else if (t == STRING())
{
if ("".equals(v0))
{
e.args = new Expr[] { e.args[1] };
e.op = OP_convert_s;
changed = true;
}
else if ("".equals(v1))
{
e.args = new Expr[] { e.args[0] };
e.op = OP_convert_s;
changed = true;
}
}
}
}
if (changed)
work.addAll(uses.get(e));
}
while (changed);
}