用x64汇编语言编写384位无符号整数乘法(上)

一、用GCC生成汇编模板
1、编写C语言头文件mul384.h,内容就下面一行
uint64_t mul384(uint64_t c[12], uint64_t a[6], uint64_t b[6]);//c = a * b
2、编写相应C程序文件mul384.c,写个空函数就行了
#include <stdint.h>
#include "mul384.h"
uint64_t mul384(uint64_t c[12], uint64_t a[6], uint64_t b[6])
{
    return 0;
}
3、用GCC编译C程序文件生成汇编文件mul384.s,生成汇编模板文件
gcc -Wall -O2 mul384.c -S

这是mul384.s的全部内容

    .file    "mul384.c"
    .text
    .p2align 4,,15
.globl mul384
    .type    mul384, @function
mul384:
.LFB0:
    .cfi_startproc
    xorl    %eax, %eax
    ret
    .cfi_endproc
.LFE0:
    .size    mul384, .-mul384
    .ident    "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-11)"
    .section    .note.GNU-stack,"",@progbits

这个mul384.s文件之所以被称为汇编模板,是因为后面编写的汇编代码需要填入下面两句之间

    .cfi_startproc
    .cfi_endproc

替换原先的这两条指令

    xorl    %eax, %eax
    ret
二、算法选择与汇编程序设计
1、C函数接口设计

在x64平台上,无符号整数位宽为64位,一个384位无符号整数需要6个64位无符号整数构成,两个384位无符号整数相乘,其结果需要不超过768位的存储空间,占用12个64位无符号整数空间,故此设计C函数声明为:

uint64_t mul384(uint64_t c[12], uint64_t a[6], uint64_t b[6]);//c = a * b

uint64_t的定义在头文件stdint.h中,因此应用中编译C程序时要包含此头文件。

2、汇编程序输入输出设计

为了提高代码性能,采用“一次输入,一次输出”的内存访问策略,除了最初的输入和最后的输出外,整个运算过程中不访问内存,过程数据全部缓存于SSE寄存器中,为此CPU必须支持SSE42指令集,本文代码只适用于用户态程序,不能用于内核态(不清楚内核态和用户态编程区别的读者请无视这句话)。另外就是调用者传给函数用于输入输出的数组指针a[]、b[]和c[]必须满足16字节对齐要求,否则必将触发CPU错误导致进程崩溃。

3、乘法算法选择

384位乘法在大数运算中属于短位长运算,FFT乘法神马的就别想了,还不够折腾的,Karatsuba算法也不用考虑,对于常用x64处理器来讲,若将基本64位加法指令 addq 耗时设定为1单位,那么带进位64位加法指令 adcq 耗时为2单位,64位无符号乘法指令 mulq 耗时为4单位,综合考虑得失后,采用最基本的分治乘法算法作为本文使用的算法,详细如下:

|====|====|====|====|====|====|====|====|====|====|=====|=====|
|c[0]|c[1]|c[2]|c[3]|c[4]|c[5]|c[6]|c[7]|c[8]|c[9]|c[10]|c[11]|
|====|====|====|====|====|====|====|====|====|====|=====|=====|
|a[0]*b[0]|    |    |    |    |    |    |    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |a[0]*b[1]|    |    |    |    |    |    |    |     |     |
|    |a[1]*b[0]|    |    |    |    |    |    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |a[0]*b[2]|    |    |    |    |    |    |     |     |
|    |    |a[1]*b[1]|    |    |    |    |    |    |     |     |
|    |    |a[2]*b[0]|    |    |    |    |    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |a[0]*b[3]|    |    |    |    |    |     |     |
|    |    |    |a[1]*b[2]|    |    |    |    |    |     |     |
|    |    |    |a[2]*b[1]|    |    |    |    |    |     |     |
|    |    |    |a[3]*b[0]|    |    |    |    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |a[0]*b[4]|    |    |    |    |     |     |
|    |    |    |    |a[1]*b[3]|    |    |    |    |     |     |
|    |    |    |    |a[2]*b[2]|    |    |    |    |     |     |
|    |    |    |    |a[3]*b[1]|    |    |    |    |     |     |
|    |    |    |    |a[4]*b[0]|    |    |    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |    |a[0]*b[5]|    |    |    |     |     |
|    |    |    |    |    |a[1]*b[4]|    |    |    |     |     |
|    |    |    |    |    |a[2]*b[3]|    |    |    |     |     |
|    |    |    |    |    |a[3]*b[2]|    |    |    |     |     |
|    |    |    |    |    |a[4]*b[1]|    |    |    |     |     |
|    |    |    |    |    |a[5]*b[0]|    |    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |    |    |a[1]*b[5]|    |    |     |     |
|    |    |    |    |    |    |a[2]*b[4]|    |    |     |     |
|    |    |    |    |    |    |a[3]*b[3]|    |    |     |     |
|    |    |    |    |    |    |a[4]*b[2]|    |    |     |     |
|    |    |    |    |    |    |a[5]*b[1]|    |    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |    |    |    |a[2]*b[5]|    |     |     |
|    |    |    |    |    |    |    |a[3]*b[4]|    |     |     |
|    |    |    |    |    |    |    |a[4]*b[3]|    |     |     |
|    |    |    |    |    |    |    |a[5]*b[2]|    |     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |    |    |    |    |a[3]*b[5]|     |     |
|    |    |    |    |    |    |    |    |a[4]*b[4]|     |     |
|    |    |    |    |    |    |    |    |a[5]*b[3]|     |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |    |    |    |    |    |a[4]*b[5] |     |
|    |    |    |    |    |    |    |    |    |a[5]*b[4] |     |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |    |    |    |    |    |    |    |    |    | a[5]*b[5] |
|====|====|====|====|====|====|====|====|====|====|=====|=====|
4、寄存器规划

以高性能运算为目的的汇编语言编程设计中,寄存器规划是重中之重,还好384位乘法对x64处理器来讲只是入门级小菜,所以我用了两周时间完成了寄存器规划,详细使用规划如下:

(1).rdi是输出数据c[]的首地址,rsi是输入数据a[]的首地址,rdx是输入数据b[]的首地址

(2).xmm0 ~ xmm5这个6个SSE寄存器用于缓存运算过程与结果数据
|---------|---------|---------|---------|---------|-----------|
|  %xmm0  |  %xmm1  |  %xmm2  |  %xmm3  |  %xmm4  |  %xmm5    |
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|c[0]|c[1]|c[2]|c[3]|c[4]|c[5]|c[6]|c[7]|c[8]|c[9]|c[10]|c[11]|
|----|----|----|----|----|----|----|----|----|----|-----|-----|

(3).xmm6 ~ xmm8这个6个SSE寄存器用于输入数据a和b
|---------|---------|---------|---------|---------|---------|
|  %xmm6  |  %xmm7  |  %xmm8  |  %xmm6  |  %xmm7  |  %xmm8  |
|----|----|----|----|----|----|----|----|----|----|----|----|
|a[0]|a[1]|a[2]|a[3]|a[4]|a[5]|b[0]|b[1]|b[2]|b[3]|b[4]|b[5]|
|----|----|----|----|----|----|----|----|----|----|----|----|

(4).xmm14和xmm15用于r12 ~ r15的备份与恢复
|---------|---------|
| %xmm14  | %xmm15  |
|----|----|----|----|
|%r12|%r13|%r14|%r15|
|----|----|----|----|

(5).r10 ~ r15用于乘法指令mulq的操作数,其数值固定
|----|----|----|----|----|----|
|%r10|%r11|%r12|%r13|%r14|%r15|
|----|----|----|----|----|----|
|a[1]|a[3]|a[5]|b[1]|b[3]|b[5]|
|----|----|----|----|----|----|

(6).r8, r9, rsi三个通用寄存器用于累加过程,循环使用
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|c[0]|c[1]|c[2]|c[3]|c[4]|c[5]|c[6]|c[7]|c[8]|c[9]|c[10]|c[11]|
|----|----|----|----|----|----|----|----|----|----|-----|-----|
|    |%r8 |%r9 |%rsi|%r8 |%r9 |%rsi|%r8 |%r9 |%rsi|%r8  |%r9  |
|----|----|----|----|----|----|----|----|----|----|-----|-----|


转载于:https://my.oschina.net/safedead/blog/376148

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是用8086汇编语言实现三十二无符号乘法的代码: ``` ; 32-bit unsigned multiplication using 16-bit multiplication instructions mul32: push bx ; save bx register value push cx ; save cx register value ; load operands into registers mov ax, word ptr [si] ; load lower word of operand 1 into ax mov bx, word ptr [si + 2] ; load upper word of operand 1 into bx mov cx, word ptr [di] ; load lower word of operand 2 into cx mov dx, word ptr [di + 2] ; load upper word of operand 2 into dx ; perform multiplication mul cx ; multiply lower words mov si, ax ; save lower word of result mov ax, bx ; move upper word of operand 1 to ax mul cx ; multiply lower word of operand 1 with upper word of operand 2 add si, ax ; add result to lower word of result mov ax, word ptr [si - 2] ; load lower word of result into ax mov bx, word ptr [si] ; load upper word of result into bx mul dx ; multiply upper words add bx, ax ; add result to upper word of result ; store result in memory mov word ptr [di], si ; store lower word of result in memory mov word ptr [di + 2], bx ; store upper word of result in memory pop cx ; restore cx register value pop bx ; restore bx register value ret ; return from function ``` 这段代码使用了16乘法指令MUL来完成32无符号乘法操作。它首先将操作数加载到AX、BX、CX和DX寄存器中,然后使用MUL指令将低相乘并将结果存储在SI寄存器中,接着将BX中的高移动到AX中,再次使用MUL指令将其与CX中的低相乘,并将结果加到SI中。然后将SI寄存器中的低和高分别移动到AX和BX中,并再次使用MUL指令将DX中的高与AX中的低相乘,并将结果加到BX中。最后,将结果存储在目标地址中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值