Структура данных для ассемблерного кода? [исследовать]

Я планирую создать структуру данных, оптимизированную для хранения кода сборки. Таким образом, я могу полностью нести ответственность за алгоритмы оптимизации, которые будут работать над этой структурой. Если я могу скомпилировать во время работы. Это будет своего рода динамическое исполнение. Это возможно? Кто-нибудь видел что-то подобное?

Следует ли использовать структуры для связывания структуры в поток программы. Предметы лучше?

struct asm_code {
   int type;
   int value;
   int optimized;
   asm_code *next_to_execute;
 } asm_imp;

Обновление: Думаю получится, вроде связанный список.

Обновление: я знаю, что есть и другие компиляторы. Но это военный сверхсекретный проект. Поэтому мы не можем доверять никакому коду. Мы должны делать все сами.

Обновление: хорошо, я думаю, я просто сгенерирую базовый машинный код i386. Но как мне прыгнуть в свой блок памяти, когда он будет готов?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
5 427
5

Ответы 5

Это возможно. Генерация динамического кода даже широко используется в некоторых областях, таких как программный рендеринг и графика. Вы находите широкое применение во всех типах языков сценариев, в динамической компиляции байтового кода в машинный код (.NET, Java, насколько я знаю Perl. Недавно к клубу присоединился и JavaScript).

Вы также находите, что это используется в очень сложных математических приложениях. Это имеет значение, если вы, например, удалите все умножение с нулем из умножения матриц, если вы планируете проделать такое умножение несколько тысяч раз.

Я настоятельно рекомендую вам прочитать представление кода SSA. Это представление, в котором каждый примитив превращается в так называемую форму трех операндов, и каждая переменная назначается только один раз (следовательно, та же форма статического одиночного присвоения).

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

Чтобы понять, как выглядит SSA, вы можете попробовать компилятор LLVM. На их веб-сайте есть небольшой виджет «Попробовать», с которым можно поиграть. Вы вставляете код C в окно и получаете что-то, близкое к форме SSA.

Небольшой пример того, как это выглядит:

Давайте возьмем этот алгоритм целочисленного квадратного корня в C. (произвольный пример, я просто взял что-то простое, но нетривиальное):

unsigned int isqrt32 (unsigned int value) 
{
    unsigned int g = 0;
    unsigned int bshift = 15;
    unsigned int b = 1<<bshift;
    do {
        unsigned int temp = (g+g+b)<<bshift;
        if (value >= temp) {
            g += b;
            value -= temp;
        }
        b>>=1;
    } while (bshift--);
    return g;
}

LLVM превращает это в:

define i32 @isqrt32(i32 %value) nounwind  {
entry:
    br label %bb

bb:     ; preds = %bb, %entry
    %indvar = phi i32 [ 0, %entry ], [ %indvar.next, %bb ]      
    %b.0 = phi i32 [ 32768, %entry ], [ %tmp23, %bb ]
    %g.1 = phi i32 [ 0, %entry ], [ %g.0, %bb ]     
    %value_addr.1 = phi i32 [ %value, %entry ], [ %value_addr.0, %bb ]      
    %bshift.0 = sub i32 15, %indvar 
    %tmp5 = shl i32 %g.1, 1 
    %tmp7 = add i32 %tmp5, %b.0 
    %tmp9 = shl i32 %tmp7, %bshift.0    
    %tmp12 = icmp ult i32 %value_addr.1, %tmp9      
    %tmp17 = select i1 %tmp12, i32 0, i32 %b.0      
    %g.0 = add i32 %tmp17, %g.1     
    %tmp20 = select i1 %tmp12, i32 0, i32 %tmp9     
    %value_addr.0 = sub i32 %value_addr.1, %tmp20           
    %tmp23 = lshr i32 %b.0, 1       
    %indvar.next = add i32 %indvar, 1       
    %exitcond = icmp eq i32 %indvar.next, 16    
    br i1 %exitcond, label %bb30, label %bb

bb30:       ; preds = %bb
    ret i32 %g.0
}

Я знаю, что сначала это выглядит ужасно. Это даже не чистая SSA-форма. Чем больше вы читаете об этом изображении, тем больше в нем смысла. И вы также узнаете, почему это представление так широко используется в наши дни.

Инкапсулировать всю необходимую информацию в структуру данных очень просто. В конце концов, вы должны решить, хотите ли вы использовать перечисления или строки для имен опкодов и т. д.

Кстати, я знаю, что дал вам не структуру данных, а скорее формальный, но практичный язык и совет, где искать дальше.

Это очень красивое и интересное направление исследований.

Обновлено: И пока я не забыл: не упускайте из виду встроенные функции .NET и Java. Эти языки позволяют компилировать из байт-кода или исходного кода внутри программы и выполнять результат.

Ваше здоровье, Нильс


Что касается вашего редактирования: как выполнить двоичный blob с кодом:

Переход к вашему двоичному BLOB-объекту зависит от ОС и платформы. Короче говоря, у вас отключен кеш инструкций, может быть вам нужно выполнить обратную запись в кеш данных, и вам, возможно, придется включить права выполнения в области памяти, в которую вы записали свой код.

В win32 это относительно просто, поскольку очистка кеша инструкций кажется достаточной, если вы поместите свой код в кучу.

Вы можете использовать эту заглушку для начала:

typedef void (* voidfunc) (void);

void * generate_code (void)
{
    // reserve some space
    unsigned char * buffer = (unsigned char *) malloc (1024);


    // write a single RET-instruction
    buffer[0] = 0xc3; 

    return buffer;
}

int main (int argc, char **args)
{
    // generate some code:
    voidfunc func = (voidfunc) generate_code();

    // flush instruction cache:
    FlushInstructionCache(GetCurrentProcess(), func, 1024);

    // execute the code (it does nothing atm)
    func();

    // free memory and exit.
    free (func);
}

Нет проблем - это одна из моих любимых тем, поэтому ответ оказался скорее пустословием. :-)

Nils Pipenbrinck 20.09.2008 19:39

Я хочу найти основную проблему с представлением asm-кода в памяти. Я думаю, это может стать следующим большим событием. Что, если бы мы могли просто перепрыгнуть через процесс ссылки. Это было бы замечательно. :) Ненавижу время ожидания.

Flinkman 20.09.2008 19:55

В 99% случаев разница в производительности незначительна. Основное преимущество классов состоит в том, что код, созданный ООП, лучше и проще для понимания, чем процедурный код.

Я не уверен, на каком языке вы кодируете - обратите внимание, что в C# основное различие между классами и структурами заключается в том, что структуры являются типами значений, а классы - ссылочными типами. В этом случае вы можете начать со структур, но все же добавить к ним поведение (конструктор, методы).

Не обсуждая техническую ценность оптимизации вашего кода, в коде C++ выбор между структурой POD или полным объектом в основном является точкой инкапсуляции.

Встраивание кода позволит компилятору оптимизировать (или нет) используемые конструкторы / средства доступа. Не будет потери производительности.

Сначала установите конструктор

Если вы работаете с компилятором C++, создайте хотя бы один конструктор:

struct asm_code {
   asm_code()
      : type(0), value(0), optimized(0) {}

   asm_code(int type_, int value_, int optimized_)
      : type(type_), value(value_), optimized(_optimized) {}

   int type;
   int value;
   int optimized;
 };

По крайней мере, в вашем коде не будет неопределенных структур.

Возможны ли все комбинации данных?

Использование структуры, подобной используемой вами, означает, что возможен любой тип, с любым значением и любой оптимизацией. Например, если я установил type = 25, value = 1205 и optimized = -500, то это нормально.

Если вы не хотите, чтобы пользователь помещал случайные значения в вашу структуру, добавьте встроенные средства доступа:

struct asm_code {

   int getType() { return type ; }
   void setType(int type_) { VERIFY_TYPE(type_) ; type = type_ ; }

   // Etc.

   private :
      int type;
      int value;
      int optimized;
 };

Это позволит вам контролировать то, что установлено внутри вашей структуры, и легче отлаживать ваш код (или даже выполнять проверку вашего кода во время выполнения).

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

Flinkman 20.09.2008 19:40

Спасибо Вам за Ваш вопрос. Теперь я понимаю, что мне, возможно, нужно ... подождать. мышление nop ...

Flinkman 20.09.2008 20:01

Я предполагаю, что вы хотите, чтобы структура данных содержала какой-то шаблон инструкций, вероятно, проанализированный из существующего машинного кода, например:

add r1, r2, <int>

У вас будет массив этой структуры, и вы выполните некоторую оптимизацию этого массива, возможно, изменив его размер или построив новый, и сгенерируете соответствующий машинный код.

Если ваша целевая машина использует инструкции переменной ширины (например, x86), вы не можете определить размер массива, не закончив синтаксический анализ инструкций, из которых вы строите массив. Также вы не можете точно определить, сколько буфера вам нужно, прежде чем фактически сгенерировать все инструкции из оптимизированного массива. Однако вы можете сделать хорошую оценку.

Проверьте GNU Lightning. Может быть вам полезно.

После некоторого чтения я пришел к выводу, что обычная шепелявка лучше всего подходит для этой задачи. С макросами lisp у меня огромные возможности.

Другие вопросы по теме