home comics writing pictures archive about

2017-01-07 - In IL: Grade Analyser (switch)

In IL

Last time we looked at the IL switch instruction. Today we are going to look at the switch statement in C# and see how it uses the IL switch instruction.

We are going to use a program that tests a student's grade and prints out a message.

Program.cs
using System;
namespace csSwitch
{
class Program
{
static void Main(string[] args)
{
char grade = 'B';
bool needToImprove = false;
switch (grade)
{
case 'A':
Console.WriteLine("Your grade is excellent");
needToImprove = false;
break;
case 'B':
Console.WriteLine("Your grade is good");
needToImprove = false;
break;
case 'C':
Console.WriteLine("Your grade is OK");
needToImprove = true;
break;
case 'D':
Console.WriteLine("Your grade is acceptable");
needToImprove = true;
break;
default:
Console.WriteLine("You failed");
needToImprove = true;
break;
}
if (needToImprove)
{
Console.WriteLine("You can do better");
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

Now if we compile the above program we get.

Main
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 114 (0x72)
.maxstack 2
.locals init ([0] char grade,
[1] bool needToImprove)
IL_0000: ldc.i4.s 66
IL_0002: stloc.0
IL_0003: ldc.i4.0
IL_0004: stloc.1
IL_0005: ldloc.0
IL_0006: ldc.i4.s 65
IL_0008: sub
IL_0009: switch (
IL_0020,
IL_002e,
IL_003c,
IL_004a)
IL_001e: br.s IL_0058
IL_0020: ldstr "Your grade is excellent"
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: ldc.i4.0
IL_002b: stloc.1
IL_002c: br.s IL_0064
IL_002e: ldstr "Your grade is good"
IL_0033: call void [mscorlib]System.Console::WriteLine(string)
IL_0038: ldc.i4.0
IL_0039: stloc.1
IL_003a: br.s IL_0064
IL_003c: ldstr "Your grade is OK"
IL_0041: call void [mscorlib]System.Console::WriteLine(string)
IL_0046: ldc.i4.1
IL_0047: stloc.1
IL_0048: br.s IL_0064
IL_004a: ldstr "Your grade is acceptable"
IL_004f: call void [mscorlib]System.Console::WriteLine(string)
IL_0054: ldc.i4.1
IL_0055: stloc.1
IL_0056: br.s IL_0064
IL_0058: ldstr "You failed"
IL_005d: call void [mscorlib]System.Console::WriteLine(string)
IL_0062: ldc.i4.1
IL_0063: stloc.1
IL_0064: ldloc.1
IL_0065: brfalse.s IL_0071
IL_0067: ldstr "You can do better"
IL_006c: call void [mscorlib]System.Console::WriteLine(string)
IL_0071: ret
} // end of method Program::Main
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

Looking at the IL we can clearly see the code writing text to the console, setting the needToImprove variable, and then doing some branching for each of the cases. This is all stuff we've seen before but what's going on before that?

The program starts by loading the value 66 into the grade variable. 66 is the Unicode value for the capital B letter. We then set the needToImprove variable to 0 which represents false. Then we push the grade variable onto the stack and subtract 65 from it. Why does it do that?

65 is the Unicode value for A. Since Unicode letters are in alphabetical order subtracting 65 from the grade variable converts the character value into an offset from A. So A becomes 0, B becomes 1, C becomes 2 and so on. As you will recall from last time switch instructions are 0 based so having the first option be 0 and subsequent options incrementing from there simplifies the switch instruction. Otherwise we would need to specify branching targets for 0 to 64 that all point to the default case.

So now let's look at the switch instruction. The following table shows the switch instruction branch index, the branch target, and the corresponding case statement.

Index Branch Target Case
0 ('A' - 65) IL_0020 case 'A'
1 ('B' - 65) IL_002e case 'B'
2 ('C' - 65) IL_003c case 'C'
3 ('D' - 65) IL_004a case 'D'

If you look at the instructions at each of the branch targets you can see they line up with the case statements perfectly.

There's only one thing left, the default case. Recall that if the value on the stack doesn't correspond to a target of the switch instruction then nothing happens and executions continues at the next instruction. Thus if the grade value isn't A, B, C, or D, execution will move on to the unconditional branch to IL_0058. This is our default case.

Next time we will try and bust the switch statement.

Comments: