Основы стека оценки .NET IL / MSIL - PullRequest
0 голосов
/ 02 марта 2019

Не могу найти хорошего ответа на эти вопросы.

Вот то, что, как мне кажется, я знаю, и о чем я размышляю.

  • Оценочный стекбуфер памяти, такой как стек стилей C (это стек собственных int / size_t)?
  • Элементы стека оценки могут быть 32- или 64-битными (как они смешиваются в одном стеке?)
  • Ldloc_0 сохраняет локальную переменную в стеке оценки, НО, как, если ее размер превышает 64 бита?
  • Хранит ли Ldloc_0 только ptrs для локальных переменных в стеке оценки?
  • Хранятся ли объекты встек оценки всегда либо указатели, либо примитивные значения?
  • Если .maxsize равен 8, значит ли это (8 * size_t)?Если да, то как, если я читаю документы с указанием 32 или 64 бит

Возьмите пример ниже.Сохраняется ли эта локальная переменная в стеке оценки с помощью ссылки ptr?

public struct MyStruct
{
    public long x, y, z;

    public static MyStruct Foo()
    {
        MyStruct c;
        c.x = 1;
        c.y = 2;
        c.z = 3;
        return c;   
    }
}

"ldloc.0" явно хранит структуру в стеке оценки, НО она также намного больше 64 бит.Вместо этого сохраняется ссылка?

.class public sequential ansi sealed beforefieldinit MyStruct
    extends [mscorlib]System.ValueType
{
    // Fields
    .field public int64 x
    .field public int64 y
    .field public int64 z

    // Methods
    .method public hidebysig static 
        valuetype MyStruct Foo () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 34 (0x22)
        .maxstack 2
        .locals init (
            [0] valuetype MyStruct,
            [1] valuetype MyStruct
        )

        IL_0000: nop
        IL_0001: ldloca.s 0
        IL_0003: ldc.i4.1
        IL_0004: conv.i8
        IL_0005: stfld int64 MyStruct::x
        IL_000a: ldloca.s 0
        IL_000c: ldc.i4.2
        IL_000d: conv.i8
        IL_000e: stfld int64 MyStruct::y
        IL_0013: ldloca.s 0
        IL_0015: ldc.i4.3
        IL_0016: conv.i8
        IL_0017: stfld int64 MyStruct::z
        IL_001c: ldloc.0// What is actually stored here?
        IL_001d: stloc.1
        IL_001e: br.s IL_0020

        IL_0020: ldloc.1
        IL_0021: ret
    } // end of method MyStruct::Foo

} // end of class MyStruct

Ответы [ 2 ]

0 голосов
/ 25 апреля 2019

Если .maxsize равен 8, значит ли это (8 * size_t)?

Директива .maxstack не соответствует фактическому размеру оценкистек во время выполнения.Вместо этого он указывает инструментам анализа , сколько элементов находится в стеке одновременно.Установка .maxstack неправильно (как, например, слишком мала), метод считается не поддающимся проверке, что может привести к проблемам в сценариях с низким уровнем доверия (но это не должно быть проблемой для вас, так как вы читаете CIL, а не пишете).

Например, давайте рассмотрим простой метод Add, который принимает параметры int, складывает их вместе, сохраняет результат в поле класса с именем sum и возвращает значение этого поля.

.method private hidebysig instance
    int32 Add (
        int32 value1,
        int32 value2
    ) cil managed
{
    .maxstack 3 // At most, there are three elements on the stack.

    ldarg.0                   // 1 item on the stack
    ldarg.1                   // 2 items on the stack
    ldarg.2                   // 3 items on the stack
    add                       // 2 items on the stack
    stfld    int32 Foo::sum   // 0 items on the stack
    ldarg.0                   // 1 item on the stack
    ldfld    int32 Foo::sum   // 1 item on the stack
    ret
}

В стеке оценки метода никогда не может быть более 3 элементов одновременно.


Источники:

ECMA-335,Раздел III.1.7.4

0 голосов
/ 02 марта 2019

Не все элементы стека имеют одинаковый размер и могут содержать типы значений (struct s) любого размера.Начиная с ECMA-335, раздел I.12.3.2.1:

Стек оценки состоит из слотов, в которых могут храниться данные любого типа, включая незаписанный экземпляр типа значения ,

[...]

Хотя некоторые JIT-компиляторы могут отслеживать типы в стеке более подробно, CLI требует, чтобы значения были только одним из:

  • int64, 8-байтовое целое число со знаком
  • int32, 4-байтовое целое число со знаком
  • native int, целое число со знаком или 4 или 8 байтов, в зависимости от того, что большеудобно для целевой архитектуры
  • F, значение с плавающей запятой (float32, float64 или другое представление, поддерживаемое базовым оборудованием)
  • &, управляемый указатель
  • O, ссылка на объект
  • *, «переходный указатель», который может использоваться только в теле одного метода, который указывает на значение, известное какв неуправляемой памяти (см. спецификацию CIL Instruction Set для более подробной информации. * типы создаются внутри CLI; они не создаются пользователем).
  • Пользовательский тип значения

Чуть раньше, в разделе I.12.1:

Пользовательопределяемые типы значений могут появляться в ячейках памяти или в стеке и не иметь ограничений по размеру

Таким образом, в вашем случае инструкция ldloc.0 загружает полностью экземпляр типа значения - с его тремя даннымиполя - в стек.

Благодаря этому ответу за указание на эти секции ECMA.Этот и другие ответы на этот вопрос указывают , почему стек может измеряться в слотах, а не в байтах: поскольку JIT-компилятор уже оценивает, как преобразовать MSIL в собственные инструкции, поэтому он должен знать типызначений в стеке в каждой инструкции.

...