IL#指令未被C#公开

Yel*_*ika 33 c# il instructions

C#没有暴露哪些IL指令?

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

其他?

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

编辑2:使用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]

Eri*_*ert 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

newarr
newobj
NOP


弹出
只读
refanytype
refanyval
REM
rem_un
保留
重新抛出
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
挥发性
异或

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

  • @YelPika:假设您有一个指向C++对象的vtable的原始指针,并且您希望调用该vtable中的一个方法,其签名是您所知道的.有没有办法在C#中,所以今天直接做,即使在不安全的代码,因为没有办法表达的:"这是一个指向一个内存位置,它包含的代码返回void,并需要通过一个int的方法概念在堆栈上".您需要做的是使用该签名构建一个委托对象,然后将指针指向委托构造函数,然后调用该委托.这是非常缓慢和记忆沉重的. (5认同)

Shu*_*oUk 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语句的解析器的人会欣赏这些,因此他们可以捕获这些并保持状态.