我正在开发一个系统,该系统在开发时将一整套.NET程序集"烘焙"为本机代码。理想情况下,它将直接输出机器语言,但为了避免处理寄存器着色、不同目标平台的不同机器代码等问题,我选择让它输出C代码,然后使用现有编译器将其编译为ML。
这带来的一个问题是,在C代码中无法有效地执行整数数学运算的溢出检查。某些CIL指令(即带有.ovf
后缀的指令)明确检查溢出。另一个问题的众多答案提出了在C中进行这种检查的各种技术,但与JIT编译器可能输出的机器代码(例如,英特尔上的jo
或into
)相比,它们的性能都非常差
考虑到应用程序是相当关键的性能,忽略这些指令上的.ovf
并发出与未检查变体相同的C代码将是非常诱人的。严格地说,根据ECMA-335,这当然会破坏我的CLI实现,但我想知道它在实践中是否真的会被破坏。
我的印象是,这些检查主要用于程序调试。在使用我的CLI的情况下,该程序已经调试完毕,并且它没有控制核弹头(无论如何都不是真正的核弹头)
该系统主要用于我们自己的代码,这些代码都不依赖于溢出检查的正确性。(根据这一点,这将是一种糟糕的形式。)我主要关心的是我们可能链接到的.NET类库,当然我的系统也必须处理这些类库。
在我预计必须链接到的所有代码中,我主要关心的是Mono附带的BCL。我们正在考虑使用BCL,因为它已经是现成的,并且获得了大量许可。然而,当我从Mono对System.dll进行IL转储时,我发现它充满了.ovf
指令(其中许多是conv.ovf
,它可以在C中像在ML中一样高效地实现,但有些是算术上的。)
Mono的BCL是否真的依赖于这些正确性检查,或者它们只是可以安全地忽略已经被认为是健全的代码的健全性检查?(我期待后者,但我想我会问,以防其他人知道得更好。)
我认为,将其转换为C语言的主要问题是,CIL本质上是一种比C语言更低级别的语言,而且通常很难"攀登"语言级别。您的示例显示了这一点,因为检查溢出在x86中是微不足道的,但在C.中实际上相当困难
就忽略Mono BCL中的溢出检查而言,最重要的问题是
如果不需要检查,为什么要使用它们?
Mono BCL是一个相当成熟的库,因此期望它由知道自己在做什么的人编写得很好是明智的;因此,如果您通过删除溢出检查来修改代码,代码可能会出现,但请记住,溢出情况应该是异常,而不是规则。此外,虽然以前的溢出会立即引发异常,但现在它会在可能会或不会导致更大问题的地方传递"坏数据"。
简而言之,删除溢出检查可能意味着大多数时候大多数代码都能工作,但不能再保证它能工作。这意味着,除非你非常彻底地检查每一个可能的代码路径,或者用测试用例覆盖所有代码,否则你会因为避免这些检查而面临非常大的风险。