//
// statement.cs: Statement representation for the IL tree.
//
-// Author:
+// Authors:
// Miguel de Icaza (miguel@ximian.com)
// Martin Baulig (martin@ximian.com)
-// Marek Safar (marek.safar@seznam.cz)
+// Marek Safar (marek.safar@gmail.com)
//
// Copyright 2001, 2002, 2003 Ximian, Inc.
// Copyright 2003, 2004 Novell, Inc.
HasCapturedThis = 1 << 7,
IsExpressionTree = 1 << 8,
CompilerGenerated = 1 << 9,
- IsAsync = 1 << 10
+ IsAsync = 1 << 10,
+ Resolved = 1 << 11
}
public Block Parent;
public override bool Resolve (BlockContext ec)
{
+ if ((flags & Flags.Resolved) != 0)
+ return true;
+
Block prev_block = ec.CurrentBlock;
bool ok = true;
if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
ok = false;
+ flags |= Flags.Resolved;
return ok;
}
}
}
+ sealed class LabelMarker : Statement
+ {
+ readonly Switch s;
+ readonly List<SwitchLabel> labels;
+
+ public LabelMarker (Switch s, List<SwitchLabel> labels)
+ {
+ this.s = s;
+ this.labels = labels;
+ }
+
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ foreach (var l in labels) {
+ if (l.IsDefault)
+ ec.MarkLabel (s.DefaultLabel);
+ else
+ ec.MarkLabel (l.GetILLabel (ec));
+ }
+ }
+ }
+
public List<SwitchSection> Sections;
public Expression Expr;
SwitchSection default_section;
SwitchLabel null_section;
+ Statement simple_stmt;
+ VariableReference value;
ExpressionStatement string_dictionary;
FieldExpr switch_cache_field;
static int unique_counter;
if (constant_section == null)
constant_section = default_section;
+ } else {
+ //
+ // Store switch expression for comparission purposes
+ //
+ value = new_expr as VariableReference;
+ if (value == null)
+ value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
}
bool first = true;
}
if (default_section == null)
- ec.CurrentBranching.CreateSibling (
- null, FlowBranching.SiblingType.SwitchSection);
+ ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
ec.EndFlowBranching ();
ec.Switch = old_switch;
if (!ok)
return false;
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
- // TODO: Optimize single case, and single+default case
- ResolveStringSwitchMap (ec);
+ if (!is_constant) {
+ if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
+ if (string_labels.Count < 7)
+ ResolveSimpleSwitch (ec);
+ else
+ ResolveStringSwitchMap (ec);
+ } else if (labels.Count < 3 && !IsNullable) {
+ ResolveSimpleSwitch (ec);
+ }
}
return true;
return sl;
}
+ //
+ // Prepares switch using simple if/else comparison for small label count (4 + optional default)
+ //
+ void ResolveSimpleSwitch (BlockContext bc)
+ {
+ simple_stmt = default_section != null ? default_section.Block : null;
+
+ for (int i = Sections.Count - 1; i >= 0; --i) {
+ var s = Sections[i];
+
+ if (s == default_section) {
+ s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
+ continue;
+ }
+
+ s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
+
+ Expression cond = null;
+ for (int ci = 0; ci < s.Labels.Count; ++ci) {
+ var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
+
+ if (ci > 0) {
+ cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
+ } else {
+ cond = e;
+ }
+ }
+
+ simple_stmt = new If (cond, s.Block, simple_stmt, loc);
+ }
+
+ // It's null for empty switch
+ if (simple_stmt != null)
+ simple_stmt.Resolve (bc);
+ }
+
+ //
+ // Converts string switch into string hashtable
+ //
void ResolveStringSwitchMap (ResolveContext ec)
{
FullNamedExpression string_dictionary_type;
string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
}
- void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
+ void DoEmitStringSwitch (EmitContext ec)
{
Label l_initialized = ec.DefineLabel ();
EmitTableSwitch (ec, string_switch_variable);
string_switch_variable.Release (ec);
}
-
+
protected override void DoEmit (EmitContext ec)
{
//
default_target = ec.DefineLabel ();
null_target = ec.DefineLabel ();
- // Store variable for comparission purposes
- // TODO: Don't duplicate non-captured VariableReference
- LocalTemporary value;
if (IsNullable) {
- value = new LocalTemporary (SwitchType);
unwrap.EmitCheck (ec);
ec.Emit (OpCodes.Brfalse, null_target);
- new_expr.Emit (ec);
- value.Store (ec);
- } else if (!is_constant) {
- value = new LocalTemporary (SwitchType);
- new_expr.Emit (ec);
- value.Store (ec);
- } else
- value = null;
+ value.EmitAssign (ec, new_expr, false, false);
+ } else if (new_expr != value && !is_constant) {
+ value.EmitAssign (ec, new_expr, false, false);
+ }
//
// Setup the codegen context
if (constant_section != null)
constant_section.Block.Emit (ec);
} else if (string_dictionary != null) {
- DoEmitStringSwitch (value, ec);
+ DoEmitStringSwitch (ec);
+ } else if (simple_stmt != null) {
+ simple_stmt.Emit (ec);
} else {
EmitTableSwitch (ec, value);
}
- if (value != null)
- value.Release (ec);
-
// Restore context state.
ec.MarkLabel (ec.LoopEnd);