Просмотр исходного кода

2004-07-16 Ben Maurer <[email protected]>

	* assign.cs: IAssignMethod has a new interface, as documented
	inline. All assignment code now uses this new api.

	* ecore.cs, expression.cs: All classes which implement
	IAssignMethod now use the new interface.

	* expression.cs (Invocation): add a hack to EmitCall so that
	IndexerAccess can be the target of a compound assignment without
	evaluating its arguments twice.

	* statement.cs: Handle changes in Invocation api.

svn path=/trunk/mcs/; revision=31240
Ben Maurer 21 лет назад
Родитель
Сommit
524f401e02
5 измененных файлов с 481 добавлено и 449 удалено
  1. 14 0
      mcs/mcs/ChangeLog
  2. 15 44
      mcs/mcs/assign.cs
  3. 113 112
      mcs/mcs/ecore.cs
  4. 336 290
      mcs/mcs/expression.cs
  5. 3 3
      mcs/mcs/statement.cs

+ 14 - 0
mcs/mcs/ChangeLog

@@ -1,3 +1,17 @@
+2004-07-16  Ben Maurer  <[email protected]>
+
+	* assign.cs: IAssignMethod has a new interface, as documented
+	inline. All assignment code now uses this new api.
+
+	* ecore.cs, expression.cs: All classes which implement
+	IAssignMethod now use the new interface.
+
+	* expression.cs (Invocation): add a hack to EmitCall so that
+	IndexerAccess can be the target of a compound assignment without
+	evaluating its arguments twice.
+
+	* statement.cs: Handle changes in Invocation api.
+
 2004-07-12  Martin Baulig  <[email protected]>
 
 	Merged latest changes into gmcs.  Please keep this comment in

+ 15 - 44
mcs/mcs/assign.cs

@@ -29,19 +29,21 @@ namespace Mono.CSharp {
 	/// </remarks>
 	public interface IAssignMethod {
 		//
-		// This method will emit the code for the actual assignment
+		// This is an extra version of Emit. If leave_copy is `true'
+		// A copy of the expression will be left on the stack at the
+		// end of the code generated for EmitAssign
 		//
-		void EmitAssign (EmitContext ec, Expression source);
-
-		//
-		// This method is invoked before any code generation takes
-		// place, and it is a mechanism to inform that the expression
-		// will be invoked more than once, and that the method should
-		// use temporary values to avoid having side effects
+		void Emit (EmitContext ec, bool leave_copy);
+		
 		//
-		// Example: a [ g () ] ++
+		// This method does the assignment
+		// `source' will be stored into the location specified by `this'
+		// if `leave_copy' is true, a copy of `source' will be left on the stack
+		// if `prepare_for_load' is true, when `source' is emitted, there will
+		// be data on the stack that it can use to compuatate its value. This is
+		// for expressions like a [f ()] ++, where you can't call `f ()' twice.
 		//
-		void CacheTemporaries (EmitContext ec);
+		void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
 	}
 
 	/// <summary>
@@ -394,7 +396,7 @@ namespace Mono.CSharp {
 			}
 
 			Expression temp_source = (temp != null) ? temp : source;
-			((IAssignMethod) target).EmitAssign (ec, temp_source);
+			((IAssignMethod) target).EmitAssign (ec, temp_source, false, false);
 			return temp_source;
 		}
 
@@ -416,21 +418,9 @@ namespace Mono.CSharp {
 				((EventExpr) target).EmitAddOrRemove (ec, source);
 				return;
 			}
-
-			bool use_temporaries = false;
 			
-			//
-			// FIXME! We need a way to "probe" if the process can
-			// just use `dup' to propagate the result
-			// 
 			IAssignMethod am = (IAssignMethod) target;
 
-			if (this is CompoundAssign)
-				am.CacheTemporaries (ec);
-
-			if (!is_statement)
-				use_temporaries = true;
-
 			Expression temp_source;
 			if (embedded != null) {
 				temp_source = embedded.EmitEmbedded (ec);
@@ -442,27 +432,8 @@ namespace Mono.CSharp {
 				}
 			} else
 				temp_source = source;
-
-			if (use_temporaries){
-				//
-				// Doing this for every path is too expensive
-				// I wonder if we can work around this and have a less
-				// expensive path
-				//
-				LocalTemporary tempo;
-				
-				tempo = new LocalTemporary (ec, source.Type);
-				
-				temp_source.Emit (ec);
-				tempo.Store (ec);
-				am.EmitAssign (ec, tempo);
-				if (!is_statement)
-					tempo.Emit (ec);
-				
-				tempo.Release (ec);
-			} else {
-				am.EmitAssign (ec, temp_source);
-			}
+		
+			am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign);
 				
 			if (embedded != null) {
 				if (temp != null)

+ 113 - 112
mcs/mcs/ecore.cs

@@ -1238,13 +1238,6 @@ namespace Mono.CSharp {
 				return 0;
 		}
 
-		//
-		// Default implementation of IAssignMethod.CacheTemporaries
-		//
-		public virtual void CacheTemporaries (EmitContext ec)
-		{
-		}
-
 		static void Error_NegativeArrayIndex (Location loc)
 		{
 			Report.Error (284, loc, "Can not create array with a negative size");
@@ -2561,9 +2554,8 @@ namespace Mono.CSharp {
 		Expression instance_expr;
 		VariableInfo variable_info;
 
-		LocalTemporary temporary;
-		IMemoryLocation instance_ml;
-		bool have_temporary;
+		LocalTemporary temp;
+		bool prepared;
 		
 		public FieldExpr (FieldInfo fi, Location l)
 		{
@@ -2724,37 +2716,8 @@ namespace Mono.CSharp {
 
 			return true;
 		}
-
-		public override void CacheTemporaries (EmitContext ec)
-		{
-			if (!FieldInfo.IsStatic && (temporary == null))
-				temporary = new LocalTemporary (ec, instance_expr.Type);
-		}
-
-		void EmitInstance (EmitContext ec)
-		{
-			if (instance_expr.Type.IsValueType)
-				CacheTemporaries (ec);
-
-			if ((temporary == null) || have_temporary)
-				return;
-
-			if (instance_expr.Type.IsValueType) {
-				instance_ml = instance_expr as IMemoryLocation;
-				if (instance_ml == null) {
-					instance_expr.Emit (ec);
-					temporary.Store (ec);
-					instance_ml = temporary;
-				}
-			} else {
-				instance_expr.Emit (ec);
-				temporary.Store (ec);
-			}
-
-			have_temporary = true;
-		}
-
-		override public void Emit (EmitContext ec)
+		
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
 			ILGenerator ig = ec.ig;
 			bool is_volatile = false;
@@ -2774,46 +2737,52 @@ namespace Mono.CSharp {
 					ig.Emit (OpCodes.Volatile);
 				
 				ig.Emit (OpCodes.Ldsfld, FieldInfo);
-				return;
+			} else {
+				if (!prepared)
+					EmitInstance (ec);
+				
+				if (is_volatile)
+					ig.Emit (OpCodes.Volatile);
+				
+				ig.Emit (OpCodes.Ldfld, FieldInfo);
 			}
-
-			EmitInstance (ec);
-			if (instance_ml != null)
-				instance_ml.AddressOf (ec, AddressOp.Load);
-			else if (temporary != null)
-				temporary.Emit (ec);
-			else
-				instance_expr.Emit (ec);
-
-			if (is_volatile)
-				ig.Emit (OpCodes.Volatile);
 			
-			ig.Emit (OpCodes.Ldfld, FieldInfo);
+			if (leave_copy) {	
+				ec.ig.Emit (OpCodes.Dup);
+				if (!FieldInfo.IsStatic) {
+					temp = new LocalTemporary (ec, this.Type);
+					temp.Store (ec);
+				}
+			}
 		}
-
-		public void EmitAssign (EmitContext ec, Expression source)
+		
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
 		{
 			FieldAttributes fa = FieldInfo.Attributes;
 			bool is_static = (fa & FieldAttributes.Static) != 0;
 			bool is_readonly = (fa & FieldAttributes.InitOnly) != 0;
 			ILGenerator ig = ec.ig;
+			prepared = prepare_for_load;
 
 			if (is_readonly && !ec.IsConstructor){
 				Report_AssignToReadonly (!is_static);
 				return;
 			}
 
-			if (!is_static){
+			if (!is_static) {
 				EmitInstance (ec);
-				if (instance_ml != null)
-					instance_ml.AddressOf (ec, AddressOp.Store);
-				else if (temporary != null)
-					temporary.Emit (ec);
-				else
-					instance_expr.Emit (ec);
+				if (prepare_for_load)
+					ig.Emit (OpCodes.Dup);
 			}
 
 			source.Emit (ec);
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				if (!FieldInfo.IsStatic) {
+					temp = new LocalTemporary (ec, this.Type);
+					temp.Store (ec);
+				}
+			}
 
 			if (FieldInfo is FieldBuilder){
 				FieldBase f = TypeManager.GetField (FieldInfo);
@@ -2829,6 +2798,29 @@ namespace Mono.CSharp {
 				ig.Emit (OpCodes.Stsfld, FieldInfo);
 			else 
 				ig.Emit (OpCodes.Stfld, FieldInfo);
+			
+			if (temp != null)
+				temp.Emit (ec);
+		}
+
+		void EmitInstance (EmitContext ec)
+		{
+			if (instance_expr.Type.IsValueType) {
+				if (instance_expr is IMemoryLocation) {
+					((IMemoryLocation) instance_expr).AddressOf (ec, AddressOp.LoadStore);
+				} else {
+					LocalTemporary t = new LocalTemporary (ec, instance_expr.Type);
+					instance_expr.Emit (ec);
+					t.Store (ec);
+					t.AddressOf (ec, AddressOp.Store);
+				}
+			} else
+				instance_expr.Emit (ec);
+		}
+
+		public override void Emit (EmitContext ec)
+		{
+			Emit (ec, false);
 		}
 		
 		public void AddressOf (EmitContext ec, AddressOp mode)
@@ -2880,20 +2872,7 @@ namespace Mono.CSharp {
 			if (FieldInfo.IsStatic){
 				ig.Emit (OpCodes.Ldsflda, FieldInfo);
 			} else {
-				//
-				// In the case of `This', we call the AddressOf method, which will
-				// only load the pointer, and not perform an Ldobj immediately after
-				// the value has been loaded into the stack.
-				//
 				EmitInstance (ec);
-				if (instance_ml != null)
-					instance_ml.AddressOf (ec, AddressOp.LoadStore);
-				else if (temporary != null)
-					temporary.Emit (ec);
-				else if (instance_expr is This)
-					((This)instance_expr).AddressOf (ec, AddressOp.LoadStore);
-				else
-					instance_expr.Emit (ec);
 				ig.Emit (OpCodes.Ldflda, FieldInfo);
 			}
 		}
@@ -2932,8 +2911,8 @@ namespace Mono.CSharp {
 		bool must_do_cs1540_check;
 		
 		Expression instance_expr;
-		LocalTemporary temporary;
-		bool have_temporary;
+		LocalTemporary temp;
+		bool prepared;
 
 		public PropertyExpr (EmitContext ec, PropertyInfo pi, Location l)
 		{
@@ -3240,38 +3219,40 @@ namespace Mono.CSharp {
 			return this;
 		}
 
-		public override void CacheTemporaries (EmitContext ec)
+
+		
+		public override void Emit (EmitContext ec)
 		{
-			if (!is_static){
-				// we need to do indirection on the pointer
-				bool need_address = instance_expr.Type.IsValueType;
-				temporary = new LocalTemporary (ec, instance_expr.Type, need_address);
-			}
+			Emit (ec, false);
 		}
-
-		Expression EmitInstance (EmitContext ec)
+		
+		void EmitInstance (EmitContext ec)
 		{
-			if (temporary != null){
-				if (!have_temporary){
-					if (temporary.PointsToAddress){
-						// must store the managed pointer
-						IMemoryLocation loc = instance_expr as IMemoryLocation;
-						loc.AddressOf (ec, AddressOp.LoadStore);
-					} else 
-						instance_expr.Emit (ec);
-					temporary.Store (ec);
-						
-					have_temporary = true;
+			if (is_static)
+				return;
+
+			if (instance_expr.Type.IsValueType) {
+				if (instance_expr is IMemoryLocation) {
+					((IMemoryLocation) instance_expr).AddressOf (ec, AddressOp.LoadStore);
+				} else {
+					LocalTemporary t = new LocalTemporary (ec, instance_expr.Type);
+					instance_expr.Emit (ec);
+					t.Store (ec);
+					t.AddressOf (ec, AddressOp.Store);
 				}
-				return temporary;
 			} else
-				return instance_expr;
+				instance_expr.Emit (ec);
+			
+			if (prepared)
+				ec.ig.Emit (OpCodes.Dup);
 		}
 
-		override public void Emit (EmitContext ec)
+		
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
-			Expression expr = EmitInstance (ec);
-
+			if (!prepared)
+				EmitInstance (ec);
+			
 			//
 			// Special case: length of single dimension array property is turned into ldlen
 			//
@@ -3283,30 +3264,50 @@ namespace Mono.CSharp {
 				// System.Array.Length can be called, but the Type does not
 				// support invoking GetArrayRank, so test for that case first
 				//
-				if (iet != TypeManager.array_type && (iet.GetArrayRank () == 1)){
-					expr.Emit (ec);
+				if (iet != TypeManager.array_type && (iet.GetArrayRank () == 1)) {
 					ec.ig.Emit (OpCodes.Ldlen);
 					ec.ig.Emit (OpCodes.Conv_I4);
 					return;
 				}
 			}
 
-			Invocation.EmitCall (ec, IsBase, IsStatic, expr, getter, null, loc);
+			Invocation.EmitCall (ec, IsBase, IsStatic, new EmptyAddressOf (), getter, null, loc);
+			
+			if (!leave_copy)
+				return;
 			
+			ec.ig.Emit (OpCodes.Dup);
+			if (!is_static) {
+				temp = new LocalTemporary (ec, this.Type);
+				temp.Store (ec);
+			}
 		}
 
 		//
 		// Implements the IAssignMethod interface for assignments
 		//
-		public void EmitAssign (EmitContext ec, Expression source)
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
 		{
-			Expression expr = EmitInstance (ec);
-
-			Argument arg = new Argument (source, Argument.AType.Expression);
-			ArrayList args = new ArrayList ();
+			prepared = prepare_for_load;
+			
+			EmitInstance (ec);
 
-			args.Add (arg);
-			Invocation.EmitCall (ec, IsBase, IsStatic, expr, setter, args, loc);
+			source.Emit (ec);
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				if (!is_static) {
+					temp = new LocalTemporary (ec, this.Type);
+					temp.Store (ec);
+				}
+			}
+			
+			ArrayList args = new ArrayList (1);
+			args.Add (new Argument (new EmptyAddressOf (), Argument.AType.Expression));
+			
+			Invocation.EmitCall (ec, IsBase, IsStatic, new EmptyAddressOf (), setter, args, loc);
+			
+			if (temp != null)
+				temp.Emit (ec);
 		}
 
 		override public void EmitStatement (EmitContext ec)

+ 336 - 290
mcs/mcs/expression.cs

@@ -46,13 +46,13 @@ namespace Mono.CSharp {
 		public override void Emit (EmitContext ec)
 		{
 			if (args != null) 
-				Invocation.EmitArguments (ec, mi, args);
+				Invocation.EmitArguments (ec, mi, args, false, null);
 
 			ec.ig.Emit (OpCodes.Call, mi);
 			return;
 		}
 		
-		static public Expression MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
+		static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
 							 Expression e, Location loc)
 		{
 			ArrayList args;
@@ -80,6 +80,10 @@ namespace Mono.CSharp {
 			if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
 				ec.ig.Emit (OpCodes.Pop);
 		}
+		
+		public MethodInfo Method {
+			get { return mi; }
+		}
 	}
 
 	public class ParenthesizedExpression : Expression
@@ -643,7 +647,7 @@ namespace Mono.CSharp {
 	public class Indirection : Expression, IMemoryLocation, IAssignMethod {
 		Expression expr;
 		LocalTemporary temporary;
-		bool have_temporary;
+		bool prepared;
 		
 		public Indirection (Expression expr, Location l)
 		{
@@ -659,54 +663,47 @@ namespace Mono.CSharp {
 		
 		public override void Emit (EmitContext ec)
 		{
-			ILGenerator ig = ec.ig;
-
-			if (temporary != null){
-				if (have_temporary) {
-					temporary.Emit (ec);
-				} else {
-					expr.Emit (ec);
-					ec.ig.Emit (OpCodes.Dup);
-					temporary.Store (ec);
-					have_temporary = true;
-				}
-			} else
+			if (!prepared)
 				expr.Emit (ec);
 			
-			LoadFromPtr (ig, Type);
+			LoadFromPtr (ec.ig, Type);
 		}
 
-		public void EmitAssign (EmitContext ec, Expression source)
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
-			if (temporary != null){
-				if (have_temporary)
-					temporary.Emit (ec);
-				else {
-					expr.Emit (ec);
-					ec.ig.Emit (OpCodes.Dup);
-					temporary.Store (ec);
-					have_temporary = true;
-				}
-			} else 
-				expr.Emit (ec);
+			Emit (ec);
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				temporary = new LocalTemporary (ec, expr.Type);
+				temporary.Store (ec);
+			}
+		}
+		
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+		{
+			prepared = prepare_for_load;
+			
+			expr.Emit (ec);
 
+			if (prepare_for_load)
+				ec.ig.Emit (OpCodes.Dup);
+			
 			source.Emit (ec);
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				temporary = new LocalTemporary (ec, expr.Type);
+				temporary.Store (ec);
+			}
+			
 			StoreFromPtr (ec.ig, type);
+			
+			if (temporary != null)
+				temporary.Emit (ec);
 		}
 		
 		public void AddressOf (EmitContext ec, AddressOp Mode)
 		{
-			if (temporary != null){
-				if (have_temporary){
-					temporary.Emit (ec);
-					return;
-				}
-				expr.Emit (ec);
-				ec.ig.Emit (OpCodes.Dup);
-				temporary.Store (ec);
-				have_temporary = true;
-			} else
-				expr.Emit (ec);
+			expr.Emit (ec);
 		}
 
 		public override Expression DoResolve (EmitContext ec)
@@ -716,12 +713,7 @@ namespace Mono.CSharp {
 			//
 			return this;
 		}
-
-		public new void CacheTemporaries (EmitContext ec)
-		{
-			temporary = new LocalTemporary (ec, expr.Type);
-		}
-
+		
 		public override string ToString ()
 		{
 			return "*(" + expr + ")";
@@ -757,13 +749,15 @@ namespace Mono.CSharp {
 		}
 		
 		Mode mode;
+		bool is_expr = false;
+		bool recurse = false;
+		
 		Expression expr;
-		LocalTemporary temp_storage;
 
 		//
 		// This is expensive for the simplest case.
 		//
-		Expression method;
+		StaticCallExpr method;
 			
 		public UnaryMutator (Mode m, Expression e, Location l)
 		{
@@ -855,9 +849,7 @@ namespace Mono.CSharp {
 			} else if (expr.eclass == ExprClass.IndexerAccess){
 				IndexerAccess ia = (IndexerAccess) expr;
 				
-				temp_storage = new LocalTemporary (ec, expr.Type);
-				
-				expr = ia.ResolveLValue (ec, temp_storage);
+				expr = ia.ResolveLValue (ec, this);
 				if (expr == null)
 					return null;
 
@@ -975,114 +967,34 @@ namespace Mono.CSharp {
 			}
 			
 		}
-
-		static EmptyExpression empty_expr;
 		
 		void EmitCode (EmitContext ec, bool is_expr)
 		{
-			ILGenerator ig = ec.ig;
-			IAssignMethod ia = (IAssignMethod) expr;
-			Type expr_type = expr.Type;
-
-			ia.CacheTemporaries (ec);
-
-			//
-			// NOTE: We should probably handle three cases:
-			//
-			//     * method invocation required.
-			//     * direct stack manipulation possible
-			//     * the object requires an "instance" field
-			//
-			if (temp_storage == null){
-				//
-				// Temporary improvement: if we are dealing with something that does
-				// not require complicated instance setup, avoid using a temporary
-				//
-				// For now: only localvariables when not remapped
-				//
-
-				if (method == null &&
-				    ((expr is LocalVariableReference) ||(expr is FieldExpr && ((FieldExpr) expr).FieldInfo.IsStatic))){
-					if (empty_expr == null)
-						empty_expr = new EmptyExpression ();
-					
-					switch (mode){
-					case Mode.PreIncrement:
-					case Mode.PreDecrement:
-						expr.Emit (ec);
-					
-						LoadOneAndEmitOp (ec, expr_type);
-						if (is_expr)
-							ig.Emit (OpCodes.Dup);
-						ia.EmitAssign (ec, empty_expr);
-						break;
-						
-					case Mode.PostIncrement:
-					case Mode.PostDecrement:
-						expr.Emit (ec);
-						if (is_expr)
-							ig.Emit (OpCodes.Dup);
-						
-						LoadOneAndEmitOp (ec, expr_type);
-						ia.EmitAssign (ec, empty_expr);
-						break;
-					}
-					return;
-				}
-				temp_storage = new LocalTemporary (ec, expr_type);
-			}
-			
-			switch (mode){
-			case Mode.PreIncrement:
-			case Mode.PreDecrement:
-				if (method == null){
-					expr.Emit (ec);
-					
-					LoadOneAndEmitOp (ec, expr_type);
-				} else 
-					method.Emit (ec);
-				
-				temp_storage.Store (ec);
-				ia.EmitAssign (ec, temp_storage);
-				if (is_expr)
-					temp_storage.Emit (ec);
-				break;
-				
-			case Mode.PostIncrement:
-			case Mode.PostDecrement:
-				if (is_expr)
-					expr.Emit (ec);
-				
-				if (method == null){
-					if (!is_expr)
-						expr.Emit (ec);
-					else
-						ig.Emit (OpCodes.Dup);
-					
-					LoadOneAndEmitOp (ec, expr_type);
-				} else {
-					method.Emit (ec);
-				}
-				
-				temp_storage.Store (ec);
-				ia.EmitAssign (ec, temp_storage);
-				break;
-			}
-
-			temp_storage.Release (ec);
+			recurse = true;
+			this.is_expr = is_expr;
+			((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
 		}
+		
 
 		public override void Emit (EmitContext ec)
 		{
-			EmitCode (ec, true);
+			if (recurse) {
+				((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement  || mode == Mode.PostDecrement));
+				if (method == null)
+					LoadOneAndEmitOp (ec, expr.Type);
+				else
+					ec.ig.Emit (OpCodes.Call, method.Method);
+				recurse = false;
+				return;
+			}
 			
+			EmitCode (ec, true);
 		}
 		
 		public override void EmitStatement (EmitContext ec)
 		{
 			EmitCode (ec, false);
 		}
-
 	}
 
 	/// <summary>
@@ -3142,7 +3054,7 @@ namespace Mono.CSharp {
 			ILGenerator ig = ec.ig;
 			
 			if (Arguments != null) 
-				Invocation.EmitArguments (ec, method, Arguments);
+				Invocation.EmitArguments (ec, method, Arguments, false, null);
 			
 			if (method is MethodInfo)
 				ig.Emit (OpCodes.Call, (MethodInfo) method);
@@ -3277,7 +3189,7 @@ namespace Mono.CSharp {
 				break;
 			}
 			
-			Invocation.EmitArguments (ec, concat_method, operands);
+			Invocation.EmitArguments (ec, concat_method, operands, false, null);
 			ec.ig.Emit (OpCodes.Call, concat_method);
 		}
 	}
@@ -3306,7 +3218,7 @@ namespace Mono.CSharp {
 		{
 			ILGenerator ig = ec.ig;
 			
-			Invocation.EmitArguments (ec, method, args);
+			Invocation.EmitArguments (ec, method, args, false, null);
 			
 			ig.Emit (OpCodes.Call, (MethodInfo) method);
 			ig.Emit (OpCodes.Castclass, type);
@@ -3733,12 +3645,19 @@ namespace Mono.CSharp {
 			ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
 		}
 		
-		public void EmitAssign (EmitContext ec, Expression source)
+		public void Emit (EmitContext ec, bool leave_copy)
+		{
+			Emit (ec);
+			if (leave_copy)
+				ec.ig.Emit (OpCodes.Dup);
+		}
+		
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
 		{
-			ILGenerator ig = ec.ig;
-
 			source.Emit (ec);
-			ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
+			if (leave_copy)
+				ec.ig.Emit (OpCodes.Dup);
+			ec.ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
 		}
 		
 		public void AddressOf (EmitContext ec, AddressOp mode)
@@ -3765,7 +3684,8 @@ namespace Mono.CSharp {
 		Block block;
 		VariableInfo vi;
 		public Parameter.Modifier mod;
-		public bool is_ref, is_out;
+		public bool is_ref, is_out, prepared;
+		LocalTemporary temp;
 		
 		public ParameterReference (Parameters pars, Block block, int idx, string name, Location loc)
 		{
@@ -3900,6 +3820,11 @@ namespace Mono.CSharp {
 		}
 		
 		public override void Emit (EmitContext ec)
+		{
+			Emit (ec, false);
+		}
+		
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
 			ILGenerator ig = ec.ig;
 			
@@ -3910,33 +3835,56 @@ namespace Mono.CSharp {
 
 			EmitLdArg (ig, arg_idx);
 
-			if (!is_ref)
-				return;
-
-			//
-			// If we are a reference, we loaded on the stack a pointer
-			// Now lets load the real value
-			//
-			LoadFromPtr (ig, type);
+			if (is_ref) {
+				if (prepared)
+					ec.ig.Emit (OpCodes.Dup);
+	
+				//
+				// If we are a reference, we loaded on the stack a pointer
+				// Now lets load the real value
+				//
+				LoadFromPtr (ig, type);
+			}
+			
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				
+				if (is_ref) {
+					temp = new LocalTemporary (ec, type);
+					temp.Store (ec);
+				}
+			}
 		}
-
-		public void EmitAssign (EmitContext ec, Expression source)
+		
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
 		{
 			ILGenerator ig = ec.ig;
-			
 			int arg_idx = idx;
-
+			
+			prepared = prepare_for_load;
+			
 			if (!ec.IsStatic)
 				arg_idx++;
 
-			if (is_ref)
+			if (is_ref && !prepared)
 				EmitLdArg (ig, arg_idx);
 			
 			source.Emit (ec);
 
-			if (is_ref)
+			if (leave_copy)
+				ec.ig.Emit (OpCodes.Dup);
+			
+			if (is_ref) {
+				if (leave_copy) {
+					temp = new LocalTemporary (ec, type);
+					temp.Store (ec);
+				}
+				
 				StoreFromPtr (ig, type);
-			else {
+				
+				if (temp != null)
+					temp.Emit (ec);
+			} else {
 				if (arg_idx <= 255)
 					ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
 				else
@@ -5082,14 +5030,24 @@ namespace Mono.CSharp {
 		///   emission of the arguments is known not to contain
 		///   a `params' field (for example in constructors or other routines
 		///   that keep their arguments in this structure)
+		///   
+		///   if `dup_args' is true, a copy of the arguments will be left
+		///   on the stack. If `dup_args' is true, you can specify `this_arg'
+		///   which will be duplicated before any other args. Only EmitCall
+		///   should be using this interface.
 		/// </summary>
-		public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments)
+		public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
 		{
 			ParameterData pd;
 			if (mb != null)
 				pd = GetParameterData (mb);
 			else
 				pd = null;
+			
+			LocalTemporary [] temps = null;
+			
+			if (dup_args)
+				temps = new LocalTemporary [arguments.Count];
 
 			//
 			// If we are calling a params method with no arguments, special case it
@@ -5126,6 +5084,18 @@ namespace Mono.CSharp {
 				}
 					    
 				a.Emit (ec);
+				if (dup_args) {
+					ec.ig.Emit (OpCodes.Dup);
+					(temps [i] = new LocalTemporary (ec, a.Type)).Store (ec);
+				}
+			}
+			
+			if (dup_args) {
+				if (this_arg != null)
+					this_arg.Emit (ec);
+				
+				for (int i = 0; i < top; i ++)
+					temps [i].Emit (ec);
 			}
 
 			if (pd != null && pd.Count > top &&
@@ -5189,10 +5159,25 @@ namespace Mono.CSharp {
 		public static void EmitCall (EmitContext ec, bool is_base,
 					     bool is_static, Expression instance_expr,
 					     MethodBase method, ArrayList Arguments, Location loc)
+		{
+			EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
+		}
+		
+		// `dup_args' leaves an extra copy of the arguments on the stack
+		// `omit_args' does not leave any arguments at all.
+		// So, basically, you could make one call with `dup_args' set to true,
+		// and then another with `omit_args' set to true, and the two calls
+		// would have the same set of arguments. However, each argument would
+		// only have been evaluated once.
+		public static void EmitCall (EmitContext ec, bool is_base,
+					     bool is_static, Expression instance_expr,
+					     MethodBase method, ArrayList Arguments, Location loc,
+		                             bool dup_args, bool omit_args)
 		{
 			ILGenerator ig = ec.ig;
 			bool struct_call = false;
 			bool this_call = false;
+			LocalTemporary this_arg = null;
 
 			Type decl_type = method.DeclaringType;
 
@@ -5232,26 +5217,28 @@ namespace Mono.CSharp {
                 return; 
 			
 			if (!is_static){
-				if (decl_type.IsValueType)
+				this_call = instance_expr == null;
+				if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
 					struct_call = true;
+				
 				//
 				// If this is ourselves, push "this"
 				//
-				if (instance_expr == null) {
-					this_call = true;
+				if (!omit_args) {
+				Type t = null;
+				if (this_call) {
 					ig.Emit (OpCodes.Ldarg_0);
+					t = decl_type;
 				} else {
 					//
 					// Push the instance expression
 					//
-					if (instance_expr.Type.IsValueType){
+					if (instance_expr.Type.IsValueType) {
 						//
 						// Special case: calls to a function declared in a 
 						// reference-type with a value-type argument need
-						// to have their value boxed.  
-
-						struct_call = true;
-						if (decl_type.IsValueType){
+						// to have their value boxed.
+						if (decl_type.IsValueType) {
 							//
 							// If the expression implements IMemoryLocation, then
 							// we can optimize and use AddressOf on the
@@ -5259,28 +5246,40 @@ namespace Mono.CSharp {
 							//
 							// If not we have to use some temporary storage for
 							// it.
-							if (instance_expr is IMemoryLocation){
+							if (instance_expr is IMemoryLocation) {
 								((IMemoryLocation)instance_expr).
 									AddressOf (ec, AddressOp.LoadStore);
-							}
-							else {
-								Type t = instance_expr.Type;
-								
+							} else {
+								LocalTemporary temp = new LocalTemporary (ec, instance_expr.Type);
 								instance_expr.Emit (ec);
-								LocalBuilder temp = ig.DeclareLocal (t);
-								ig.Emit (OpCodes.Stloc, temp);
-								ig.Emit (OpCodes.Ldloca, temp);
+								temp.Store (ec);
+								temp.AddressOf (ec, AddressOp.Load);
 							}
+							
+							// avoid the overhead of doing this all the time.
+							if (dup_args)
+								t = TypeManager.GetReferenceType (instance_expr.Type);
 						} else {
 							instance_expr.Emit (ec);
 							ig.Emit (OpCodes.Box, instance_expr.Type);
+							t = TypeManager.object_type;
 						} 
-					} else
+					} else {
 						instance_expr.Emit (ec);
+						t = instance_expr.Type;
+					}
+				}
+				
+				if (dup_args) {
+					this_arg = new LocalTemporary (ec, t);
+					ig.Emit (OpCodes.Dup);
+					this_arg.Store (ec);
+				}
 				}
 			}
 
-			EmitArguments (ec, method, Arguments);
+			if (!omit_args)
+				EmitArguments (ec, method, Arguments, dup_args, this_arg);
 
 			OpCode call_op;
 			if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
@@ -5642,7 +5641,7 @@ namespace Mono.CSharp {
 			}
 
 			if (method != null)
-				Invocation.EmitArguments (ec, method, Arguments);
+				Invocation.EmitArguments (ec, method, Arguments, false, null);
 
 			if (is_value_type){
 				if (method == null)
@@ -5689,7 +5688,7 @@ namespace Mono.CSharp {
 			IMemoryLocation ml = (IMemoryLocation) value_target;
 			ml.AddressOf (ec, AddressOp.Store);
 			if (method != null)
-				Invocation.EmitArguments (ec, method, Arguments);
+				Invocation.EmitArguments (ec, method, Arguments, false, null);
 
 			if (method == null)
 				ec.ig.Emit (OpCodes.Initobj, type);
@@ -6372,9 +6371,14 @@ namespace Mono.CSharp {
 
 						e.Emit (ec);
 
-						if (dims == 1)
-							ArrayAccess.EmitStoreOpcode (ig, array_element_type);
-						else 
+						if (dims == 1) {
+							bool is_stobj;
+							OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj);
+							if (is_stobj)
+								ig.Emit (OpCodes.Stobj, etype);
+							else
+								ig.Emit (op);
+						} else 
 							ig.Emit (OpCodes.Call, set);
 
 					}
@@ -6561,28 +6565,36 @@ namespace Mono.CSharp {
 			return this;
 		}
 
-		public override void Emit (EmitContext ec)
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
-			ILGenerator ig = ec.ig;
-
-			ec.EmitThis ();
-			if (ec.TypeContainer is Struct)
-				ig.Emit (OpCodes.Ldobj, type);
+			Emit (ec);
+			if (leave_copy)
+				ec.ig.Emit (OpCodes.Dup);
 		}
-
-		public void EmitAssign (EmitContext ec, Expression source)
+		
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
 		{
 			ILGenerator ig = ec.ig;
 			
 			if (ec.TypeContainer is Struct){
 				ec.EmitThis ();
 				source.Emit (ec);
+				if (leave_copy)
+					ec.ig.Emit (OpCodes.Dup);
 				ig.Emit (OpCodes.Stobj, type);
 			} else {
-				source.Emit (ec);
-				ig.Emit (OpCodes.Starg, 0);
+				throw new Exception ("how did you get here");
 			}
 		}
+		
+		public override void Emit (EmitContext ec)
+		{
+			ILGenerator ig = ec.ig;
+
+			ec.EmitThis ();
+			if (ec.TypeContainer is Struct)
+				ig.Emit (OpCodes.Ldobj, type);
+		}
 
 		public void AddressOf (EmitContext ec, AddressOp mode)
 		{
@@ -7434,7 +7446,8 @@ namespace Mono.CSharp {
 		//
 		ElementAccess ea;
 
-		LocalTemporary [] cached_locations;
+		LocalTemporary temp;
+		bool prepared;
 		
 		public ArrayAccess (ElementAccess ea_data, Location l)
 		{
@@ -7533,20 +7546,6 @@ namespace Mono.CSharp {
 				ig.Emit (OpCodes.Ldelem_Ref);
 		}
 
-		/// <summary>
-		///    Emits the right opcode to store an object of Type `t'
-		///    from an array of T.  
-		/// </summary>
-		static public void EmitStoreOpcode (ILGenerator ig, Type t)
-		{
-			bool is_stobj;
-			OpCode op = GetStoreOpcode (t, out is_stobj);
-			if (is_stobj)
-				ig.Emit (OpCodes.Stobj, t);
-			else
-				ig.Emit (op);
-		}
-
 		/// <summary>
 		///    Returns the right opcode to store an object of Type `t'
 		///    from an array of T.  
@@ -7638,101 +7637,111 @@ namespace Mono.CSharp {
 		{
 			ILGenerator ig = ec.ig;
 			
-			if (cached_locations == null){
-				ea.Expr.Emit (ec);
-				foreach (Argument a in ea.Arguments){
-					Type argtype = a.Expr.Type;
-					
-					a.Expr.Emit (ec);
-					
-					if (argtype == TypeManager.int64_type)
-						ig.Emit (OpCodes.Conv_Ovf_I);
-					else if (argtype == TypeManager.uint64_type)
-						ig.Emit (OpCodes.Conv_Ovf_I_Un);
-				}
-				return;
-			}
-
-			if (cached_locations [0] == null){
-				cached_locations [0] = new LocalTemporary (ec, ea.Expr.Type);
-				ea.Expr.Emit (ec);
-				ig.Emit (OpCodes.Dup);
-				cached_locations [0].Store (ec);
+			ea.Expr.Emit (ec);
+			foreach (Argument a in ea.Arguments){
+				Type argtype = a.Expr.Type;
 				
-				int j = 1;
+				a.Expr.Emit (ec);
 				
-				foreach (Argument a in ea.Arguments){
-					Type argtype = a.Expr.Type;
-					
-					cached_locations [j] = new LocalTemporary (ec, TypeManager.intptr_type /* a.Expr.Type */);
-					a.Expr.Emit (ec);
-					if (argtype == TypeManager.int64_type)
-						ig.Emit (OpCodes.Conv_Ovf_I);
-					else if (argtype == TypeManager.uint64_type)
-						ig.Emit (OpCodes.Conv_Ovf_I_Un);
-
-					ig.Emit (OpCodes.Dup);
-					cached_locations [j].Store (ec);
-					j++;
-				}
-				return;
+				if (argtype == TypeManager.int64_type)
+					ig.Emit (OpCodes.Conv_Ovf_I);
+				else if (argtype == TypeManager.uint64_type)
+					ig.Emit (OpCodes.Conv_Ovf_I_Un);
 			}
-
-			foreach (LocalTemporary lt in cached_locations)
-				lt.Emit (ec);
 		}
 
-		public new void CacheTemporaries (EmitContext ec)
-		{
-			cached_locations = new LocalTemporary [ea.Arguments.Count + 1];
-		}
-		
-		public override void Emit (EmitContext ec)
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
 			int rank = ea.Expr.Type.GetArrayRank ();
 			ILGenerator ig = ec.ig;
 
-			LoadArrayAndArguments (ec);
-			
-			if (rank == 1)
-				EmitLoadOpcode (ig, type);
-			else {
-				MethodInfo method;
+			if (!prepared) {
+				LoadArrayAndArguments (ec);
 				
-				method = FetchGetMethod ();
-				ig.Emit (OpCodes.Call, method);
+				if (rank == 1)
+					EmitLoadOpcode (ig, type);
+				else {
+					MethodInfo method;
+					
+					method = FetchGetMethod ();
+					ig.Emit (OpCodes.Call, method);
+				}
+			} else
+				LoadFromPtr (ec.ig, this.type);
+			
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				temp = new LocalTemporary (ec, this.type);
+				temp.Store (ec);
 			}
 		}
+		
+		public override void Emit (EmitContext ec)
+		{
+			Emit (ec, false);
+		}
 
-		public void EmitAssign (EmitContext ec, Expression source)
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
 		{
 			int rank = ea.Expr.Type.GetArrayRank ();
 			ILGenerator ig = ec.ig;
 			Type t = source.Type;
+			prepared = prepare_for_load;
 
-			LoadArrayAndArguments (ec);
-
-			//
-			// The stobj opcode used by value types will need
-			// an address on the stack, not really an array/array
-			// pair
-			//
-			if (rank == 1){
-				if (t == TypeManager.enum_type || t == TypeManager.decimal_type ||
-				    (t.IsSubclassOf (TypeManager.value_type) && !TypeManager.IsEnumType (t) && !TypeManager.IsBuiltinType (t)))
-					ig.Emit (OpCodes.Ldelema, t);
+			if (prepare_for_load) {
+				AddressOf (ec, AddressOp.LoadStore);
+				ec.ig.Emit (OpCodes.Dup);
+				source.Emit (ec);
+				if (leave_copy) {
+					ec.ig.Emit (OpCodes.Dup);
+					temp = new LocalTemporary (ec, this.type);
+					temp.Store (ec);
+				}
+				StoreFromPtr (ec.ig, t);
+				
+				if (temp != null)
+					temp.Emit (ec);
+				
+				return;
 			}
 			
-			source.Emit (ec);
+			LoadArrayAndArguments (ec);
 
-			if (rank == 1)
-				EmitStoreOpcode (ig, t);
-			else {
+			if (rank == 1) {
+				bool is_stobj;
+				OpCode op = GetStoreOpcode (t, out is_stobj);
+				//
+				// The stobj opcode used by value types will need
+				// an address on the stack, not really an array/array
+				// pair
+				//
+				if (is_stobj)
+					ig.Emit (OpCodes.Ldelema, t);
+				
+				source.Emit (ec);
+				if (leave_copy) {
+					ec.ig.Emit (OpCodes.Dup);
+					temp = new LocalTemporary (ec, this.type);
+					temp.Store (ec);
+				}
+				
+				if (is_stobj)
+					ig.Emit (OpCodes.Stobj, t);
+				else
+					ig.Emit (op);
+			} else {
 				ModuleBuilder mb = CodeGen.Module.Builder;
 				int arg_count = ea.Arguments.Count;
 				Type [] args = new Type [arg_count + 1];
 				MethodInfo set;
 				
+				source.Emit (ec);
+				if (leave_copy) {
+					ec.ig.Emit (OpCodes.Dup);
+					temp = new LocalTemporary (ec, this.type);
+					temp.Store (ec);
+				}
+				
 				for (int i = 0; i < arg_count; i++){
 					//args [i++] = a.Type;
 					args [i] = TypeManager.int32_type;
@@ -7748,6 +7757,9 @@ namespace Mono.CSharp {
 				
 				ig.Emit (OpCodes.Call, set);
 			}
+			
+			if (temp != null)
+				temp.Emit (ec);
 		}
 
 		public void AddressOf (EmitContext ec, AddressOp mode)
@@ -8039,19 +8051,53 @@ namespace Mono.CSharp {
 			return this;
 		}
 		
-		public override void Emit (EmitContext ec)
+		bool prepared = false;
+		LocalTemporary temp;
+		
+		public void Emit (EmitContext ec, bool leave_copy)
 		{
-			Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc);
+			Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
+			if (leave_copy) {
+				ec.ig.Emit (OpCodes.Dup);
+				temp = new LocalTemporary (ec, Type);
+				temp.Store (ec);
+			}
 		}
-
+		
 		//
 		// source is ignored, because we already have a copy of it from the
 		// LValue resolution and we have already constructed a pre-cached
 		// version of the arguments (ea.set_arguments);
 		//
-		public void EmitAssign (EmitContext ec, Expression source)
+		public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+		{
+			prepared = prepare_for_load;
+			Argument a = (Argument) set_arguments [set_arguments.Count - 1];
+			
+			if (prepared) {
+				source.Emit (ec);
+				if (leave_copy) {
+					ec.ig.Emit (OpCodes.Dup);
+					temp = new LocalTemporary (ec, Type);
+					temp.Store (ec);
+				}
+			} else if (leave_copy) {
+				temp = new LocalTemporary (ec, Type);
+				source.Emit (ec);
+				temp.Store (ec);
+				a.Expr = temp;
+			}
+			
+			Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
+			
+			if (temp != null)
+				temp.Emit (ec);
+		}
+		
+		
+		public override void Emit (EmitContext ec)
 		{
-			Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc);
+			Emit (ec, false);
 		}
 	}
 

+ 3 - 3
mcs/mcs/statement.cs

@@ -4029,7 +4029,7 @@ namespace Mono.CSharp {
 				conv.Emit (ec);
 				ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
 			} else 
-				((IAssignMethod)variable).EmitAssign (ec, conv);
+				((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
 				
 			statement.Emit (ec);
 			ig.Emit (OpCodes.Br, ec.LoopBegin);
@@ -4116,7 +4116,7 @@ namespace Mono.CSharp {
 					conv.Emit (ec);
 					ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
 				} else 
-					((IAssignMethod)variable).EmitAssign (ec, conv);
+					((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
 
 				statement.Emit (ec);
 
@@ -4196,7 +4196,7 @@ namespace Mono.CSharp {
 					conv.Emit (ec);
 					ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
 				} else 
-					((IAssignMethod)variable).EmitAssign (ec, conv);
+					((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
 				statement.Emit (ec);
 				ig.MarkLabel (ec.LoopBegin);
 				for (dim = rank - 1; dim >= 0; dim--){