public override void CGenStmt(Env env, CGenState state) {
// Inside a switch statement, the initializations are ignored,
// but the stack size should be changed.
List<Tuple<Env, Decln>> declns;
List<Tuple<Env, Stmt>> stmts;
var compoundStmt = this.Stmt as CompoundStmt;
if (compoundStmt == null) {
throw new NotImplementedException();
}
declns = compoundStmt.Declns;
stmts = compoundStmt.Stmts;
// Track all case values.
IReadOnlyList<Int32> values = CaseLabelsGrabber.GrabLabels(this);
// Make sure there are no duplicates.
if (values.Distinct().Count() != values.Count) {
throw new InvalidOperationException("case labels not unique.");
}
// Request labels for these values.
Dictionary<Int32, Int32> value_to_label = values.ToDictionary(value => value, value => state.RequestLabel());
Int32 label_finish = state.RequestLabel();
Int32 num_default_stmts = stmts.Count(_ => _.Item2 is DefaultStmt);
if (num_default_stmts > 1) {
throw new InvalidOperationException("duplicate defaults.");
}
Int32 label_default =
num_default_stmts == 1 ?
state.RequestLabel() :
label_finish;
Int32 saved_stack_size = state.StackSize;
Int32 stack_size =
declns.Any() ?
declns.Last().Item1.StackSize :
saved_stack_size;
// 1. Evaluate Expr.
CGenExprStmt(env, this.Expr, state);
// 2. Expand stack.
state.CGenForceStackSizeTo(stack_size);
// 3. Make the Jump list.
foreach (KeyValuePair<Int32, Int32> value_label_pair in value_to_label) {
state.CMPL(value_label_pair.Key, Reg.EAX);
state.JZ(value_label_pair.Value);
}
state.JMP(label_default);
// 4. List all the statements.
state.InSwitch(label_finish, label_default, value_to_label);
foreach (Tuple<Env, Stmt> env_stmt_pair in stmts) {
env_stmt_pair.Item2.CGenStmt(env_stmt_pair.Item1, state);
}
state.OutLabels();
// 5. finish:
state.CGenLabel(label_finish);
// 6. Restore stack size.
state.CGenForceStackSizeTo(saved_stack_size);
}
}