Skip to content

Commit

Permalink
improves switch code generation (#233)
Browse files Browse the repository at this point in the history
- use better label variable names for end of switch / each switch section
- does not emit a jump to the end of the switch at the last case before a 'default'
- adds individual switch sections to snippet / cecilified mapping
  • Loading branch information
adrianoc committed Jul 28, 2023
1 parent 214eb89 commit 628214e
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 22 deletions.
22 changes: 11 additions & 11 deletions Cecilifier.Core.Tests/Tests/Unit/Miscellaneous.Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,35 +113,35 @@ void M()
\s+//case 1: \(condition\)
\s+il_M_7.Emit\(OpCodes.Ldloc, l_switchCondition_13\);
\s+il_M_7.Emit\(OpCodes.Ldc_I4, 1\);
\s+il_M_7.Emit\(OpCodes.Beq_S, nop_15\);
\s+il_M_7.Emit\(OpCodes.Beq_S, lbl_caseCode_0_15\);
\s+//case 2: \(condition\)
\s+il_M_7.Emit\(OpCodes.Ldloc, l_switchCondition_13\);
\s+il_M_7.Emit\(OpCodes.Ldc_I4, 2\);
\s+il_M_7.Emit\(OpCodes.Beq_S, nop_16\);
\s+il_M_7.Emit\(OpCodes.Beq_S, lbl_caseCode_1_16\);
\s+il_M_7.Emit\(OpCodes.Br, nop_17\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_caseCode_2_17\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);
\s+//case 1: \(code\)
\s+il_M_7.Append\(nop_15\);
\s+il_M_7.Append\(lbl_caseCode_0_15\);
\s+il_M_7.Emit\(OpCodes.Ldstr, "C1"\);
\s+il_M_7.Emit\(OpCodes.Call, .+System.Console.+WriteLine.+\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);
\s+//case 2: \(code\)
\s+il_M_7.Append\(nop_16\);
\s+il_M_7.Append\(lbl_caseCode_1_16\);
\s+il_M_7.Emit\(OpCodes.Ldstr, "C2"\);
\s+il_M_7.Emit\(OpCodes.Call, .+System.Console.+WriteLine.+\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);
\s+//default: \(code\)
\s+il_M_7.Append\(nop_17\);
\s+il_M_7.Append\(lbl_caseCode_2_17\);
\s+il_M_7.Emit\(OpCodes.Ldstr, "CD"\);
\s+il_M_7.Emit\(OpCodes.Call, .+System.Console.+WriteLine.+\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);
\s+//End of switch
\s+il_M_7.Append\(nop_14\);
\s+il_M_7.Append\(lbl_endOfSwitch_14\);
"""));
}

Expand Down
31 changes: 21 additions & 10 deletions Cecilifier.Core/AST/StatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,47 +79,58 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node)
Context.EmitCilInstruction(_ilVar, OpCodes.Stloc, evaluatedExpressionVariable); // stores evaluated expression in local var

// Add label to end of switch
var endOfSwitchLabel = CreateCilInstruction(_ilVar, OpCodes.Nop);
var endOfSwitchLabel = CreateCilInstruction(_ilVar, Context.Naming.Label("endOfSwitch"), OpCodes.Nop);
breakToInstructionVars.Push(endOfSwitchLabel);

// Write the switch conditions.
var nextTestLabels = node.Sections.Select(_ => CreateCilInstruction(_ilVar, OpCodes.Nop)).ToArray();
var nextTestLabels = node.Sections.Select( (_, index) =>
{
var labelName = Context.Naming.Label($"caseCode_{index}");
CreateCilInstruction(_ilVar, labelName, OpCodes.Nop);
return labelName;
}).ToArray();

var hasDefault = false;
var currentLabelIndex = 0;
foreach (var switchSection in node.Sections)
{
if (switchSection.Labels.First().Kind() == SyntaxKind.DefaultSwitchLabel)
{
Context.EmitCilInstruction(_ilVar, OpCodes.Br, nextTestLabels[currentLabelIndex]);
hasDefault = true;
continue;
}

foreach (var sectionLabel in switchSection.Labels)
{
using var _ = LineInformationTracker.Track(Context, sectionLabel);

Context.WriteNewLine();
Context.WriteComment($"{sectionLabel.ToString()} (condition)");
Context.EmitCilInstruction(_ilVar, OpCodes.Ldloc, evaluatedExpressionVariable);
ExpressionVisitor.Visit(Context, _ilVar, sectionLabel);
string operand = nextTestLabels[currentLabelIndex];
Context.EmitCilInstruction(_ilVar, OpCodes.Beq_S, operand);
Context.WriteNewLine();
Context.EmitCilInstruction(_ilVar, OpCodes.Beq_S, nextTestLabels[currentLabelIndex]);
}
currentLabelIndex++;
}

// if at runtime the code hits this point it means none of the labels matched.
// so, just jump to the end of the switch.
Context.EmitCilInstruction(_ilVar, OpCodes.Br, endOfSwitchLabel);

// if at runtime the code hits this point and the switch does not have a default section
// it means none of the labels matched so just jump to the end of the switch.
if (!hasDefault)
Context.EmitCilInstruction(_ilVar, OpCodes.Br, endOfSwitchLabel);

// Write the statements for each switch section...
currentLabelIndex = 0;
foreach (var switchSection in node.Sections)
{
using var _ = LineInformationTracker.Track(Context, switchSection);
Context.WriteNewLine();
Context.WriteComment($"{switchSection.Labels.First().ToString()} (code)");
AddCecilExpression($"{_ilVar}.Append({nextTestLabels[currentLabelIndex]});");
foreach (var statement in switchSection.Statements)
{
statement.Accept(this);
}
Context.WriteNewLine();
currentLabelIndex++;
}

Expand Down
3 changes: 2 additions & 1 deletion Cecilifier.Core/AST/SyntaxWalkerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ protected string CreateCilInstruction(string ilVar, OpCode opCode, object operan
return instVar;
}

protected void CreateCilInstruction(string ilVar, string instVar, OpCode opCode, object operand = null)
protected string CreateCilInstruction(string ilVar, string instVar, OpCode opCode, object operand = null)
{
var operandStr = operand == null ? string.Empty : $", {operand}";
AddCecilExpression($"var {instVar} = {ilVar}.Create({opCode.ConstantName()}{operandStr});");
return instVar;
}

protected void LoadLiteralValue(string ilVar, ITypeSymbol type, string value, bool isTargetOfCall, SyntaxNode parent)
Expand Down

0 comments on commit 628214e

Please sign in to comment.