Форум программистов, компьютерный форум, киберфорум
Наши страницы

C++

Войти
Регистрация
Восстановить пароль
 
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 2
Завершенные тесты: 1
#1

Нетривиальный тривиальный калькулятор - C++

28.07.2014, 21:57. Просмотров 563. Ответов 4
Метки нет (Все метки)

Привет!
Ковырясь в OpenJDK разобрался как генерится/исполняется ассемблерный код в рантайме. Решил поделиться увиденным подходом, написал калькулятор, который часто здесь просят студенты (ввести 5 + 5, напечатать ответ).
Обычно это выглядит так
C++
1
2
3
4
5
switch (op) {
case '+': return a + b;
case '-': return a -b;
// etc
}
а вот как это выглядит с генерацией кода в рантайме (только linux x64)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/mman.h>
 
 
int main()
{
    int a, b;
    char op;
    std::cin >> a >> op >> b;
 
    const int page_size = sysconf(_SC_PAGESIZE);
    unsigned char *buffer = new unsigned char[page_size * 2];    
    unsigned char *page_start = (unsigned char*)(((long)buffer) & ~(page_size - 1)); // необходимое выравнивание
    
    unsigned char opcodes[3][128] = {
      {0x48, 0x01, 0xF7, 0x48, 0x89, 0xF8, 0xC3},
      {0x48, 0x29, 0xF7, 0x48, 0x89, 0xF8, 0xC3}, 
      {0x48, 0x89, 0xF8, 0xF7, 0xE6, 0xC3}, 
    };
    
    int op_pos = std::string("+-*").find(op);
    memcpy(page_start, &opcodes[op_pos][0], 128);
 
    if (mprotect(page_start, page_size, PROT_EXEC)) {
        std::cout << "mprotect error1" << std::endl;
        return 1;
    }
    
    int res = ((int (*)(int, int))page_start)(a, b);
    std::cout << a << " " << op << " " << b << " = " << res << std::endl;
    
    return 0;
}
писал на коленке, поэтому не пинайте за константы 3, 128, etc. Может для кого-то это не будет новым, но многим, думаю, будет интересно.

P.S. допустимые арифметические операции +, - и *. Деление было уже влом делать Никаких проверок ввода, как видите, нет.
2
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
28.07.2014, 21:57
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Нетривиальный тривиальный калькулятор (C++):

Тривиальный move конструктор - C++
Всем привет. Вобщем пишу код используя классы некой библиотеки, полагаясь, что в будущем разработчики добавят move-конструкторы в эти...

Нетривиальный прямой перебор - C++
Собстевенно g++, QT. Даю упрощенную постановку задачи, ибо реальная тяжелая для понимания. Есть 7 видов гирек разной массы для...

Найти и вывести на экран наибольший нетривиальный делитель числа - C++
Пользователь вводит с клавиатуры натуральное число, найти и вывести на экран наибольший нетривиальный (т.е. не равный введённому числу или...

Простой калькулятор и калькулятор с парсингом - C++
Ребят я совсем не давно только начал изучать сишку, решил написать простенький калькулятор который работает с 2 числами. Возник вопрос, как...

Что такой тривиальный и нетривиальный решений - Дифференциальные уравнения
Здравствуйте, эти термины не как не понимаю. Что такой тривиальное и нетривиальное решение Например, для того, чтобы решит...

Нужен нетривиальный виджет - C++ Qt
Здравствуйте. Виджет, на который транслируется рандомная область (заданного размера) графического изображения с большой частотой...

4
ISergey
Maniac
Эксперт С++
1395 / 906 / 56
Регистрация: 02.01.2009
Сообщений: 2,706
Записей в блоге: 1
31.07.2014, 17:40 #2
Для наглядности

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 unsigned char opcodes[3][128] = {
    
    /* a + b */
    
    { 
        0x48, 0x01, 0xF7,   /* add rdi, rsi */
        0x48, 0x89, 0xF8,   /* mov rax, rdi */
        0xC3                /* ret          */
    },
    
    /* a - b */
    
    {
        0x48, 0x29, 0xF7,   /* sub rdi, rsi */ 
        0x48, 0x89, 0xF8,   /* mov rax, rdi */ 
        0xC3                /* ret          */
    }, 
    
    /* a * b */
    
    {
        0x48, 0x89, 0xF8,   /* mov rax, rdi */  
        0xF7, 0xE6,         /* mul rsi      */ 
        0xC3                /* ret          */
    }, 
    
    /* a / b */ 
    
    {
        0x52,               /* push rdx     */
        0x48, 0x31, 0xD2,   /* xor rdx, rdx */
        0x48, 0x89, 0xF8,   /* mov rax, rdi */
        0x48, 0xF7, 0xFE,   /* idiv rsi     */ 
        0x5A                /* pop rdx      */
        0xC3                /* ret          */
    }, 
  
};
1
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 2
Завершенные тесты: 1
31.07.2014, 19:30  [ТС] #3
Тогда еще наверное стоит сказать, что согласно call convention x64 первые 2 аргумента лягут на регистры rdi и rsi соответственно.
0
ISergey
Maniac
Эксперт С++
1395 / 906 / 56
Регистрация: 02.01.2009
Сообщений: 2,706
Записей в блоге: 1
31.07.2014, 19:48 #4
Цитата Сообщение от Kastaneda Посмотреть сообщение
Тогда еще наверное стоит сказать, что согласно call convention x64 первые 2 аргумента лягут на регистры rdi и rsi соответственно.
На Windows в rcx, rdx

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <Windows.h>
 
 
int main()
{
    int a, b;
    char op;
    DWORD oldProtect;
 
    std::cin >> a >> op >> b;
 
    unsigned char opcodes[3][128] = {
 
        /* a + b */
        {
            0x48, 0x01, 0xd1, /* add rcx, rdx */
            0x48, 0x89, 0xc8, /* mov rax, rcx */
            0xc3
        }
 
        /*.......*/
    };
 
    int op_pos = std::string("+-*").find(op);
 
    VirtualProtect(&opcodes[op_pos][0], 128, PAGE_EXECUTE_READWRITE, &oldProtect);
 
    int res = ((int (*)(int, int))&opcodes[op_pos][0])(a, b);
 
    std::cout << a << " " << op << " " << b << " = " << res << std::endl;
 
    return 0;
}
0
Kastaneda
31.07.2014, 19:53  [ТС]     Нетривиальный тривиальный калькулятор
  #5

Не по теме:

Цитата Сообщение от ISergey Посмотреть сообщение
На Windows в rcx, rdx
Да, там еще кол-во регистров отличается. Всмысле в *nix на регистрах можно передать больше аргументов

0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
31.07.2014, 19:53
Привет! Вот еще темы с ответами:

Вычислить максимальные нетривиальный делитель - Free Pascal
Ввести натуральное k&gt;1 и вычислить максимальный нетривиальный делитель.

Footer внизу страницы (нетривиальный вариант) - HTML, CSS
Добрый вечер! Не могу никак додуматься, как грамотней решить проблему... Бьюс не первый час. В чем суть: Есть контейнер фиксированной...

Найти наибольший нетривиальный делитель числа - Turbo Pascal
Дано натуральное число. Найти его наибольший нетривиальный делитель или вывести единицу, если такового нет.

Нетривиальный вопрос по издевательству над чужим окном - Visual Basic
Есть окно чужой проги, написанной, по-моему, на VC++. У него есть статус-бар, являющийся классом 'msctls_statusbar32' - если верить...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
31.07.2014, 19:53
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.