Merge pull request #4155 from BrzVlad/fix-tls-lmf-addr
[mono-project.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.ControlFlow.cs
blob2e8cede706452f77c056368053f4063415bfec64
1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
16 using System.Diagnostics;
17 using System.Dynamic.Utils;
19 #if !FEATURE_CORE_DLR
20 namespace Microsoft.Scripting.Ast.Compiler {
21 #else
22 namespace System.Linq.Expressions.Compiler {
23 #endif
25 // The part of the LambdaCompiler dealing with low level control flow
26 // break, contiue, return, exceptions, etc
27 partial class LambdaCompiler {
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));
34 return result;
37 private LabelInfo ReferenceLabel(LabelTarget node) {
38 LabelInfo result = EnsureLabel(node);
39 result.Reference(_labelBlock);
40 return result;
43 private LabelInfo DefineLabel(LabelTarget node) {
44 if (node == null) {
45 return new LabelInfo(_ilg, null, false);
47 LabelInfo result = EnsureLabel(node);
48 result.Define(_labelBlock);
49 return result;
52 private void PushLabelBlock(LabelScopeKind type) {
53 _labelBlock = new LabelScopeInfo(_labelBlock, type);
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;
62 private void EmitLabelExpression(Expression expr, CompilationFlags flags) {
63 var node = (LabelExpression)expr;
64 Debug.Assert(node.Target != null);
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;
71 if (_labelBlock.Kind == LabelScopeKind.Block) {
72 _labelBlock.TryGetLabelInfo(node.Target, out label);
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);
79 // if we're in a switch or block, we should've found the label
80 Debug.Assert(label != null);
83 if (label == null) {
84 label = DefineLabel(node.Target);
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);
96 label.Mark();
99 private void EmitGotoExpression(Expression expr, CompilationFlags flags) {
100 var node = (GotoExpression)expr;
101 var labelInfo = ReferenceLabel(node.Target);
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);
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);
122 labelInfo.EmitJump();
124 EmitUnreachable(node, flags);
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);
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"
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;
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;
160 if (_labelBlock.Parent.Kind == LabelScopeKind.Switch &&
161 _labelBlock.Parent.ContainsTarget(label)) {
162 return false;
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;
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);
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);
189 DefineBlockLabels(@switch.DefaultBody);
190 return true;
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;
198 PushLabelBlock(LabelScopeKind.Statement);
199 return true;
201 case ExpressionType.Conditional:
202 case ExpressionType.Loop:
203 case ExpressionType.Goto:
204 PushLabelBlock(LabelScopeKind.Statement);
205 return true;
209 private void DefineBlockLabels(Expression node) {
210 var block = node as BlockExpression;
211 if (block == null || block is SpilledExpressionBlock) {
212 return;
214 for (int i = 0, n = block.ExpressionCount; i < n; i++) {
215 Expression e = block.GetExpression(i);
217 var label = e as LabelExpression;
218 if (label != null) {
219 DefineLabel(label.Target);
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;
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;
252 continue;