Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.ControlFlow.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System.Diagnostics;
17 using System.Dynamic.Utils;
18
19 #if !FEATURE_CORE_DLR
20 namespace Microsoft.Scripting.Ast.Compiler {
21 #else
22 namespace System.Linq.Expressions.Compiler {
23 #endif
24
25     // The part of the LambdaCompiler dealing with low level control flow
26     // break, contiue, return, exceptions, etc
27     partial class LambdaCompiler {
28
29         private LabelInfo EnsureLabel(LabelTarget node) {
30             LabelInfo result;
31             if (!_labelInfo.TryGetValue(node, out result)) {
32                 _labelInfo.Add(node, result = new LabelInfo(_ilg, node, false));
33             }
34             return result;
35         }
36
37         private LabelInfo ReferenceLabel(LabelTarget node) {
38             LabelInfo result = EnsureLabel(node);
39             result.Reference(_labelBlock);
40             return result;
41         }
42
43         private LabelInfo DefineLabel(LabelTarget node) {
44             if (node == null) {
45                 return new LabelInfo(_ilg, null, false);
46             }
47             LabelInfo result = EnsureLabel(node);
48             result.Define(_labelBlock);
49             return result;
50         }
51
52         private void PushLabelBlock(LabelScopeKind type) {
53             _labelBlock = new LabelScopeInfo(_labelBlock, type);
54         }
55
56         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")]
57         private void PopLabelBlock(LabelScopeKind kind) {
58             Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind);
59             _labelBlock = _labelBlock.Parent;
60         }
61
62         private void EmitLabelExpression(Expression expr, CompilationFlags flags) {
63             var node = (LabelExpression)expr;
64             Debug.Assert(node.Target != null);
65
66             // If we're an immediate child of a block, our label will already
67             // be defined. If not, we need to define our own block so this
68             // label isn't exposed except to its own child expression.
69             LabelInfo label = null;
70
71             if (_labelBlock.Kind == LabelScopeKind.Block) {
72                 _labelBlock.TryGetLabelInfo(node.Target, out label);
73
74                 // We're in a block but didn't find our label, try switch
75                 if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch) {
76                     _labelBlock.Parent.TryGetLabelInfo(node.Target, out label);
77                 }
78
79                 // if we're in a switch or block, we should've found the label
80                 Debug.Assert(label != null);
81             }
82
83             if (label == null) {
84                 label = DefineLabel(node.Target);
85             }
86
87             if (node.DefaultValue != null) {
88                 if (node.Target.Type == typeof(void)) {
89                     EmitExpressionAsVoid(node.DefaultValue, flags);
90                 } else {
91                     flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
92                     EmitExpression(node.DefaultValue, flags);
93                 }
94             }
95
96             label.Mark();
97         }
98
99         private void EmitGotoExpression(Expression expr, CompilationFlags flags) {
100             var node = (GotoExpression)expr;
101             var labelInfo = ReferenceLabel(node.Target);
102
103             var tailCall = flags & CompilationFlags.EmitAsTailCallMask;
104             if (tailCall != CompilationFlags.EmitAsNoTail) {
105                 // Since tail call flags are not passed into EmitTryExpression, CanReturn 
106                 // means the goto will be emitted as Ret. Therefore we can emit the goto's
107                 // default value with tail call. This can be improved by detecting if the
108                 // target label is equivalent to the return label.
109                 tailCall = labelInfo.CanReturn ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
110                 flags = UpdateEmitAsTailCallFlag(flags, tailCall);
111             }
112
113             if (node.Value != null) {
114                 if (node.Target.Type == typeof(void)) {
115                     EmitExpressionAsVoid(node.Value, flags);
116                 } else {
117                     flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
118                     EmitExpression(node.Value, flags);
119                 }
120             }
121
122             labelInfo.EmitJump();
123
124             EmitUnreachable(node, flags);
125         }
126
127         // We need to push default(T), unless we're emitting ourselves as
128         // void. Even though the code is unreachable, we still have to
129         // generate correct IL. We can get rid of this once we have better
130         // reachability analysis.
131         private void EmitUnreachable(Expression node, CompilationFlags flags) {
132             if (node.Type != typeof(void) && (flags & CompilationFlags.EmitAsVoidType) == 0) {
133                 _ilg.EmitDefault(node.Type);
134             }
135         }
136
137         private bool TryPushLabelBlock(Expression node) {
138             // Anything that is "statement-like" -- e.g. has no associated
139             // stack state can be jumped into, with the exception of try-blocks
140             // We indicate this by a "Block"
141             // 
142             // Otherwise, we push an "Expression" to indicate that it can't be
143             // jumped into
144             switch (node.NodeType) {
145                 default:
146                     if (_labelBlock.Kind != LabelScopeKind.Expression) {
147                         PushLabelBlock(LabelScopeKind.Expression);
148                         return true;
149                     }
150                     return false;
151                 case ExpressionType.Label:
152                     // LabelExpression is a bit special, if it's directly in a
153                     // block it becomes associate with the block's scope. Same
154                     // thing if it's in a switch case body.
155                     if (_labelBlock.Kind == LabelScopeKind.Block) {
156                         var label = ((LabelExpression)node).Target;
157                         if (_labelBlock.ContainsTarget(label)) {
158                             return false;
159                         }
160                         if (_labelBlock.Parent.Kind == LabelScopeKind.Switch &&
161                             _labelBlock.Parent.ContainsTarget(label)) {
162                             return false;
163                         }
164                     }
165                     PushLabelBlock(LabelScopeKind.Statement);
166                     return true;
167                 case ExpressionType.Block:
168                     if (node is SpilledExpressionBlock) {
169                         // treat it as an expression
170                         goto default;
171                     }
172
173                     PushLabelBlock(LabelScopeKind.Block);
174                     // Labels defined immediately in the block are valid for
175                     // the whole block.
176                     if (_labelBlock.Parent.Kind != LabelScopeKind.Switch) {
177                         DefineBlockLabels(node);
178                     }
179                     return true;
180                 case ExpressionType.Switch:
181                     PushLabelBlock(LabelScopeKind.Switch);
182                     // Define labels inside of the switch cases so theyare in
183                     // scope for the whole switch. This allows "goto case" and
184                     // "goto default" to be considered as local jumps.
185                     var @switch = (SwitchExpression)node;
186                     foreach (SwitchCase c in @switch.Cases) {
187                         DefineBlockLabels(c.Body);
188                     }
189                     DefineBlockLabels(@switch.DefaultBody);
190                     return true;
191
192                 // Remove this when Convert(Void) goes away.
193                 case ExpressionType.Convert:
194                     if (node.Type != typeof(void)) {
195                         // treat it as an expression
196                         goto default;
197                     }
198                     PushLabelBlock(LabelScopeKind.Statement);
199                     return true;
200
201                 case ExpressionType.Conditional:
202                 case ExpressionType.Loop:
203                 case ExpressionType.Goto:
204                     PushLabelBlock(LabelScopeKind.Statement);
205                     return true;
206             }
207         }
208
209         private void DefineBlockLabels(Expression node) {
210             var block = node as BlockExpression;
211             if (block == null || block is SpilledExpressionBlock) {
212                 return;
213             }
214             for (int i = 0, n = block.ExpressionCount; i < n; i++) {
215                 Expression e = block.GetExpression(i);
216
217                 var label = e as LabelExpression;
218                 if (label != null) {
219                     DefineLabel(label.Target);
220                 }
221             }
222         }
223
224         // See if this lambda has a return label
225         // If so, we'll create it now and mark it as allowing the "ret" opcode
226         // This allows us to generate better IL
227         private void AddReturnLabel(LambdaExpression lambda) {
228             var expression = lambda.Body;
229
230             while (true) {
231                 switch (expression.NodeType) {
232                     default:
233                         // Didn't find return label
234                         return;
235                     case ExpressionType.Label:
236                         // Found the label. We can directly return from this place
237                         // only if the label type is reference assignable to the lambda return type.
238                         var label = ((LabelExpression)expression).Target;
239                         _labelInfo.Add(label, new LabelInfo(_ilg, label, TypeUtils.AreReferenceAssignable(lambda.ReturnType, label.Type)));
240                         return;
241                     case ExpressionType.Block:
242                         // Look in the last significant expression of a block
243                         var body = (BlockExpression)expression;
244                         // omit empty and debuginfo at the end of the block since they
245                         // are not going to emit any IL
246                         for (int i = body.ExpressionCount - 1; i >= 0; i--) {
247                             expression = body.GetExpression(i);
248                             if (Significant(expression)) {
249                                 break;
250                             }
251                         }
252                         continue;
253                 }
254             }
255         }
256     }
257 }