【问题标题】:Does bounds-checking logic occur in MSIL or machine code边界检查逻辑是否出现在 MSIL 或机器代码中
【发布时间】:2016-08-06 07:07:18
【问题描述】:

我刚刚开始涉足 MSIL 的美妙世界,但我似乎无法在这里找到有关边界检查发生位置的任何答案。 C# 编译器是否插入执行边界检查的 MSIL 指令,或者 MSIL JIT 编译器在将这些指令转换为机器代码时是否为所有适用的 MSIL 指令插入这些指令?

我问的原因是因为我需要知道在我直接使用 MSIL 生成函数时是否需要添加这些指令。

编辑:收到一个答案并尝试验证后,它似乎不正确。以下代码实际上失败了。单步调试调试器发现第二个测试超出了数组边界,第三个测试做得更糟:

static class ArrayExtensions
{
#if false
    public static void ClearRangeReferenceImplementation(byte[] buffer, int offset, int byteCount)
    {
        for (int current = offset; current < offset + byteCount; ++current)
        {
            buffer[current] = 0;
        }
    }
#endif
    private static readonly Action<IntPtr, int, int> MemclearRaw = InitMemclearRaw();
    private static Action<IntPtr, int, int> InitMemclearRaw()
    {
        DynamicMethod memclearMethod = new DynamicMethod("Memclear", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null, new Type[] { typeof(IntPtr), typeof(int), typeof(int) }, typeof(this), true);
        ILGenerator il = memclearMethod.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Add);
        il.Emit(OpCodes.Ldc_I4_0);
        il.Emit(OpCodes.Ldarg_2);
        il.Emit(OpCodes.Initblk);
        il.Emit(OpCodes.Ret);
        return (Action<IntPtr, int, int>)memclearMethod.CreateDelegate(typeof(Action<IntPtr, int, int>));
    }
    /// <summary>
    /// Clears the specified range of the specified buffer in the most optimal manner available without resorting to PInvoke or inline assembly.
    /// </summary>
    /// <param name="buffer">The buffer to acted upon.</param>
    /// <param name="offset">The offset in the buffer where the clearing is to start.</param>
    /// <param name="count">The number of bytes to be cleared.</param>
    public static void ClearRange(this byte[] buffer, int offset, int count)
    {
        if (count == 0) return;
        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            MemclearRaw(handle.AddrOfPinnedObject(), offset, count);
        }
        finally
        {
            handle.Free();
        }
    }
}
[TestClass]
public class TestArrayExtensions
{
    /// <summary>
    /// Performs failure tests on <see cref="ArrayExtensions.ClearRange"/>.
    /// </summary>
    [TestMethod]
    public void Array_ClearRangeExceptions()
    {
        byte[] b = Rand.NewBytes(0, 100);
        AssertExceptionClearRange(null, 0, b.Length, typeof(NullReferenceException));
        b = Rand.NewBytes(0, 100);
        AssertExceptionClearRange(b, 0, b.Length + 1, typeof(ArgumentOutOfRangeException));
        b = Rand.NewBytes(0, 100);
        AssertExceptionClearRange(b, 0, -1, typeof(ArgumentOutOfRangeException));
        b = Rand.NewBytes(0, 100);
        AssertExceptionClearRange(b, -1, b.Length, typeof(ArgumentOutOfRangeException));
    }

    private static void AssertExceptionClearRange(byte[] buffer, int offset, int count, Type exceptionType)
    {
        try
        {
            ArrayExtensions.ClearRange(buffer, offset, count);
            Assert.Fail("ArrayExtensions.ClearRange did not throw the expected exception!");
        }
        catch (Exception ex)
        {
            Assert.AreEqual(exceptionType, ex.GetType());
        }
    }

MSIL 似乎只进行空指针检查,而不是边界检查。

我目前正在使用 VS2012 并以 .NET Framework 4.5.1 为目标,如果这有什么不同的话。

【问题讨论】:

  • 不在MSIL中,jitter生成机器码做边界检查。以及产生异常的代码。并且知道如何省略该代码,因为它可以判断索引永远不会越界,这是它不在 MSIL 中的最终原因。
  • @HansPassant:情况似乎并非如此。请使用上面的代码进行验证。
  • 很难看出您要证明什么,C# 和 VB.NET 编译器都不会生成 OpCodes.Initblk。它不是可验证的指令。 C++/CLI 编译器可以将它用于非托管 C++ 代码。
  • @HansPassant:您是说大多数 MSIL 指令都进行边界检查,但这个没有?有什么方法可以知道哪些该做哪些不做?

标签: .net jit cil


【解决方案1】:

C# 编译器做的优化很少。

所有的魔法都发生在生成机器码的 JIT 中。

要回答这个问题,如果您生成与 C# 或 VB.NET 类似的代码(JIT 可以确定这是某种形式的 for 循环等),它将起作用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-06
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多