Проблема, с которой вы столкнулись при оптимизации этого кода:
08000328 <mul_test01>:
8000328: f04f 5000 mov.w r0, #536870912 ; 0x20000000
800032c: 4770 bx lr
800032e: bf00 nop
ваш код ничего не делает во время выполнения, поэтому оптимизатор может просто вычислить окончательный ответ.
this:
.thumb_func
.globl mul_test02
mul_test02:
smull r2,r3,r0,r1
mov r0,r3
bx lr
вызывается с этим:
c = mul_test02(0x40000000,0x40000000);
дает 0x10000000
UMULL дает тот же результат, потому что вы используете положительные числа, операнды и результаты все положительные, поэтому он не попадает вподписанные / неподписанные различия.
Хм, хорошо, вы меня поняли на этом.Я бы прочитал ваш код как указание компилятору повысить умножение до 64 бит.smull - это два 32-битных операнда, которые дают 64-битный результат, а это не то, о чем просит ваш код .... но и gcc, и clang использовали smull в любом случае, даже если я оставил его как не вызываемую функцию, поэтому он не зналВремя компиляции, что операнды не имели значащих цифр выше 32, они все еще использовали smull.
Возможно, причина была в смещении.
Да, именно так ..
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b) >> 31;
return(c);
}
дает
как gcc, так и clang (хорошо перебивает clangr0 и r1 вместо использования r2 и r3)
08000340 <mul_test04>:
8000340: fb81 2300 smull r2, r3, r1, r0
8000344: 0fd0 lsrs r0, r2, #31
8000346: ea40 0043 orr.w r0, r0, r3, lsl #1
800034a: 4770 bx lr
, но этот
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b);
return(c);
}
дает этот
gcc:
08000340 <mul_test04>:
8000340: fb00 f001 mul.w r0, r0, r1
8000344: 4770 bx lr
8000346: bf00 nop
лязг:
0800048c <mul_test04>:
800048c: 4348 muls r0, r1
800048e: 4770 bx lr
Таким образом, при сдвиге битов компиляторы понимают, что вас интересует только верхняя часть результата, поэтому они могут отбросить верхнюю часть операндов, что означает использование smull.
Теперь, если вы сделаете это:
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b) >> 32;
return(c);
}
оба компилятора станут еще умнее, в частности Clang:
0800048c <mul_test04>:
800048c: fb81 1000 smull r1, r0, r1, r0
8000490: 4770 bx lr
gcc:
08000340 <mul_test04>:
8000340: fb81 0100 smull r0, r1, r1, r0
8000344: 4608 mov r0, r1
8000346: 4770 bx lr
Я могуобратите внимание, что 0x40000000 рассматривается как число с плавающей точкой, где вы отслеживаете десятичное место, и это место является фиксированным.0x20000000 будет иметь смысл в качестве ответа.Я пока не могу решить, работает ли этот 31-битный сдвиг универсально или только для этого одного случая.
Полный пример, использованный для вышеупомянутого, приведен здесь
https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01
идействительно запустил его на stm32f4, чтобы убедиться, что он работает и результаты.
РЕДАКТИРОВАТЬ:
Если вы передаете параметры в функцию вместо их жесткого кодирования в функции:
int myfun ( int a, int b )
{
return(a+b);
}
Компилятор вынужден создавать код времени выполнения вместо оптимизации ответа во время компиляции.
Теперь, если вы вызываете эту функцию из другой функции с жестко закодированными числами:
...
c=myfun(0x1234,0x5678);
...
В этомВызывая функцию, компилятор может решить вычислить ответ и просто поместить его туда во время компиляции.Если функция myfun () является глобальной (не объявленной как статическая), то компилятор не знает, будет ли ее использовать какой-либо другой код, который будет позже ссылаться, поэтому даже вблизи точки вызова в этом файле он оптимизирует ответ, который ему все еще нужно для получения фактической функции.и оставьте его в объекте для вызова другого кода в других файлах, так что вы все еще можете проверить, что компилятор / оптимизатор делает с этим кодом C.Если вы не используете, например, llvm, где вы можете оптимизировать весь проект (по файлам), внешний код, вызывающий эту функцию, будет использовать реальную функцию, а не вычисленный ответ во время компиляции.оставил код времени выполнения для функции как глобальную функцию, но внутри файла он вычислил ответ во время компиляции и поместил жестко закодированный ответ в код вместо вызова функции:
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b) >> 31;
return(c);
}
в другой функции втот же файл:
hexstring(mul_test04(0x40000000,0x40000000),1);
Сама функция реализована в коде:
0800048c <mul_test04>:
800048c: fb81 1000 smull r1, r0, r1, r0
8000490: 0fc9 lsrs r1, r1, #31
8000492: ea41 0040 orr.w r0, r1, r0, lsl #1
8000496: 4770 bx lr
, но там, где она вызывается, они жестко закодировали ответ, потому что у них была вся информация, необходимая для выполнения.Итак:
8000520: f04f 5000 mov.w r0, #536870912 ; 0x20000000
8000524: 2101 movs r1, #1
8000526: f7ff fe73 bl 8000210 <hexstring>
Если вам не нужен жестко закодированный ответ, вам нужно использовать функцию, которая не находится на том же этапе оптимизации.
Управление компилятором и оптимизатором сводится к большому количествупрактика, и это не точная наука, так как компиляторы и оптимизаторы постоянно развиваютсяили хуже).
Выделив небольшой фрагмент кода в функции, вы вызываете проблемы другим способом, более крупным функциям, скорее всего, понадобится фрейм стека и по мере необходимости будут вытеснять переменные из регистров в стек, более мелким функциям это может не понадобиться, иоптимизаторы могут изменить то, как код реализован в результате.Вы тестируете фрагмент кода одним способом, чтобы увидеть, что делает компилятор, затем используете его в более крупной функции и не получаете желаемого результата.Если есть точная инструкция или последовательность инструкций, которые вы хотите реализовать .... Реализуйте их на ассемблере.Если вы нацеливались на определенный набор инструкций в конкретном наборе / процессоре команд, тогда избегайте игры, избегайте изменения кода при смене компьютеров / компиляторов / и т. Д. И просто используйте ассемблер для этой цели.при необходимости ifdef или иным образом использовать параметры условной компиляции для сборки для разных целей без ассемблера.