movsd из памяти в xmm0 в c x86-64 jit - PullRequest
0 голосов
/ 26 июня 2019

Я пытаюсь написать небольшой JIT x86-64, и я немного над головой в некоторых местах.

Я пытаюсь JIT простой функции, которая присваивает значение с плавающей точкой в ​​регистр xmm0, а затем возвращает его, но я не уверен, как мне следует кодировать аргументы для вызова movsd.

Любая помощь будет принята с благодарностью.

/* main.c */
#include <stdio.h>
#include <sys/mman.h>

#define xmm(n) (n)

typedef double(*fn)();

fn jit(){
    char* memory = mmap(NULL,
                        4096,
                        PROT_READ|PROT_WRITE|PROT_EXEC,
                        MAP_PRIVATE|MAP_ANONYMOUS,
                        -1, 0);
    int i=0;
    float myfloat = 3.1f;
    memory[i++] = 0x48; /* REX.W */
    memory[i++] = 0xf2; /*******************/
    memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
    memory[i++] = 0x10; /*******************/

    memory[i++] = 0x47 | xmm(0) << 3; /* Not 100% sure this is correct */

    memory[i++] = 0; /* what goes here to load myfloat into xmm0? */

    memory[i++] = 0xc3; /* RET */
    return (fn) memory;
}

int main(){
    fn f = jit();
    printf("result: %f\n", (*f)());
    return 0;
}

1 Ответ

2 голосов
/ 26 июня 2019

Инструкции SSE, как правило, не поддерживают немедленные действия, за исключением некоторых редких инструкций с однобайтовым немедленным для управления их работой.Таким образом, вам необходимо:

  • сохранить myfloat в какой-нибудь соседней области памяти
  • создать операнд памяти, ссылающийся на эту область

Оба шага просты,Для первого шага я бы просто использовал начало memory и позволил бы коду начать сразу после этого.Обратите внимание, что в этом случае вам необходимо обязательно вернуть указатель на начало функции, а не на начало memory.Возможны и другие решения.Просто убедитесь, что myfloat хранится в пределах ± 2 ГБ от кода.

Чтобы сгенерировать операнд, пересмотрите руководства Intel.Требуемый режим адресации - это 32-битный RIP-операнд.Это генерируется с mod = 0, r / m = 5. Смещение - это 32-разрядное число со знаком, которое добавляется к значению RIP прямо в конце инструкции (это то, откуда берется +4, так как нужно учитывать множитель).в длину смещения).

Таким образом, мы имеем что-то вроде:

memory[i++] = 0xf2; /*******************/
memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
memory[i++] = 0x10; /*******************/
memory[i++] = 0005 | xmm(0) << 3; /* mod = 0, r/m = 5: [rip + disp32] */
*(int *)(memory + i) = memory + i + 4 - addr_of_myfloat;
i += 4;
memory[i++] = 0xc3; /* RET */

Обратите внимание, что префикс REX здесь не нужен.

...