IL#指令未被C#公开

时间:2011-08-18 16:15:15

标签: c# il instructions

C#没有公开哪些IL指令?

我指的是sizeof和cpblk这样的指令 - 没有执行这些指令的类或命令(C#中的sizeof是在编译时计算的,而不是在运行时AFAIK计算的。)

其他?

编辑:我之所以这样问(希望这会让我的问题更有效)是因为我正在开发一个小型库,它将提供这些指令的功能。 sizeof和cpblk已经实现了 - 我想知道在继续之前我可能错过了什么。

EDIT2:使用Eric的回答,我编写了一份指令列表:

  • JMP
  • 愈伤组织
  • Cpobj
  • Ckfinite
  • 前缀[1-7]
  • Prefixref
  • Endfilter
  • 未对齐的
  • 尾调用
  • Cpblk
  • Initblk

列表中没有包含许多其他指令,我正在将它们分开,因为它们基本上是其他指令的快捷方式(压缩以节省时间和空间):

  • Ldarg [0-3]
  • Ldloc [0-3]
  • Stloc [0-3]
  • Ldc_ [I4_ [M1 / S / 0-8] / I8 / R4 / R8]
  • Ldind_ [I1 / U1 / I2 / U2 / I4 / U4 / I8 / R4 / R8]
  • Stind_ [I1 / I2 / I4 / I8 / R4 / R8]
  • Conv_ [I1 / I2 / I4 / I8 / R4 / R8 / U4 / U8 / U2 / U1]
  • Conv_Ovf_ [I1 / I2 / I4 / I8 / U1 / U2 / U4 / U8]
  • Conv_Ovf_ [I1 / I2 / I4 / I8 / U1 / U2 / U4 / U8] _Un
  • Ldelem_ [I1 / I2 / I4 / I8 / U1 / U2 / U4 / R4 / R8]
  • Stelem_ [I1 / I2 / I4 / I8 / R4 / R8]

4 个答案:

答案 0 :(得分:36)

  

我指的是sizeof和cpblk这样的指令 - 没有执行这些指令的类或命令(C#中的sizeof是在编译时计算的,而不是在运行时AFAIK计算的。)

这是不正确的。当然,sizeof(int)将被视为编译时常量4,但是有很多情况(全部在unsafe代码中),编译器依赖运行时来确定内存大小。结构是。例如,考虑一个包含两个指针的结构。它在32位机器上的大小为8,而在64位机器上为16。在这些情况下,编译器将生成sizeof操作码。

  

其他?

我没有列出我们不生成的所有操作码 - 我从来没有需要构建这样的列表。但是,我可以告诉你,在C#中无法生成“call indirect”(calli)指令;我们偶尔会被要求提供该功能,因为它可以提高某些互操作方案的性能。

更新:我刚刚使用源代码来生成一个我们绝对生成的操作码列表。他们是:

添加
add_ovf
add_ovf_un

arglist中
BEQ
beq_s
BGE
bge_s
bge_un
bge_un_s
BGT
bgt_s
bgt_un
bgt_un_s
BLE
ble_s
ble_un
ble_un_s
BLT
blt_s
blt_un
blt_un_s
bne_un
bne_un_s

BR
br_s
brfalse
brfalse_s
brtrue
brtrue_s
调用
callvirt
castclass
CEQ
CGT
cgt_un
CLT
clt_un
受限
conv_i
conv_ovf_i
conv_ovf_i_un
conv_ovf_u
conv_ovf_u_un
conv_r
conv_r_un
conv_u
DIV
div_un
DUP
endfinally
initobj
isinst
ldarg
ldarg_
ldarg_s
ldarga
ldarga_s
ldc_i
ldc_r
ldelem
ldelem_i
ldelem_r
ldelem_ref
ldelem_u
ldelema
ldfld
ldflda
ldftn
ldind_i
ldind_r
ldind_ref
ldind_u
ldlen
ldloc
ldloc_
ldloc_s
ldloca
ldloca_s
ldnull
ldobj
ldsfld
ldsflda
ldstr
ldtoken
ldvirtftn
离开
leave_s
localloc
mkrefany
MUL
mul_ovf
mul_ovf_un
NEG
newarr
newobj
NOP


流行
只读
refanytype
refanyval
REM
rem_un
RET
重新抛出
SHL
SHR
shr_un
的sizeof
starg
starg_s
stelem
stelem_i
stelem_r
stelem_ref
stfld
stind_i
stind_r
stind_ref
stloc
stloc_s
stobj
stsfld

sub_ovf
sub_ovf_un
切换

unbox_any
挥发性
xor

我不会保证这一切都是他们所有人,但这肯定是他们中的大部分。然后,您可以将其与所有操作码的列表进行比较,并查看缺少的内容。

答案 1 :(得分:13)

根据Eric的回答,这里有一些我发现的。在哪里我可以看到我指出它的原因,如果不是我自由推测。请随意指出这些推测是否错误。

  

Break

     

发出公共语言基础结构(CLI)信号,通知调试器已经触发了断点。

您可以通过调用System.Diagnostics.Debugger.Break()来执行此操作,这似乎不是直接使用该指令,而是使用烘焙到CLR中的BreakInternal()方法。

  

CpblkCpobj

     

将指定的数字字节从源地址复制到目标地址。   将位于对象地址(类型&,*或native int)的值类型复制到目标对象的地址(类型&,*或native int)。

我认为这些是为C ++ / CLI(以前的托管C ++)添加的,但这纯粹是对我的推测。它们也可能出现在某些系统调用中,但不会由编译器正常生成,并为不安全的游戏和游戏提供一些空间。

  

Endfilter

     

将控制从异常的filter子句转移回公共语言基础结构(CLI)异常处理程序。

C#不支持异常过滤。 VB编译器无疑会利用这个。

  

Initblk

     

将指定地址的指定内存块初始化为给定大小和初始值。

我将再次推测这在不安全的代码和C ++ / CLI

中可能有用
  

Jmp

     

退出当前方法并跳转到指定方法。

我将推测这种蹦床可能对那些想要避免尾调用的人有用。也许DLR使用它?

  

Tailcall

     

执行后缀方法调用指令,以便在执行实际调用指令之前删除当前方法的堆栈帧。

在其他地方深入讨论,目前c#编译器不会发出此操作码

  

Unaligned

     

表示当前位于评估堆栈顶部的地址可能未与紧随其后的ldind,stind,ldfld,stfld,ldobj,stobj,initblk或cpblk指令的自然大小对齐。

C#(和CLR)对其产生的大部分代码和数据的对齐性质提出了相当多的保证。毫不奇怪,这不会被发出,但我可以看出为什么它会被包括在内。

  

Unbox

     

将值类型的盒装表示形式转换为未装箱的形式。

c#编译器倾向于专门为此目的使用Unbox_Any指令。我认为,基于在2.0版本的指令集中添加它,它使得泛型要么可行,要么更简单。那时在整个代码中使用它来处理所有内容,泛型或其他内容,要么更安全,更简单或更快(或者是所有内容的某种组合)。


脚注:

  

Prefix1,Prefix2,Prefix3,Prefix4,Prefix5,Prefix6,Prefix7,Prefixref

     

基础结构。这是一条保留的指令。

这些不是指令,有些IL指令比其他指令长。这些可变长度的前缀应该从前缀开始,这些前缀本身永远不会有效,以使解析清晰。这些前缀操作码是为此保留的,因此它们不会在别处使用。毫无疑问,有人为IL序列实现基于switch语句的解析器会欣赏这些,这样他们就可以捕获这些并保持状态。

答案 2 :(得分:4)

一个有趣的例子是tail.callOpCodes.Tailcall),它可以为递归提供尾调用优化。

答案 3 :(得分:1)

.override IL 指令(我不知道它是否是正确的术语,但它肯定不是指令)是由C#编译器生成的,但仅限于显式接口实现的特殊情况。

能够更自由地使用它会很有趣,比如在VB.NET中,实现成员可以别名甚至拥有不同的访问修饰符接口成员。