我一直在阅读div和mul组装操作,我决定通过在C中编写一个简单的程序来实现它们:
#include <stdlib.h>
#include <stdio.h>
int main()
{
size_t i = 9;
size_t j = i / 5;
printf("%zu\n",j);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
然后生成汇编语言代码:
gcc -S division.c -O0 -masm=intel
Run Code Online (Sandbox Code Playgroud)
但是看生成的division.s文件,它不包含任何div操作!相反,它通过位移和魔术数字来做某种黑魔法.这是一个计算代码片段i/5:
mov rax, QWORD PTR [rbp-16] ; Move i (=9) to RAX
movabs rdx, -3689348814741910323 ; Move some magic number to RDX (?)
mul rdx ; Multiply 9 by magic number
mov rax, rdx ; Take only the upper 64 bits of the …Run Code Online (Sandbox Code Playgroud) 如何仅使用位移和加法进行乘法和除法?
什么是最快的可分性测试?比如说,给定一个小端架构和一个32位有符号整数:如何计算得非常快,一个数字可被2,3,4,5整除,......最多16?
警告:给定的代码仅为示例.每一行都是独立的!使用模运算的明显解决方案在许多处理器上都很慢,这些处理器没有DIV硬件(像许多ARM一样).有些编译器也无法进行这样的优化(例如,如果divisor是函数的参数或依赖于某些东西).
Divisible_by_1 = do();
Divisible_by_2 = if (!(number & 1)) do();
Divisible_by_3 = ?
Divisible_by_4 = ?
Divisible_by_5 = ?
Divisible_by_6 = ?
Divisible_by_7 = ?
Divisible_by_8 = ?
Divisible_by_9 = ?
Divisible_by_10 = ?
Divisible_by_11 = ?
Divisible_by_12 = ?
Divisible_by_13 = ?
Divisible_by_14 = ?
Divisible_by_15 = ?
Divisible_by_16 = if(!number & 0x0000000F) do();
Run Code Online (Sandbox Code Playgroud)
和特殊情况:
Divisible_by_2k = if(number & (tk-1)) do(); //tk=2**k=(2*2*2*...) k times
Run Code Online (Sandbox Code Playgroud) 我有以下 C/C++ 函数:
unsigned div3(unsigned x) {
return x / 3;
}
Run Code Online (Sandbox Code Playgroud)
使用 clang 10 at编译时-O3,结果为:
div3(unsigned int):
mov ecx, edi # tmp = x
mov eax, 2863311531 # result = 3^-1
imul rax, rcx # result *= tmp
shr rax, 33 # result >>= 33
ret
Run Code Online (Sandbox Code Playgroud)
我所理解的是:除以 3 相当于乘以乘法逆 3 -1 mod 2 32,即 2863311531。
不过还是有些不明白的地方:
ecx/rcx呢?我们不能乘rax用edi直接?eaxand不是更快ecx吗?imul …在我的程序中,我使用了很多整数除以10 ^ x和整数mod函数10.
例如:
unsigned __int64 a = 12345;
a = a / 100;
....
Run Code Online (Sandbox Code Playgroud)
要么:
unsigned __int64 a = 12345;
a = a % 1000;
....
Run Code Online (Sandbox Code Playgroud)
如果我要使用正确的位移>>,那么我将获得模式2^x,这不是我想要的.
有什么办法可以加速整数除法和mod函数的程序吗?
任何人都可以告诉我纯粹的汇编代码以十进制格式显示寄存器中的值吗?请不要建议使用printf hack,然后使用gcc进行编译.
描述:
好吧,我做了一些研究和NASM的一些实验,并认为我可以使用c库中的printf函数来打印整数.我是通过使用GCC编译器编译目标文件来完成的,所有工作都很公平.
但是,我想要实现的是以十进制形式打印存储在任何寄存器中的值.
我做了一些研究,发现DOS命令行的中断向量021h可以显示字符串和字符,而2或9位于ah寄存器中,数据在dx中.
结论:
我找到的所有示例都没有显示如何在不使用C库的printf的情况下以十进制形式显示寄存器的内容值.有没有人知道如何在装配中这样做?
我必须将数字拆分成数字才能在LCD上显示.现在我使用以下方法:
pos = 7;
do
{
LCD_Display(pos, val % 10);
val /= 10;
pos--;
} while (pos >= 0 && val);
Run Code Online (Sandbox Code Playgroud)
这种方法的问题在于MSP430微控制器上的除法和模运算非常慢.有没有替代这种方法的东西,既不涉及分裂,也不会减少操作次数?
注意:我不能使用任何库函数,例如itoa.这些库很大,而且功能本身也非常耗费资源(在循环次数和RAM使用方面).
据我所知,大多数编译器会通过乘法然后向右移位进行快速除法.例如,如果你检查这个SO线程,它会说当你要求Microsoft编译器除以10时,它会将被除数乘以0x1999999A(即2 ^ 32/10),然后将结果除以2 ^ 32(使用32向右移动).
到现在为止还挺好.
但是,一旦我使用GCC在ARM上测试了相同的除法,但编译器做了一些略微不同的事情.首先,它将被除数乘以0x66666667(2 ^ 34/10),然后将结果除以2 ^ 34.到目前为止,除了使用更高的乘数之外,它与Microsoft相同.然而,在那之后,它从结果中减去(被除数/ 2 ^ 31).
我的问题:为什么在ARM版本上有额外的减法?你能给我一个数字例子,如果没有减法,结果会出错吗?
如果你想检查生成的代码,它在下面(带我的评论):
ldr r2, [r7, #4] @--this loads the dividend from memory into r2
movw r3, #:lower16:1717986919 @--moves the lower 16 bits of the constant
movt r3, #:upper16:1717986919 @--moves the upper 16 bits of the constant
smull r1, r3, r3, r2 @--multiply long, put lower 32 bits in r1, higher 32 in r3
asr r1, r3, #2 @--r3>>2, then store in r1 (effectively …Run Code Online (Sandbox Code Playgroud) 我有这个功能
long long int divideBy10(long long int a){
return a / 10;
}
Run Code Online (Sandbox Code Playgroud)
它被编译为:
mov rax, rdi
movabs rcx, 7378697629483820647
imul rcx
mov rax, rdx
shr rax, 63
sar rdx, 2
add rax, rdx
ret
Run Code Online (Sandbox Code Playgroud)
如果我添加 __builtin_assume(a > 0);
它被编译为
mov rax, rdi
movabs rcx, -3689348814741910323
mul rcx
mov rax, rdx
shr rax, 3
ret
Run Code Online (Sandbox Code Playgroud)
代码效率更高,因为它不必担心负号。现在,如果我添加 __builtin_assume(a < 10000); 我原以为它会被编译成一个乘法而没有移位。但事实并非如此。
我想也许编译器只跟踪数字是正数还是负数,但是
long long int noBranch(long long int a){
__builtin_assume(a < 400);
if( a < 500){
return a;
}
return 0; …Run Code Online (Sandbox Code Playgroud) 上周我接受了采访,有一个这样的测试:
使用SHIFT LEFT,SHIFT RIGHT,ADD,SUBSTRACT指令计算N/9(给定N为正整数)
.