in modules/asc/src/java/adobe/abc/GlobalOptimizer.java [5107:5833]
void sccp_eval(Method m, Expr e,
Map<Expr,Object> values,
Map<Expr,Typeref> types,
Set<Edge>flowWork, Set<Expr>ssaWork,
EdgeMap<Expr> uses)
{
Object v = null;
Typeref tref = null;
if (e.op == OP_phi)
{
for (Expr a: e.args)
{
// compute the phi() meet operation. ignore any inputs we
// haven't evaluated yet. If all inputs have the same value,
// phi() has that value. Otherwise return BOTTOM.
Object av = values.get(a);
if (av == null) continue; // ignore TOP inputs
if (v == null)
v = av;
else if (!av.equals(v))
v = BOTTOM;
// same idea for types, but if they aren't all the same
// then compute the most derived base class (mdb) of the types.
Typeref aref = types.get(a);
if (tref == null)
tref = aref;
else if (!tref.equals(aref))
tref = mdb(tref,aref);
}
}
else
{
// if any arg is TOP result is TOP (unchanged)
for (Expr a : e.args) if (!values.containsKey(a)) return;
for (Expr a : e.scopes) if (!values.containsKey(a)) return;
for (Expr a : e.locals) if (!values.containsKey(a)) return;
v = BOTTOM;
tref = ANY().ref;
switch (e.op)
{
default:
System.err.println("unhandled op:" + e.op + ":"+ opNames[e.op]);
assert(false);
case OP_hasnext2_o:
case OP_nextname:
case OP_nextvalue:
case OP_call:
case OP_callsuper:
case OP_getsuper:
case OP_getdescendants:
break;
case OP_convert_o:
{
tref = types.get(e.args[0]).nonnull();
v = values.get(e.args[0]);
break;
}
case OP_esc_xattr:
case OP_esc_xelem:
tref = STRING().ref.nonnull();
break;
case OP_newcatch:
tref = m.handlers[e.imm[0]].activation;
break;
case OP_newobject:
tref = OBJECT().ref.nonnull();
break;
case OP_newarray:
tref = ARRAY().ref.nonnull();
break;
case OP_newactivation:
tref = m.activation;
break;
case OP_getglobalscope:
if (m.cx.scopes.length > 0)
{
tref = m.cx.scopes[0];
}
else
{
// same as getscopeobject<0>
v = values.get(e.scopes[0].args[0]);
tref = types.get(e.scopes[0].args[0]);
}
break;
case OP_getscopeobject:
v = values.get(e.scopes[0].args[0]);
tref = types.get(e.scopes[0].args[0]);
if ( tref == null )
{
// FIXME: Should be more thorough.
tref = ANY().ref;
}
break;
case OP_newclass:
tref = e.c.ref.nonnull();
break;
case OP_newfunction:
tref = FUNCTION().ref.nonnull();
break;
case OP_finddef:
if (TypeCache.instance().globals.contains(e.ref))
tref = TypeCache.instance().globals.get(e.ref);
break;
case OP_findpropstrict:
case OP_findproperty:
{
int i = findInner(e.ref, e.scopes, types);
if (i >= 0)
{
v = values.get(e.scopes[i]);
tref = types.get(e.scopes[i]);
}
else if ((i = findOuter(e.ref, m.cx.scopes)) >= 0)
{
tref = m.cx.scopes[i];
}
else if (TypeCache.instance().globals.contains(e.ref))
{
tref = TypeCache.instance().globals.get(e.ref);
}
else
{
// not found. use global.
if (m.cx.scopes.length > 0)
{
tref = m.cx.scopes[0];
}
else
{
v = values.get(e.scopes[0]);
tref = types.get(e.scopes[0]);
}
}
break;
}
case OP_getlex:
{
// findproperty + getproperty
int i = findInner(e.ref, e.scopes, types);
Typeref stref = i >= 0 ? types.get(e.scopes[i]) :
(i=findOuter(e.ref, m.cx.scopes)) >= 0 ? m.cx.scopes[i] :
TypeCache.instance().globals.contains(e.ref) ? TypeCache.instance().globals.get(e.ref) :
m.cx.scopes.length > 0 ? m.cx.scopes[0] :
types.get(e.scopes[0]);
Binding b = stref.t.findGet(e.ref);
// code below is a copy of OP_getproperty
if (isSlot(b))
{
// TODO we only compute const value here if primitive type.
// it would be more correct if we knew whether the initializer
// changed the const value. (consts can be computed in init).
tref = b.type;
if (isConst(b) && b.defaultValueChanged())
v = b.value;
}
else if (isMethod(b))
{
// TODO if base type is or might be XML, return ANY
// TODO use MethodClosure, more specific than Function
tref = FUNCTION().ref.nonnull();
}
else if (isGetter(b))
{
tref = b.method.returns;
}
break;
}
case OP_construct:
{
tref = OBJECT().ref.nonnull();
break;
}
case OP_constructprop:
{
Type ot = type(types,e.args[0]); // type of base object
Binding b = ot.findGet(e.ref);
if (b != null && b.type != null && b.type.t.itype != null)
{
tref = b.type.t.itype.ref.nonnull();
break;
}
break;
}
case OP_callproperty:
case OP_callproplex:
{
Type ot = type(types, e.args[0]);
Binding b = ot.findGet(e.ref);
if (isMethod(b))
{
tref = b.method.returns;
}
else if (isSlot(b) && b.type != null)
{
// each of these has same logic as convert_i, convert_s, etc
if (b.type.t.itype == INT())
{
tref = INT().ref;
if ( e.args.length > 1)
v = eval_convert_i(values.get(e.args[1]));
}
else if (b.type.t.itype == UINT())
{
tref = UINT().ref;
if ( e.args.length > 1)
v = eval_convert_u(values.get(e.args[1]));
}
else if (b.type.t.itype == STRING())
{
tref = STRING().ref.nonnull();
if ( e.args.length > 1)
v = eval_convert_s(values.get(e.args[1]));
}
else if (b.type.t.itype == BOOLEAN())
{
tref = BOOLEAN().ref;
if ( e.args.length > 1)
v = eval_convert_b(values.get(e.args[1]));
}
else if (b.type.t.itype == NUMBER())
{
tref = NUMBER().ref;
if ( e.args.length > 1)
v = eval_convert_d(values.get(e.args[1]));
}
}
break;
}
case OP_applytype:
tref = types.get(e.args[0]).nonnull();
break;
case OP_callstatic:
tref = e.m.returns;
break;
case OP_arg:
if (e.imm[0] < m.getParams().length)
tref = m.getParams()[e.imm[0]];
else if (m.needsArguments()||m.needsRest() && e.imm[0] == m.getParams().length)
tref = ARRAY().ref.nonnull();
else
tref = VOID().ref;
break;
case OP_xarg:
tref = m.handlers[e.imm[0]].type;
break;
case OP_getslot:
{
Type t0 = type(types, e.args[0]);
Binding b = t0.findSlot(e.imm[0]);
if (b != null)
tref = b.type;
break;
}
case OP_getproperty:
{
Type t0 = type(types, e.args[0]);
Binding b = t0.findGet(e.ref);
if (isSlot(b))
{
// TODO we only compute const value here if primitive type.
// it would be more correct if we knew whether the initializer
// changed the const value. (consts can be computed in init).
tref = b.type;
if (isConst(b) && b.defaultValueChanged())
v = b.value;
}
else if (isMethod(b))
{
// TODO if base type is or might be XML, return ANY
// TODO use MethodClosure, more specific than Function
tref = FUNCTION().ref.nonnull();
}
else if (isGetter(b))
{
tref = b.method.returns;
}
break;
}
case OP_pushundefined:
v = e.value;
tref = VOID().ref;
break;
case OP_pushnull:
v = e.value;
tref = NULL().ref;
break;
case OP_pushtrue:
case OP_pushfalse:
v = e.value;
tref = BOOLEAN().ref;
break;
case OP_pushbyte:
case OP_pushshort:
case OP_pushint:
v = e.value;
tref = INT().ref;
break;
case OP_pushuint:
v = e.value;
tref = UINT().ref;
break;
case OP_pushstring:
v = e.value;
tref = STRING().ref.nonnull();
break;
case OP_pushnan:
case OP_pushdouble:
v = e.value;
tref = NUMBER().ref;
break;
case OP_pushnamespace:
v = e.value;
tref = NAMESPACE().ref.nonnull();
break;
case OP_jump:
flowWork.add(e.succ[0]);
return;
case OP_lookupswitch:
{
Object v1 = values.get(e.args[0]);
if (v1 == BOTTOM)
for (Edge s: e.succ)
flowWork.add(s);
else
{
// input is const
int i = intValue(v1);
if (i < 0 || i >= e.succ.length-1)
i = e.succ.length-1;
flowWork.add(e.succ[i]);
}
return;
}
case OP_iffalse:
case OP_iftrue:
{
Object v1 = values.get(e.args[0]);
if (v1 == BOTTOM)
{
flowWork.add(e.succ[0]);
flowWork.add(e.succ[1]);
}
else if (e.op == OP_iffalse)
flowWork.add(e.succ[booleanValue(v1) ? 0 : 1]);
else if (e.op == OP_iftrue)
flowWork.add(e.succ[booleanValue(v1) ? 1 : 0]);
return;
}
case OP_pushscope:
case OP_pushwith:
// treat this as a copy.
v = values.get(e.args[0]);
tref = types.get(e.args[0]).nonnull();
break;
case OP_convert_b:
tref = BOOLEAN().ref;
v = eval_convert_b(values.get(e.args[0]));
break;
case OP_not:
{
tref = BOOLEAN().ref;
Object v0 = values.get(e.args[0]);
if (v0 != BOTTOM)
v = booleanValue(v0) ? FALSE : TRUE;
break;
}
case OP_deleteproperty:
// TODO result is const false for any declared property
case OP_deldescendants:
case OP_hasnext:
case OP_hasnext2:
case OP_equals:
case OP_strictequals:
case OP_in:
case OP_istype:
case OP_istypelate:
case OP_instanceof:
tref = BOOLEAN().ref;
break;
case OP_lessthan:
case OP_lessequals:
case OP_greaterthan:
case OP_greaterequals:
{
tref = BOOLEAN().ref;
Object v0 = values.get(e.args[0]);
Object v1 = values.get(e.args[1]);
if (v0.equals(NAN) || v0 == UNDEFINED || v1.equals(NAN) || v1 == UNDEFINED)
v = FALSE;
else if (v0 != BOTTOM && v1 != BOTTOM)
v = e.op == OP_lessthan ? lessthan(v0,v1) :
e.op == OP_lessequals ? !lessthan(v1,v0) :
e.op == OP_greaterthan ? lessthan(v1,v0) :
!lessthan(v0,v1);
break;
}
case OP_convert_s:
tref = STRING().ref.nonnull();
v = eval_convert_s(values.get(e.args[0]));
break;
case OP_coerce_s:
{
tref = eval_coerce_s(types.get(e.args[0]));
v = eval_coerce_s(values.get(e.args[0]));
break;
}
case OP_coerce_o:
{
Typeref t0 = types.get(e.args[0]);
tref = eval_coerce_o(t0);
v = eval_coerce_o(values.get(e.args[0]), t0.t);
break;
}
case OP_coerce_a:
{
// This cast has meaning if it's casting from void.
// Otherwise, it's an upcast and can be removed;
// casts will be re-inserted as appropriate.
if ( ! (types.get(e.args[0]).equals(VOID().ref) ) )
{
v = values.get(e.args[0]);
tref = types.get(e.args[0]);
}
else
{
tref = ANY().ref;
}
break;
}
case OP_coerce:
{
Typeref t0 = types.get(e.args[0]);
Object v0 = values.get(e.args[0]);
Type t = TypeCache.instance().namedTypes.get(e.ref);
assert ( t != null );
if (t == STRING())
{
tref = eval_coerce_s(t0);
v = eval_coerce_s(v0);
}
else if (t == OBJECT())
{
tref = eval_coerce_o(t0);
v = eval_coerce_o(v0,t0.t);
}
else if (t == INT())
{
tref = t.ref;
v = eval_convert_i(v0);
}
else if (t == UINT())
{
tref = t.ref;
v = eval_convert_u(v0);
}
else if (t == NUMBER())
{
tref = t.ref;
v = eval_convert_d(v0);
}
else if (t == BOOLEAN())
{
tref = t.ref;
v = eval_convert_b(v0);
}
else
{
// pointer style cast
if (t0.t.extendsOrIsBase(t))
{
// ignore upcasts
tref = t0;
v = v0;
}
else if (t0.t == NULL() || t0.t == VOID())
{
tref = NULL().ref;
}
else
{
tref = t.ref;
}
}
break;
}
case OP_astype:
// TODO constant folding
tref = TypeCache.instance().namedTypes.get(e.ref).ref;
break;
case OP_astypelate:
{
Typeref t1 = types.get(e.args[1]);
if (t1.t.itype != null)
{
if (t1.t.itype.atom || t1.t.itype.numeric)
tref = OBJECT().ref;
else
tref = t1.t.itype.ref;
}
else
{
tref = ANY().ref;
}
break;
}
case OP_typeof:
{
Type t0 = type(types,e.args[0]);
if (t0 == INT() || t0 == UINT() || t0 == NUMBER())
v = "number";
else if (t0 == STRING())
v = "string";
else if (t0.extendsOrIsBase(XML()) || t0.extendsOrIsBase(XMLLIST()))
v = "xml";
else if (t0 == VOID())
v = "undefined";
else if (t0 == BOOLEAN())
v = "boolean";
else if (t0.extendsOrIsBase(FUNCTION()))
v = "function";
else if (t0 != OBJECT() && t0.extendsOrIsBase(OBJECT()))
v = "object";
tref = STRING().ref.nonnull();
break;
}
case OP_add:
{
Expr a0 = e.args[0];
Expr a1 = e.args[1];
Typeref t0 = types.get(a0);
Typeref t1 = types.get(a1);
Object v0 = values.get(a0);
Object v1 = values.get(a1);
if (t0.t == STRING() && !t0.nullable || t1.t == STRING() && !t1.nullable)
{
tref = STRING().ref.nonnull();
if (v0 != BOTTOM && v1 != BOTTOM)
v = stringValue(v0) + stringValue(v1);
}
else if (t0.t.numeric && t1.t.numeric)
{
tref = NUMBER().ref;
if (v0 instanceof Number && v1 instanceof Number)
v = doubleValue(v0) + doubleValue(v1);
}
else
{
// TODO make all primitives extend a type so we can use that type here.
tref = OBJECT().ref.nonnull(); // will be a String or a Number
}
break;
}
case OP_divide:
{
tref = NUMBER().ref;
Object v0 = values.get(e.args[0]);
Object v1 = values.get(e.args[1]);
if (v0 instanceof Number && v1 instanceof Number)
v = doubleValue(v0) / doubleValue(v1);
break;
}
case OP_subtract:
case OP_multiply:
case OP_modulo:
case OP_negate:
case OP_increment:
case OP_decrement:
tref = NUMBER().ref;
break;
case OP_convert_d:
tref = NUMBER().ref;
v = eval_convert_d(values.get(e.args[0]));
break;
case OP_convert_i:
tref = INT().ref;
v = eval_convert_i(values.get(e.args[0]));
break;
case OP_convert_u:
tref = UINT().ref;
v = eval_convert_u(values.get(e.args[0]));
break;
case OP_bitor:
{
tref = INT().ref;
Object v0 = values.get(e.args[0]);
Object v1 = values.get(e.args[1]);
if (v0 instanceof Number && v1 instanceof Number)
v = intValue(v0) | intValue(v1);
break;
}
case OP_bitand:
{
tref = INT().ref;
Object v0 = values.get(e.args[0]);
Object v1 = values.get(e.args[1]);
if (v0 instanceof Number && v1 instanceof Number)
{
v = intValue(v0) & intValue(v1);
}
break;
}
case OP_bitnot:
case OP_add_i:
case OP_subtract_i:
case OP_multiply_i:
case OP_negate_i:
case OP_bitxor:
case OP_lshift:
case OP_rshift:
case OP_hasnext2_i:
case OP_increment_i:
case OP_decrement_i:
// TODO constant folding
tref = INT().ref;
break;
case OP_urshift:
// TODO constant folding
tref = UINT().ref;
break;
// these ops do not produce any value
case OP_setslot:
case OP_setproperty:
case OP_setsuper:
//case OP_setglobalslot: // deprecated
case OP_initproperty:
case OP_callpropvoid:
case OP_constructsuper:
case OP_callsupervoid:
case OP_returnvoid:
case OP_returnvalue:
case OP_throw:
case OP_popscope:
case OP_debug:
case OP_debugline:
case OP_debugfile:
case OP_bkpt:
case OP_bkptline:
case OP_checkfilter:
return;
}
}
assert(tref != null && tref.t != null);
// singleton types have a specific value.
if (tref.t == VOID())
v = UNDEFINED;
else if (tref.t == NULL())
v = NULL();
if (v != null && !v.equals(values.get(e)))
{
values.put(e, v);
ssaWork.addAll(uses.get(e));
}
if (!tref.equals(types.get(e)))
{
types.put(e, tref);
ssaWork.addAll(uses.get(e));
}
}