void sccp_modify()

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);
	}