home comics writing pictures archive about

2016-07-24 - In IL: Largest of Two Numbers (if-else)

Last time we looked at IL branching Instructions. The simplest use of branching instructions is probably the If statement which allows the program to conditionally execute a set of instructions. If the condition is true then the program executes the code within the if block. Statements like this exist in most high level languages because they allow the program to make decisions about whether or not code should be executed. If statements can also have Else blocks which are only executed if the condition is false.

Let's look at a simple program that reports which of two numbers is the largest using if, else statements in C#.

Program.cs
using System;
namespace csIf
{
class Program
{
static void Main(string[] args)
{
int numberA = 17;
int numberB = 96;
if (numberA > numberB)
{
Console.WriteLine("numberA ({0}) is larger than numberB ({1})",
numberA, numberB);
}
else
{
Console.WriteLine("numberB ({0}) is larger than numberA ({1})",
numberB, numberA);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Compile it and we get something like this.

Main
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 56 (0x38)
.maxstack 3
.locals init ([0] int32 numberA,
[1] int32 numberB)
IL_0000: ldc.i4.s 17
IL_0002: stloc.0
IL_0003: ldc.i4.s 96
IL_0005: stloc.1
IL_0006: ldloc.0
IL_0007: ldloc.1
IL_0008: ble.s IL_0021
IL_000a: ldstr "numberA ({0}) is larger than numberB ({1})"
IL_000f: ldloc.0
IL_0010: box [mscorlib]System.Int32
IL_0015: ldloc.1
IL_0016: box [mscorlib]System.Int32
IL_001b: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0020: ret
IL_0021: ldstr "numberB ({0}) is larger than numberA ({1})"
IL_0026: ldloc.1
IL_0027: box [mscorlib]System.Int32
IL_002c: ldloc.0
IL_002d: box [mscorlib]System.Int32
IL_0032: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0037: 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

The first few lines are things we've seen before. You have the stack size directive, the local variable declaration and then variable initialization. At line 69 is where things get interesting. We start by loading both local variables and then calling a branch less-than-or-equal instruction.

The target of the branch is encoded using a label. labels appear at the start of a line and ends with a colon. When the program is compiled into its binary form the branching instruction encodes the target as an integer offset from the branching instruction to the instruction marked with the given label. The IL disassembler generates a label for each instruction based on its offset from the start of the method. This is the IL_XXXX: that appears at the start of each line.

If we look past the branch instructions we will see two sets of instructions which print a formatted string to the console. This code is very similar to instructions to print the volume of a cylinder that we saw earlier except it uses Console.WriteLine to perform the formatting and not string.format. The first set of instructions prints "numberA ({0}) is larger than numberB ({1})" and the second prints "numberB ({0}) is larger than numberA ({1})". If we compare this to the original C# program we see these sets of instructions match the if and else blocks respectively.

From last time we know that ble.s will cause the program to jump if the second value it pops off the stack is less than or equal to the first value it pops off the stack. The target of the branch has the label IL_0021 which is on line 83. At this point in the program the stack and local variables would look like this.

Evaluation Stack Local Variables
Type Value Index Type Value
int32 17 0 int32 17
int32 96 1 int32 96
     

The first value popped off the stack will be 96 and the second will be 17. Since 17 is less than or equal to 96 the condition will be true and the program branches. The next instruction to be executed will be at line 83. The program will print out "numberB (96) is larger than numberA (17)" and then return.

The ble instruction checks if numberB <= numberA. If we compare this to the original program we see that this is the inverse of the if statement condition, why is that? If you think about it the if statement and branching are opposites. An If statement says "Execute this code if the condition is true" while a branch instruction says "Jump away from this code if the condition is true". So the compiler inverses the logic to better match the instructions.

If you compile this program in debug mode you will see that the un-optimized  version includes a greater-than comparison and then a branch false. This matches the original if statement better but requires more instructions. When running with optimizations on, the compiler tries to come up with the simplest set of instructions that functionally match the original program. This is also why both sets of instructions contain their own return instruction instead of a common return instruction which would have required the if set to end with a branch back to a point past the else instructions.

Next time we are going to do the same thing with three numbers.

2016-07-02 - I Dream of School

I haven't been in school for years but for some reason I still have the occasional school dream. Dreams where I'm late to class, didn't study, or got lost in the school. Things that I have absolutely no reason to worry about and yet my brain still brings them up. The weirdest part is that in the dreams I start buying into the idea of being in back in school.

I remember one dream recently where I was in class and couldn't find my textbook. I started thinking I should get a locker so I always know where my books. The idea of being back in school seemed completely normal and my brain started problem solving the issue. Then I woke up and realized I don't have to go to school. None of this is relevant to me and it doesn't matter anymore.

I want to realize I am having a school dream while I am having it so I can run out of the class screaming "Screw you guys, I don't have to be here". That would be fun.

2016-06-18 - In IL: Branching Instructions

Conditional logic is what allows a program to appear as though it's making decisions. Certain parts of code are executed only if specific conditions are met. In higher level programming languages this would be handled by if, switch, or select statements. In IL these decisions are handled by branching instructions. Instructions that change the next instruction to be executed if certain conditions are true.

Generally instructions are executed one after the other. A branching instruction causes a jump. Instead of executing the next instruction it jumps to the instruction indicated by the branching instruction. It then continues to executes instructions after that point until another branching instruction is found.

br (BRanch)

The unconditional branch instruction always jumps to the instruction indicated by its argument.

Instruction Description Binary Format
br.s<target> Short branch unconditional 0x2B <int8>
br Branch unconditional 0x38 <int32>

brfalse (BRanch FALSE), brtrue (BRanch TRUE)

Pops a single value off of the stack. The brfalse instruction jumps to the instruction indicated by its argument if the value is 0. The brtrue instruction jumps to the instruction indicated by its argument if the value is nonzero.

The brfalse instruction is aliased as brnull (BRanch NULL) and brzero (BRanch ZERO). The brtrue function is aliased as brinst (BRanch INSTance)

Instruction Description Binary Format
brfalse.s<target> Short branch if false (brnull.s, brzero.s) 0x2C <int8>
brtrue.s<target> Short branch if true (brinst.s) 0x2D <int8>
brfalse Branch if false (brnull, brzero) 0x39 <int32>
brtrue Branch if true (brinst) 0x3A <int32>

beq (Branch EQual), ceq (Compare EQual)

Pops two values off of the stack, and compares them. The branching instruction jumps to the instruction indicated by its argument if they are equal. The compare instruction pushes 1 onto the stack if they are equal and 0 otherwise.

Instruction Description Binary Format
beq.s <target> Short branch if values are equal 0x2E <int8>
beq <target> Branch if values are equal 0x3B <int32>
ceq Compares if values are equal 0xFE 01

bge (Branch Greater-than or Equal)

Pops two values off of the stack, and compares them. The branching instruction jumps to the instruction indicated by its argument if the second value popped is greater than or equal to the first.

Instruction Description Binary Format
bge.s <target> Short branch greater than or equal 0x2F <int8>
bge.un.s<target> Short branch greater than or equal unsigned 0x34 <int8>
bge Branch greater than or equal 0x3C <int32>
bge.un Branch greater than or equal unsigned 0x41 <int32>

bgt (Branch Greater-Than), cgt (Compare Greater-Than)

Pops two values off of the stack, and compares them. The branching instruction jumps to the instruction indicated by its argument if the second value popped is greater than the first. The compare instruction pushes 1 onto the stack if the second value popped is greater than the first and 0 otherwise.

Instruction Description Binary Format
bgt.s <target> Short branch greater than 0x30 <int8>
bgt.un.s<target> Short branch greater than unsigned 0x35 <int8>
bgt Branch greater than 0x3D <int32>
bgt.un Branch greater than unsigned 0x42 <int32>
cgt Compare greater than 0xFE 02
cgt.un Compare greater than unsigned 0xFE 03

ble (Branch Less-than or Equal)

Pops two values off of the stack, and compares them. The branching instruction jumps to the instruction indicated by its argument if the second value popped is less than or equal to the first.

Instruction Description Binary Format
ble.s<target> Short branch less than or equal to 0x31 <int8>
ble.un.s<target> Short branch less than or equal to unsigned 0x36 <int8>
ble Branch less than or equal to 0x3E <int32>
ble.un Branch less than or equal to unsigned 0x43 <int32>

blt (Branch Less-Than), clt (Compare Less-Than)

Pops two values off of the stack, and compares them. The branching instruction jumps to the instruction indicated by its argument if the second value popped is less than the first. The compare instruction pushes 1 onto the stack if the second value popped is less than the first and 0 otherwise.

Instruction Description Binary Format
blt.s<target> Short branch less than 0x32 <int8>
blt.un.s<target> Short branch less than unsigned 0x37 <int8>
blt Branch less than 0x3F <int32>
blt.un Branch less than unsigned 0x44 <int32>
clt Compare less than 0xFE 04
clt.un Compare less than unsigned 0xFE 05

bne (Branch Not Equal)

Pops two values off of the stack, and compares them. The branching instruction jumps to the instruction indicated by its argument if they are not equal.

Instruction Description Binary Format
bne.un.s<target> Short branch unequal unsigned 0x33 <int8>
bne.un Branch unequal unsigned unsigned 0x40 <int32>

and, or, xor, not

These instructions perform the specified bitwise action on values popped from the stack. The not instruction takes a single value while the rest take two.

Instruction Description Binary Format
and Bitwise and 0x5F
or Bitwise or 0x60
xor Bitwise exclusive-or 0x61
not Bitwise complement 0x66

Next time we will look at if statements and how they get represented in IL.

2016-05-28 - Every Programming Language is Alright

I love programming languages. I want to learn as many programming languages as I can. I find all the differences between languages to be fascinating. The way two similar languages choose to focus on different things. The different way languages solve the same problem. While reading about programming languages I sometimes come across very general comparisons. Language Y is better than Language X. Language Y sucks, use Language Z instead. These kind of statements bug me because they miss the point of why multiple languages exist.

There are very few accidental languages. No one has ever been bashing their keyboard and went "Woops, I made a compiler" or "Damn, I inadvertently wrote a language specification". These things are difficult and very complex so people only put the time and effort into making them if they actually want them to exist for some reason. Languages also only become popular if people want to use them for some reason. It's those reasons which are the key.

If you look at why different programming languages exist you will often find that their creators wanted to solve a particular type of problem and felt that the existing languages weren't suitable for doing so. C was created to make it easier to develop operating systems. Basic was designed to make it easier for non-mathematicians to program. The multitude of web frameworks exist to try and find an easy way of making web applications. The problem that a language was created to solve should be the focus of most comparisons.

Language Y is better at solving A Problems than Language X. Language Y sucks at solving B Problems, use Language Z instead. There are no languages that are good at solving all problems and there are no languages that aren't good at solving any problems. By giving context to the comparison it becomes less general and more specific.