博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# to IL 3 Selection and Repetition(选择和重复)
阅读量:5912 次
发布时间:2019-06-19

本文共 15290 字,大约阅读时间需要 50 分钟。

In IL, a label is a name followed by the colon sign i.e ":". It gives us the ability to jump from

one part of the code to another, unconditionally. We have been constantly witnessing the
labels in the il code generated by the disassembler. For e.g.

The words preceding the colon are labels. In the program given below, we have created a

label called a2 in the abc function. The instruction br facilitates the jumping to any label in
the program, whenever desired

The function abc demonstrates this concept. In this function, the code bypasses the

instruction ldc.i4.s 30. Therefore, the return value is displayed as 20, and not 30. Thus, IL
uses the br instruction to jump unconditionally to any part of the code. (The assembly
instruction br takes 4 bytes whereas br followed by .s i.e br.s takes 1 byte, the same
explanation is applicable for every instruction tagged with .s)
The br instruction is one of the key pivots on which IL revolves. 

We have initialized the static variable to the value true in our C# program.

Static variables, if they are fields, are initialized in the static constructor .cctor. This
is shown in the above example.
Local variables, on the other hand, are initialized in the function that they are
present in.
Here, surprisingly, the value 1 is placed on the stack in the static constructor using the ldc
instruction. Even though the field i had been defined to be of type bool in both, C# and IL,
there is no sign of true or false values.
Next, stsfld is used to initialize the static variable i to the value 1 even though the variable
is of the type bool. This proves that IL supports the concept of a data type called bool but,
it does not recognise the words true and false. Thus, in IL, bool values are simply aliases
for the numbers 1 and 0 respectively.
The bool operators TRUE and FALSE are artefacts introduced by C# to make the life of
programmers easier. Since IL does not support these artefacts directly, it uses the numbers
1 and 0 instead.
The instruction ldsfld places the value of a static variable on the stack. The brfalse
instruction scans the stack. If it finds the number as 1, it interprets it as TRUE, and if it
finds the number 0, it interprets it as FALSE.
In this example, the value it finds on the stack is a 1 or TRUE and hence, it does not jump
to the label IL_0011. On conversion from C# to IL, ildasm replaces the label with a name
beginning with IL_.
The instruction brfalse means "jump to the label if FALSE". This differs from br, which
always results in a jump. Thus, brfalse is called a conditional jump instruction.
There is no instruction in IL that provides the functionality of the if statement. The if
statement of C# gets converted to branch instructions in IL. None of the assemblers that
we have worked with, support high level concepts like the if construct.
It can be appreciated from what we have just learnt that, it is imperative to gain mastery
over IL. This will help one to gain the ability to differentiate as to which concepts are a part
of IL and which ones have been introduced by the designers of the programming
languages.
It is significant to note that if IL does not support a certain feature, it cannot be
implemented in any .NET programming language. Thus, the importance of familiarising
oneself with the various concepts that IL supports, cannot be over emphasised.

An if-else statement is extremely simple to comprehend(理解) in a programming language, but it

is equally baffling in IL. IL checks whether the value on the stack is 1 or 0.
If the value on the stack is 1, as in this case, it calls the WriteLine  function with the
parameter "hi", and then jumps to the label IL_001d using the unconditional jump
instruction br.

If the value on the stack is 0, the code jumps to IL_0013 and the WriteLine function

prints false.
Thus, to implement an if-else construct in IL, a conditional and unconditional jump are
required. The complexity of the IL code increases dramatically if we use multiple if-else
statements.
You can now appreciate the intelligence level of the people who write compilers

The C# programming language can complicate life. In an inner set of braces, we cannot

create a variable that is already created earlier, in an outer set. The above C# program is
syntactically correct since the braces are at the same level.
In IL, life is comparatively hassle free. The two i's become two separate variables V_0 and
V_1. Thus, IL does not impose any of the restrictions on variables

 

On seeing the disassembled code, you will comprehend as to why programmers do not

write IL code for a living. Even a simple while loop gets converted into IL code of
stupendous(惊人的) complexity.
For a while construct, unconditionally a jump is made to the label IL_000c which is at the
end of the function. Here, it loads the value of the static variable i on the stack.
The next instruction, brtrue, does the reverse of what the instruction brfalse does. It is
implemented as follows:
If the uppermost value on the stack, i.e. the value of the field i, is 1, it jumps to label
IL_0002. Then the value "hi" is put on the stack and the WriteLine function is called.
If the stack value is 0, the program will jump to the ret instruction.
The above program, as you may have noticed, does not intend to stop. It continues to flow
like a perennial stream of water originating from a gigantic glacier

IL does not have an operator for adding two numbers. The add instruction has to be used

instead.
The add instruction requires the two numbers to be added, to be first made available on
the stack. Therefore, the ldsfld instruction places the value of the static variable i and the
constant value 3 on the stack. The add instruction then adds them up and places the
resultant sum on the stack. It also removes the two numbers, that were used in the
addition, from the stack.
Most instructions in IL get rid of the parameters that are placed on the stack for the
instruction to operate upon, once the instruction has been executed.
The instruction stsfld is used to initialize the static variable i with the resultant sum of the
addition. The rest of the code simply displays the value of the variable i.
There is no equivalent for the ++ operator in IL. It gets converted to the instruction ldc.
i4.1. In the same vein,to multiply two numbers, the mul instruction is used, to subtract,
sub is used and so on. They all have their equivalents in IL. The code following it remains
the same.

We shall now delve upon how IL handles the conditional operator. Let us consider the

statement j > 16 in C#. IL first pushes the value of j on the stack followed by the constant
value16. It then calls the operator cgt, which is being introduced for the first time in our
source code. This instruction checks if the first value on the stack is larger than the
second. If so, it puts the value 1 (TRUE) on the stack, or else it puts the value 0 (FALSE) on
the stack. This value is then stored in the variable i . Using the WritleLine function, a bool
output is produced, hence we see True displayed.
In the same vein, the < operator gets converted to the instruction clt, which checks if the
first value on the stack is smaller than the second. Thus, we can see that IL has its own
set of logical operators to internally handle the basic logical operations

The operator == is the EQUALITY operator It also needs the two operands to be checked for

equality, be placed on the stack. It thereafter uses the ceq instruction to check for equality.
If they are equal, it places the value 1 (TRUE) on the stack, and if they are not equal, it
places the value 0 (FALSE) on the stack . The ceq instruction is an integral part of the
logical instruction set of IL

The implementation of the "less than or equal to" (i.e. <= ) and the "greater than or equal

to" (i.e. >=)operator is a little more complex. They both actually have 2 conditions rolled
into one.
In the case of >=, IL first uses the cgt instruction to check if the first number is greater
than the second one. If so, it will return the value 1 or else it will return value 0. If the first
condition is FALSE, the ceq instruction checks for the two numbers to be equal. If so, it
returns a TRUE, or else it returns a FALSE.
Let us try to decipher the above IL code from a slightly different perspective. We are
comparing the value 19 with 16. In this case, the instruction cgt will put the value 1 on the
stack since 19 is greater than 16. The value 0 is put on the stack using the instruction ldc.
The ceq will compare the value 1 returned by the instruction cgt and the value 0 that was
put on the stack by the instruction ldc. Since these two values are not equal, ceq will
return 0 or FALSE on the stack.
Let us change the value of the field j in the static constructor to 1. Now, since the number
1 is not greater than 16, the cgt instruction will place the value FALSE or 0 on the stack.
Thereafter, another 0 is placed on the stack by the ldc instruction. Now, when the
instruction ceq compares the two values, since they are both 0, it return TRUE

Now, if we change the value of j to 16, the cgt instruction will return a FALSE because 16

is not greater than 16. Thereafter, since the value of 0 is placed on the stack by the
instruction ldc, both the values passed to the instruction ceq will be 0. Since a 0 is equal
to a 0, the value returned will be 1 or TRUE.
If you have not understood the above explanation, remove the lines ldc.i4.0 and ceq from
the source code and observe the output.

The "not equal to" operator i.e. != is the reverse of ==. It uses two ceq instructions. The first

ceq instruction is used to check whether the values on the stack are equal. If they are
equal, it returns TRUE; if they are not equal, it returns FALSE.
The second ceq compares the result of the earlier ceq with a FALSE. If the result of the first
ceq is TRUE, the final answer is FALSE and vice versa.
This is truly an ingenious way of negating a value !

We shall now refocus on the while loop after the slight digression into conditional

statements. This diversion was essential because we use conditional statements in loops
such as the while loop. A while loop containing a condition is slightly complex.
Let us go straight to label IL_0018, which is at the end of the zzz function in IL code. The
condition is present here. The value of i (i.e. 1) is stored on the stack. Next, the constant 2
is placed on the stack.
If you revisit the C# code, the condition in the while statement is i <= 2. The instruction ble.
s is based on the two instructors, cgt and brfalse. This instruction checks whether the first
value, i.e. the variable i, is less than or equal to the second. If so, it instructs the program
to jump to the label IL_0002. If not, the program moves to the next instruction.
Thus, instructions like ble make our life simpler because we do not have to use the
instructions cgt and brfalse anymore.
In C#,the condition of a while construct is present at the top, but the code of the condition,
is present at the bottom. On conversion to IL,the code to be executed for the duration of
the while construct is placed above the code for the condition.

It has been oft repeated that the while and the for constructs provide the same

functionality, and can be interchanged.
In the for loop, the code upto the first semicolon is to be executed only once. Hence, the
variable i that is to be initialised, is placed outside the loop. Then, we unconditionally jump
to label IL_001e to check whether the value of i is less than 2 or not. If TRUE, the code
jumps to label IL_0008, which is beginning point of the code of the for statement.
The value of i is printed using the WriteLine function. Thereafter, the value of the variable i
is increased by one and the condition is checked once again.

The difference between a do while and a while in a C# program lies in the position at which

the condition gets checked.
In a do while, the condition gets checked at the end of the loop. This means that the
code contained in it will get called at least once.
In a while, the condition is checked at the beginning of the loop. Hence, the code may
never ever get executed.
In either case, we place the value 1 on the stack and initialise the variable i or V_1.
In the while loop, we first jump to label IL_000e where the condition checked is
whether the variable is "less than or equal to 2". If TRUE, we jump to Label IL_0004.
In the do while loop, first the Write function is called and then, the rest of the code
contained in the {} braces is executed. On reaching the last line of the code within the
braces, the condition is checked.
Thus, it is easier to write a do-while loop in IL than a while loop, since the condition is a
simple check at the end of the loop

A break statement facilitates an exit from a for loop, while loop, do-while loop etc.

As usual, we jump to the label IL_0014 where the value of variable V_0 or i is placed on the
stack. Then, we place the condition value 10 on the stack and check whether i is smaller or
larger than 10, using the instruction ble.s.
If it is smaller, we get into the loop at label IL_0004. We again place the value of the
variable i on the stack and place the value 2 of the if statement on the stack. Then, we use
the bne instruction, which is a combination of the ceq and the brfalse instructions.
If the variable V_0 is TRUE, the break statement ensures an exit from the loop by jumping
to the ret statement at label IL_0019 using the instruction br.s.

A continue statement takes control to the end of the for loop. When the if statement results

in true, the program will jump to the end of the loop, bypassing the WriteLine function. The
code will then resume execution at label IL_0010 where, the value of the variable V_0 is
incremented by 1.
The main difference between the break and the continue statements is as follows:
In a break statement, the programs jumps out of the loop.
In a continue statement, the program jumps to the end of the loop, bypassing the
remaining statements.
A goto statement could have also been used to achieve the same functionality. Thus, the
break, continue or goto statements, on conversion to IL, are transformed into the same br
instruction.
The program demonstrates that a goto statement of C# is simply translated into a br
instruction in IL.

 

A simple goto statement in C# is translated into a br instruction in IL. Using a goto is

considered inappropriate in languages like C# but, its equivalent br instruction in IL is
extensively utilised for implementing various constructs like the if statement, loops etc.
Thus, what is taboo in a programming language is extremely useful in IL.

This example illustrates a for statement. We have created a variable j in the function Main

and a variable i in the for statement. This variable i is visible only in the for loop in C#.
Thus, this variable has a limited scope.
But on conversion to IL, all variables are given the same scope. This is because, the
concept of variable scoping is alien to IL. Therefore, it is upto the C# compiler to enforce
the rules of variable scoping. We can therefore conclude that, all variables have the same
scope or visibility in IL.

转载于:https://www.cnblogs.com/revoid/p/6690927.html

你可能感兴趣的文章
MySQL运维进阶-MySQL双主(master-master)+半同步(Semisync Repl
查看>>
盘点几个在手机上可以用来学习编程的软件
查看>>
深信服防火墙设备故障机的更换方法
查看>>
朝鲜APT集团Lazarus通过KEYMARBLE Backdoor瞄准俄罗斯组织
查看>>
Spring Cloud--Honghu Cloud分布式微服务云系统—云架构代码结构构建
查看>>
Linux虚拟机中搭建PHP服务
查看>>
linux笔记4.0
查看>>
nginx配置文件中的location详解
查看>>
技本功丨呀~我不会写CSS之vertical-align(上集)
查看>>
技本功丨收藏!斜杠青年与你共探微信小程序云开发(下篇)
查看>>
mongodb基础(1)
查看>>
httpd
查看>>
php 笔试题汇总
查看>>
能冒泡的事件
查看>>
easyui-tree 修改图标
查看>>
变频电源老化测试重要吗?需要做老化测试吗
查看>>
Linux下Nginx源码安装
查看>>
一文带你快速了解,python是如何解析XML文件
查看>>
如何用30分钟快速优化家中Wi-Fi?阿里工程师有绝招
查看>>
云越发展,锁定问题就会越严重?
查看>>