提交 92543440 编写于 作者: N Neal Gafter

Reorganize lowering for switch on float/double/decimal as recommended in code review.

上级 393f4521
......@@ -360,17 +360,29 @@ private void LowerBasicSwitch(DecisionTree.ByValue byValue)
}
var rewrittenSections = switchSections.ToImmutableAndFree();
MethodSymbol stringEquality = null;
switch (byValue.Type.SpecialType)
if (underlyingSwitchType.SpecialType == SpecialType.System_String)
{
case SpecialType.System_String:
{
LocalRewriter.EnsureStringHashFunction(rewrittenSections, _factory.Syntax);
stringEquality = LocalRewriter.GetSpecialTypeMethod(_factory.Syntax, SpecialMember.System_String__op_Equality);
goto case SpecialType.System_UInt32;
}
LocalRewriter.EnsureStringHashFunction(rewrittenSections, _factory.Syntax);
stringEquality = LocalRewriter.GetSpecialTypeMethod(_factory.Syntax, SpecialMember.System_String__op_Equality);
}
// The BoundSwitchStatement requires a constant target when there are no sections, so we accomodate that here.
var constantTarget = rewrittenSections.IsEmpty ? noValueMatches : null;
var switchStatement = new BoundSwitchStatement(
_factory.Syntax, null, _factory.Convert(underlyingSwitchType, byValue.Expression),
constantTarget,
ImmutableArray<LocalSymbol>.Empty, ImmutableArray<LocalFunctionSymbol>.Empty,
rewrittenSections, noValueMatches, stringEquality);
// The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly.
switch (underlyingSwitchType.SpecialType)
{
case SpecialType.System_Boolean:
// boolean switch is handled in LowerBooleanSwitch, not here.
throw ExceptionUtilities.Unreachable;
case SpecialType.System_String:
case SpecialType.System_Byte:
case SpecialType.System_Char:
case SpecialType.System_Int16:
......@@ -381,76 +393,16 @@ private void LowerBasicSwitch(DecisionTree.ByValue byValue)
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
{
// Emit requires a constant target when there are no sections, so we accomodate that here.
// CONSIDER: can we get better code generated by giving a constant target more often here,
// e.g. when the switch expression is a constant?
var constantTarget = rewrittenSections.IsEmpty ? noValueMatches : null;
var switchStatement = new BoundSwitchStatement(
_factory.Syntax, null, _factory.Convert(underlyingSwitchType, byValue.Expression),
constantTarget,
ImmutableArray<LocalSymbol>.Empty, ImmutableArray<LocalFunctionSymbol>.Empty,
rewrittenSections, noValueMatches, stringEquality);
// emit knows how to efficiently generate code for these kinds of switches.
_loweredDecisionTree.Add(switchStatement);
// The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly.
break;
}
default:
{
if (byValue.Type.TypeKind == TypeKind.Enum)
{
goto case SpecialType.System_Int32;
}
// Here we handle "other" types, such as float, double, and decimal.
// We compare the constant values using value.Equals(input), using ordinary
// overload resolution. Note that we cannot and do not rely on switching
// on the hash code, as it may not be consistent with the behavior of Equals;
// see https://github.com/dotnet/coreclr/issues/6237. Also, the hash code is
// not guaranteed to be the same on the compilation platform as the runtime
// platform.
// CONSIDER: can we improve the quality of the code using comparisons, like
// we do for other numeric types, by generating a series of tests
// that use divide-and-conquer to efficiently find a matching value?
// If so, we should use the BoundSwitchStatement and do that in emit.
LabelSymbol nextLabel = null;
foreach (var section in rewrittenSections)
{
foreach (var boundSwitchLabel in section.SwitchLabels)
{
if (nextLabel != null)
{
_loweredDecisionTree.Add(_factory.Label(nextLabel));
}
nextLabel = _factory.GenerateLabel("failcase+" + section.SwitchLabels[0].ConstantValueOpt.Value);
Debug.Assert(boundSwitchLabel.ConstantValueOpt != null);
// generate (if (value.Equals(input)) goto label;
var literal = LocalRewriter.MakeLiteral(_factory.Syntax, boundSwitchLabel.ConstantValueOpt, byValue.Type);
var condition = _factory.InstanceCall(literal, "Equals", byValue.Expression);
if (!condition.HasErrors && condition.Type.SpecialType != SpecialType.System_Boolean)
{
var call = (BoundCall)condition;
// '{1} {0}' has the wrong return type
_factory.Diagnostics.Add(ErrorCode.ERR_BadRetType, boundSwitchLabel.Syntax.GetLocation(), call.Method, call.Type);
}
_loweredDecisionTree.Add(_factory.ConditionalGoto(condition, boundSwitchLabel.Label, true));
_loweredDecisionTree.Add(_factory.Goto(nextLabel));
}
foreach (var boundSwitchLabel in section.SwitchLabels)
{
_loweredDecisionTree.Add(_factory.Label(boundSwitchLabel.Label));
}
_loweredDecisionTree.Add(_factory.Block(section.Statements));
// this location should not be reachable.
}
Debug.Assert(nextLabel != null);
_loweredDecisionTree.Add(_factory.Label(nextLabel));
_loweredDecisionTree.Add(_factory.Label(noValueMatches));
// other types, such as float, double, and decimal, are not currently
// handled by emit and must be lowered here.
_loweredDecisionTree.Add(LowerNonprimitiveSwitch(switchStatement));
break;
}
}
......@@ -458,6 +410,71 @@ private void LowerBasicSwitch(DecisionTree.ByValue byValue)
LowerDecisionTree(byValue.Expression, byValue.Default);
}
private BoundStatement LowerNonprimitiveSwitch(BoundSwitchStatement switchStatement)
{
// Here we handle "other" types, such as float, double, and decimal, by
// lowering the BoundSwitchStatement.
// We compare the constant values using value.Equals(input), using ordinary
// overload resolution. Note that we cannot and do not rely on switching
// on the hash code, as it may not be consistent with the behavior of Equals;
// see https://github.com/dotnet/coreclr/issues/6237. Also, the hash code is
// not guaranteed to be the same on the compilation platform as the runtime
// platform.
// CONSIDER: can we improve the quality of the code using comparisons, like
// we do for other numeric types, by generating a series of tests
// that use divide-and-conquer to efficiently find a matching value?
// If so, we should use the BoundSwitchStatement and do that in emit.
// Moreover, we should be able to use `==` rather than `.Equals`
// for cases (such as non-NaN) where we know the result to be the same.
var rewrittenSections = switchStatement.SwitchSections;
var expression = switchStatement.Expression;
var noValueMatches = switchStatement.BreakLabel;
Debug.Assert(switchStatement.LoweredPreambleOpt == null);
Debug.Assert(switchStatement.InnerLocals.IsDefaultOrEmpty);
Debug.Assert(switchStatement.InnerLocalFunctions.IsDefaultOrEmpty);
Debug.Assert(switchStatement.StringEquality == null);
LabelSymbol nextLabel = null;
var builder = ArrayBuilder<BoundStatement>.GetInstance();
foreach (var section in rewrittenSections)
{
foreach (var boundSwitchLabel in section.SwitchLabels)
{
if (nextLabel != null)
{
builder.Add(_factory.Label(nextLabel));
}
nextLabel = _factory.GenerateLabel("failcase+" + section.SwitchLabels[0].ConstantValueOpt.Value);
Debug.Assert(boundSwitchLabel.ConstantValueOpt != null);
// generate (if (value.Equals(input)) goto label;
var literal = LocalRewriter.MakeLiteral(_factory.Syntax, boundSwitchLabel.ConstantValueOpt, expression.Type);
var condition = _factory.InstanceCall(literal, "Equals", expression);
if (!condition.HasErrors && condition.Type.SpecialType != SpecialType.System_Boolean)
{
var call = (BoundCall)condition;
// '{1} {0}' has the wrong return type
_factory.Diagnostics.Add(ErrorCode.ERR_BadRetType, boundSwitchLabel.Syntax.GetLocation(), call.Method, call.Type);
}
builder.Add(_factory.ConditionalGoto(condition, boundSwitchLabel.Label, true));
builder.Add(_factory.Goto(nextLabel));
}
foreach (var boundSwitchLabel in section.SwitchLabels)
{
builder.Add(_factory.Label(boundSwitchLabel.Label));
}
builder.Add(_factory.Block(section.Statements));
// this location should not be reachable.
}
Debug.Assert(nextLabel != null);
builder.Add(_factory.Label(nextLabel));
builder.Add(_factory.Label(noValueMatches));
return _factory.Block(builder.ToImmutableAndFree());
}
private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
{
switch (byValue.ValueAndDecision.Count)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册