Рассмотрим следующий код:
int bn_div(bn_t *bn1, bn_t *bn2, bn_t *bnr)
{
uint32 q, m; /* Division Result */
uint32 i; /* Loop Counter */
uint32 j; /* Loop Counter */
/* Check Input */
if (bn1 == NULL) return(EFAULT);
if (bn1->dat == NULL) return(EFAULT);
if (bn2 == NULL) return(EFAULT);
if (bn2->dat == NULL) return(EFAULT);
if (bnr == NULL) return(EFAULT);
if (bnr->dat == NULL) return(EFAULT);
#if defined(__i386__) || defined(__amd64__)
__asm__ (".intel_syntax noprefix");
__asm__ ("pushl %eax");
__asm__ ("pushl %edx");
__asm__ ("pushf");
__asm__ ("movl %eax, (bn1->dat[i])");
__asm__ ("xorl %edx, %edx");
__asm__ ("divl (bn2->dat[j])");
__asm__ ("movl (q), %eax");
__asm__ ("movl (m), %edx");
__asm__ ("popf");
__asm__ ("popl %edx");
__asm__ ("popl %eax");
#else
q = bn->dat[i] / bn->dat[j];
m = bn->dat[i] % bn->dat[j];
#endif
/* Return */
return(0);
}
Типы данных uint32 - это, в основном, unsigned long int или 32-битное целое число без знака uint32_t. Тип bnint - это либо unsigned short int (uint16_t), либо uint32_t в зависимости от того, доступны ли 64-битные типы данных или нет. Если доступно 64-битное значение, тогда bnint является uint32, иначе это uint16. Это было сделано для захвата переноса/переполнения в других частях кода. Структура bn_t определяется следующим образом:
typedef struct bn_data_t bn_t;
struct bn_data_t
{
uint32 sz1; /* Bit Size */
uint32 sz8; /* Byte Size */
uint32 szw; /* Word Count */
bnint *dat; /* Data Array */
uint32 flags; /* Operational Flags */
};
Функция начинается в строке 300 в моем исходном коде. Поэтому, когда я пытаюсь скомпилировать/сделать это, я получаю следующие ошибки:
system:/home/user/c/m3/bn 1036 $$$ ->make
clang -I. -I/home/user/c/m3/bn/.. -I/home/user/c/m3/bn/../include -std=c99 -pedantic -Wall -Wextra -Wshadow -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wwrite-strings -Wfloat-equal -Winline -Wunknown-pragmas -Wundef -Wendif-labels -c /home/user/c/m3/bn/bn.c
/home/user/c/m3/bn/bn.c:302:12: warning: unused variable 'q' [-Wunused-variable]
uint32 q, m; /* Division Result */
^
/home/user/c/m3/bn/bn.c:302:15: warning: unused variable 'm' [-Wunused-variable]
uint32 q, m; /* Division Result */
^
/home/user/c/m3/bn/bn.c:303:12: warning: unused variable 'i' [-Wunused-variable]
uint32 i; /* Loop Counter */
^
/home/user/c/m3/bn/bn.c:304:12: warning: unused variable 'j' [-Wunused-variable]
uint32 j; /* Loop Counter */
^
/home/user/c/m3/bn/bn.c:320:14: error: unknown token in expression
__asm__ ("movl %eax, (bn1->dat[i])");
^
<inline asm>:1:18: note: instantiated into assembly here
movl %eax, (bn1->dat[i])
^
/home/user/c/m3/bn/bn.c:322:14: error: unknown token in expression
__asm__ ("divl (bn2->dat[j])");
^
<inline asm>:1:12: note: instantiated into assembly here
divl (bn2->dat[j])
^
4 warnings and 2 errors generated.
*** [bn.o] Error code 1
Stop in /home/user/c/m3/bn.
system:/home/user/c/m3/bn 1037 $$$ ->
Что я знаю:
Я считаю себя довольно хорошо разбирающимся в ассемблере x86 (о чем свидетельствует код, который я написал выше). Однако в последний раз, когда я смешивал язык высокого уровня, а ассемблер использовал Borland Pascal около 15-20 лет назад при написании графических драйверов для игр (до Windows 95). Мое знакомство с синтаксисом Intel.
Что я не знаю:
Как мне получить доступ к элементам bn_t (особенно * dat) из asm? Поскольку * dat является указателем на uint32, я обращаюсь к элементам как к массиву (например, bn1- > dat [i]).
Как получить доступ к локальным переменным, объявленным в стеке?
Я использую push/pop для восстановления скребковых регистров до их предыдущих значений, чтобы не нарушать компилятор. Однако мне также нужно включить ключевое слово volatile в локальные переменные?
Или, есть ли лучший способ, о котором я не знаю? Я не хочу помещать это в отдельный вызов функции из-за накладных расходов, поскольку эта функция критична по производительности.
Дополнительно:
Прямо сейчас, я только начинаю писать эту функцию, чтобы она не была полной. Отсутствуют петли и другой такой код поддержки/клея. Но основной смысл заключается в доступе к локальным переменным/элементам структуры.
ИЗМЕНИТЬ 1:
Синтаксис, который я использую, кажется единственным, который поддерживает clang. Я попробовал следующий код, и clang дал мне всевозможные ошибки:
__asm__ ("pushl %%eax",
"pushl %%edx",
"pushf",
"movl (bn1->dat[i]), %%eax",
"xorl %%edx, %%edx",
"divl ($0x0c + bn2 + j)",
"movl %%eax, (q)",
"movl %%edx, (m)",
"popf",
"popl %%edx",
"popl %%eax"
);
Он хочет, чтобы я поставил закрывающую скобку в первой строке, заменив запятую. Я перешел на использование %% вместо%, потому что я где-то читал, что встроенная сборка требует, чтобы %% обозначала регистры CPU, а clang рассказывал мне, что я использовал недопустимую escape-последовательность.