22
sm2_z256_loong64.S 代码分析
Sun Yimin edited this page 2025-10-09 11:13:58 +08:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

代码来源

代码来自GmSSL的一个未合并的PR2497946ac6/src/sm2_z256_loong64.S

函数说明

__sm2_z256_modp_add

实现(t0, t1, t2, t3) = (t0, t1, t2, t3) + (a4, a5, a6, a7)每个代表64位整数从低到高排列也就是说t0a4是最低64位数也就是多字(Multi-Word)加法。

总体实现思路

  1. 在RISC(如龙芯/LoongArch、MIPS)架构中没有x86那样的进位标识寄存器所以进位都要用sltu (无符号小于)等指令手动计算。容易出错!
  2. 使用加1来实现mod P,这里的12^{256} - P
  3. 使用or合并进位(carry)。
  4. 使用masknezmaskeqz来选择最终结果。

__sm2_z256_modp_dbl

实现(t0, t1, t2, t3) = (t0, t1, t2, t3)*2double运算。

总体实现思路

  1. 使用移位操作:srli.dsrli.d和算术运算alsl.d实现double运算。alsl.d $t3, $t3, $t5, 1表示t3 = t3<<1 + t5
  2. 其它和__sm2_z256_modp_add实现一致。

2497946ac6/src/sm2_z256_loong64.S (L94)

	add.d	$t5, $t1, $t8	//no carry
	add.d	$t5, $t5, $a1

改为

	add.d	$t5, $a1, $t8	//no carry
	add.d	$t5, $t5, $t1

改完后的 //no carry 注释才成立 (这里t8的值为0x00000000ffffffff,a1为上一个字加法进位)。

2497946ac6/src/sm2_z256_loong64.S (L104)

add.d	$a2, $a2, $a1

改为

or	$a2, $a2, $a1

也可以,其实我们只关注a2的值是0还是非0。

__sm2_z256_modp_sub

计算 (t0, t1, t2, t3) = (t0, t1, t2, t3) - (a4, a5, a6, a7)

总体实现思路

  1. 和加法一样在RISC(如龙芯/LoongArch、MIPS)架构中没有x86那样的借位标识寄存器所以借位都要用sltu (无符号小于)等指令手动计算。
  2. 如果最后一个字的减法有借位,则再减1来实现mod P,这里的12^{256} - P 否则再减的就是0。
  3. 使用maskeqz来选择减数是0还是1

__sm2_z256_modp_neg_sub

计算(t0, t1, t2, t3) = (t0, t1, t2, t3) - (a4, a5, a6, a7)

和上面方法类似。

__sm2_z256_modp_haf

计算 (t0, t1, t2, t3) = (t0, t1, t2, t3) / 2

总体实现思路

  1. 如果该数是奇数,则先减1,这里的12^{256} - P 相当于先加P否则先减的就是0。
  2. 通过andi $a2, $t0, 1取最低位值。
  3. 使用maskeqz来选择减数是0还是1
  4. 通过srli.dbstrins.d进行除2操作。bstrins.d $t0, $t1, 63, 63表示t0[63] = t1[0]t0的其它位值保持不变。

__sm2_z256_modp_mont_mul

按字蒙哥马利模约减乘法WW-MM计算 (t0,t1,t2,t3) = (a4,a5,a6,a7) * (t0,t1,t2,t3)

实现思路

  1. 可参考https://github.com/emmansun/gmsm/wiki/SM2-WWMM-(2)
    T_2=T_1 \ast P=t_0 \ast P= t_0 \ast (2^{256}-(2^{32} \ast 2^{192} + 0 \ast 2^{128} + (2^{32} - 1) \ast 2^{64} + 1))
    T_2=(t_0-t_0>>32) \ast 2^{256}+(0 - t_0<<32) \ast 2^{192} + (0 - t_0>>32) \ast 2^{128} + (t_0 - t_0<<32) \ast 2^{64} - t_0
    T_3=T + T_2=(t_4+t_0-t_0>>32) \ast 2^{256}+(t_3 - t_0<<32) \ast 2^{192} + (t_2 - t_0>>32) \ast 2^{128} + (t_1 + t_0 - t_0<<32) \ast 2^{64}
    注释:这里 t_0<<32t_0 \ast 2^{32} 的低64位 t_0>>32t_0 \ast 2^{32} 的高64位。 它这里先计算T的系数(WORD),再计算 T_2 的系数。
  2. 几个no carry判断的准确性:
    1. 2497946ac6/src/sm2_z256_loong64.S (L268) add.d $t5, $a1, $t5,这里$a1是可能的进位这一点是不一定成立的。但是如果把它和261行add.d $t5, $t5, $s2交换一下顺序则成立因为两个64位字的乘法产生的高64位字不可能是 2^{64}-1 。所以如果不交换顺序,结果是否正确,需要进一步证明,并没那么直观。
    2. 2497946ac6/src/sm2_z256_loong64.S (L273) 这里是成立的。
  3. 约简还没完成,也就是上一步 T_2 + T 还没算完,就开始了下一个字的乘法,代码可读性降低,为了性能?
  4. 最后,依然使用加1来实现mod P,这里的12^{256} - P

__sm2_z256_modp_mont_sqr

计算 (t0, t1, t2, t3) = (t0, t1, t2, t3)^2

总体实现思路

  1. 先完整计算出平方结果用8个64位字表示算完后再进行蒙哥马利约简计算。
  2. 最后,依然使用加1来实现mod P,这里的12^{256} - P

总结

  1. 进位、借位要格外小心、无进位判断要严谨。
  2. 使用加(或者减)1来实现mod P,这里的12^{256} - P
  3. 使用masknez和(或)maskeqz来选择最终结果。