Умножение с множественной точностью на встроенной сборке ARM

Я новичок в ассемблере ARM и хочу реализовать одну из своих функций C во встроенном ассемблере. Мои функции - это умножение с множественной точностью, которое умножает 32-битное целое число без знака на 256-битное целое число без знака и помещает результат в 288-битный целочисленный тип данных без знака. Я определил свой тип данных как:

typedef struct UN_256fe{

uint32_t uint32[8];

}UN_256fe;

typedef struct UN_288bite{

uint32_t uint32[9];

}UN_288bite;

и вот моя функция:

void multiply32x256(uint32_t A, UN_256fe* B, UN_288bite* res){
uint32_t temp;
asm (    "umull         %0, %1, %9, %10;\n\t"
         "umull         %18, %2, %9, %11;\n\t"
         "adds          %1, %18, %1;    \n\t"
         "umull         %18, %3, %9, %12;\n\t"
         "adcs          %2, %18, %2;    \n\t"
         "umull         %18, %4, %9, %13;\n\t"
         "adcs          %3, %18, %3;    \n\t"
         "umull         %18, %5, %9, %14;\n\t"
         "adcs          %4, %18, %4;    \n\t"
         "umull         %18, %6, %9, %15;\n\t"
         "adcs          %5, %18, %5;    \n\t"
         "umull         %18, %7, %9, %16;\n\t"
         "adcs          %6, %18, %6;    \n\t"
         "umull         %18, %8, %9, %17;\n\t"
         "adcs          %7, %18, %7;    \n\t"
         "adc           %8, %8, 0 ;     \n\t"

         : "=r"(res->uint32[8]), "=r"(res->uint32[7]), "=r"(res->uint32[6]), "=r"(res->uint32[5]), "=r"(res->uint32[4]),
           "=r"(res->uint32[3]), "=r"(res->uint32[2]), "=r"(res->uint32[1]), "=r"(res->uint32[0])
         : "r"(A), "r"(B->uint32[7]), "r"(B->uint32[6]), "r"(B->uint32[5]),
           "r"(B->uint32[4]), "r"(B->uint32[3]), "r"(B->uint32[2]), "r"(B->uint32[1]), "r"(B->uint32[0]), "r"(temp));

}

Мне кажется, это нормально. Но когда я отлаживаю свой код, например, в первой строке после выполнения "umull %0, %1, %9, %10;\n\t" у меня есть:

(gdb) p/x A //-->%9
$8 = 0x1
(gdb) p/x B->uint32[7] //-->%10
$9 = 0xffffff1
(gdb) p/x res->uint32[8] //-->%0
$10 = 0x1
(gdb) p/x res->uint32[7] //-->%1
$11 = 0x0

Кажется, я допустил некоторые ошибки в инструкции по сборке. Может ли кто-нибудь объяснить это мне?


person A23149577    schedule 13.12.2015    source источник
comment
Я не уверен, в чем ваша проблема, но ни одно из значений в массиве uint32 не изменится после выполнения первой инструкции в операторе asm. Поскольку он делает все в регистрах, только через некоторое время после выполнения всех инструкций в операторе значения в регистрах будут сохранены в массиве. Когда именно, зависит от кода, который компилятор выдает после инструкции сборки для этих хранилищ.   -  person Ross Ridge    schedule 13.12.2015
comment
Кроме того, ваши выходные операнды должны быть помечены ранними ограничениями затирания =&r, чтобы компилятор знал, что нельзя использовать регистры, назначенные этим операндам, в качестве входных операндов. Операнд temp должен быть ранним выходным операндом затирания, чтобы компилятор знал, что инструкция asm изменяет регистр.   -  person Ross Ridge    schedule 13.12.2015
comment
@RossRidge в первой строке, я ожидаю, что A(%9) умножается на B-›uint32[7](%10), а результат сохраняется в res-›uint32[8](%0) и res-› uint32[7](%1). Итак, исходя из того, что вы сказали, результат сохраняется в регистре, но почему? Я уже определил свое предназначение. Должен ли я добавить некоторые другие сборочные линии для хранения моих результатов в res-›uint32[7] и res-›uint32[8] ?? как я уже сказал, я новичок в сборке ARM, но я проделал ту же процедуру в PTX, и это сработало!   -  person A23149577    schedule 13.12.2015
comment
Он хранится в регистре, потому что ограничения для %0 и %1 используют r, что ограничивает операнд регистром. Если вместо этого вы использовали m, то компилятор использовал бы вместо этого операнд памяти, но ассемблер тогда отклонил бы оператор, потому что инструкция UMULL не принимает операнды памяти. Вам не нужно добавлять операторы, чтобы делать магазины самостоятельно. Как я уже сказал, компилятор автоматически выдаст необходимый код (инструкции по ассемблеру) для этих хранилищ после вашего оператора asm.   -  person Ross Ridge    schedule 14.12.2015
comment
@RossRidge Прости, но я не понимаю. Когда я заменяю =r на =&r, компилятор выдает эту ошибку: can’t find a register in class ‘GENERAL_REGS’ while reloading ‘asm’. Я думаю, что это связано с количеством общих регистров в моем процессоре ARM, и кажется, что я использовал много регистров в своем коде. Не могли бы вы привести пример встроенного ассемблерного кода, который получает два операнда uint32_t из памяти и перемножает их, а затем сохраняет 64-битный результат в памяти?   -  person A23149577    schedule 14.12.2015
comment
Тот факт, что вы получаете эту ошибку, имеет смысл. У вас есть 19 операндов, которым нужно задать разные регистры, иначе ассемблерный оператор не будет работать правильно, но компилятор может использовать не более 15 регистров. Для этого вам не нужен оператор asm, просто используйте uint64_t n = (uint64_t) A * B->uint32[7].   -  person Ross Ridge    schedule 14.12.2015
comment
@artlessnoise: хорошая находка, чтобы помочь OP с последующей проблемой, но этот вопрос по-прежнему касается того, где будут выходные значения при одношаговом выполнении asm. (И я бы посоветовал посмотреть на дизассемблирование конечного результата во время отладки, это прояснит, что компилятор сделал для перемещения данных, и что выходные данные все еще будут в регистрах, пока не произойдет сохранение.) Чтобы обойти это , вы могли бы дать встроенному ассемблеру некоторые выходные данные памяти и сохранить их. Или передать адреса во встроенный ассемблер и также выполнить загрузку/сохранение, чтобы преодолеть ограничения регистров.   -  person Peter Cordes    schedule 15.12.2015
comment
@artlessnoise: да, достаточно честно, закрытие как дубликат — хороший способ убрать его из системы. Любой прямой ответ на этот вопрос не добавит ничего, чего еще нет в комментариях, и это проблема. все равно никому не поможешь. Однако разделение встроенного ассемблера — ужасная идея. Компиляторы могут вставлять инструкцию, которая изменяет флаги, между любыми asm блоками. И с этим кодом это вполне может произойти с инструкциями по вычислению адресов для настройки входов/выходов. (И погода была не так уж и плоха. Хотя на обратном пути из PEI ехали довольно плохо.)   -  person Peter Cordes    schedule 15.12.2015