[PATCH 1/1] riscv: Enable ARCH_HAS_FAST_MULTIPLIER for RV64I
Chenxi Mao
maochenxi at eswin.com
Wed Jul 22 21:59:12 EDT 2020
Hi Palmer and Emil:
As Emil mentioned in previous E-mail loop, I did the same test on my kernel as well.
My kernel is based on Linux 5.8-RC6 with GCC-10.1. (ISA C extension enabled)
The disassembly code as below:
CONFIG_ARCH_HAS_FAST_MULTIPLIER enabled:
0000000000000000 <__sw_hweight32>:
0: 555557b7 lui a5,0x55555
4: 0015571b srliw a4,a0,0x1
8: 55578793 addi a5,a5,1365 # 55555555 <.LASF5+0x5555509d>
c: 8ff9 and a5,a5,a4
e: 9d1d subw a0,a0,a5
0000000000000010 <.LVL1>:
10: 333337b7 lui a5,0x33333
14: 33378793 addi a5,a5,819 # 33333333 <.LASF5+0x33332e7b>
18: 0025571b srliw a4,a0,0x2
1c: 8d7d and a0,a0,a5
1e: 8ff9 and a5,a5,a4
20: 9fa9 addw a5,a5,a0
22: 0047d51b srliw a0,a5,0x4
26: 9fa9 addw a5,a5,a0
28: 0f0f1537 lui a0,0xf0f1
2c: 1141 addi sp,sp,-16
2e: f0f50513 addi a0,a0,-241 # f0f0f0f <.LASF5+0xf0f0a57>
32: e422 sd s0,8(sp)
34: 8fe9 and a5,a5,a0
36: 0800 addi s0,sp,16
38: 0087951b slliw a0,a5,0x8
3c: 6422 ld s0,8(sp)
3e: 9d3d addw a0,a0,a5
40: 0105179b slliw a5,a0,0x10
44: 9d3d addw a0,a0,a5
46: 0185551b srliw a0,a0,0x18
4a: 0141 addi sp,sp,16
4c: 8082 ret
CONFIG_ARCH_HAS_FAST_MULTIPLIER disabled:
000000000000004e <__sw_hweight32_default>:
4e: 55555737 lui a4,0x55555
52: 0015579b srliw a5,a0,0x1
56: 55570713 addi a4,a4,1365 # 55555555 <.LASF5+0x5555509d>
5a: 8ff9 and a5,a5,a4
5c: 9d1d subw a0,a0,a5
000000000000005e <.LVL3>:
5e: 333337b7 lui a5,0x33333
62: 33378793 addi a5,a5,819 # 33333333 <.LASF5+0x33332e7b>
66: 0025571b srliw a4,a0,0x2
6a: 8d7d and a0,a0,a5
6c: 8ff9 and a5,a5,a4
6e: 9fa9 addw a5,a5,a0
70: 0047d51b srliw a0,a5,0x4
74: 9d3d addw a0,a0,a5
76: 0f0f17b7 lui a5,0xf0f1
7a: 1141 addi sp,sp,-16
7c: f0f78793 addi a5,a5,-241 # f0f0f0f <.LASF5+0xf0f0a57>
80: e422 sd s0,8(sp)
82: 8fe9 and a5,a5,a0
84: 0800 addi s0,sp,16
86: 0087d51b srliw a0,a5,0x8
8a: 6422 ld s0,8(sp)
8c: 9fa9 addw a5,a5,a0
8e: 0107d51b srliw a0,a5,0x10
92: 9d3d addw a0,a0,a5
94: 0ff57513 andi a0,a0,255
98: 0141 addi sp,sp,16
9a: 8082 ret
This 2 implementations is almost same but small differences.
Especially in CONFIG_ARCH_HAS_FAST_MULTIPLIER condition, below code didn't use "mul" instructions.
" return (w * 0x01010101) >> 24; "
So I am trying to translate this code with inline assembly as below:
//return (w * 0x01010101) >> 24;
__asm__ (
" mul %0, %0, %1\n"
: "+r" (w)
: "r" (w), "r"(0x01010101)
:);
return w >> 24;
After above change, the disassambly as below:
0000000000000000 <__sw_hweight32>:
0: 555557b7 lui a5,0x55555
4: 0015571b srliw a4,a0,0x1
8: 55578793 addi a5,a5,1365 # 55555555 <.LASF5+0x55555119>
c: 8ff9 and a5,a5,a4
e: 9d1d subw a0,a0,a5
0000000000000010 <.LVL1>:
10: 333337b7 lui a5,0x33333
14: 0025571b srliw a4,a0,0x2
18: 33378793 addi a5,a5,819 # 33333333 <.LASF5+0x33332ef7>
1c: 8d7d and a0,a0,a5
1e: 8ff9 and a5,a5,a4
20: 9fa9 addw a5,a5,a0
22: 0047d71b srliw a4,a5,0x4
26: 9f3d addw a4,a4,a5
28: 0f0f17b7 lui a5,0xf0f1
2c: 1141 addi sp,sp,-16
2e: f0f78793 addi a5,a5,-241 # f0f0f0f <.LASF5+0xf0f0ad3>
32: e422 sd s0,8(sp)
34: 8ff9 and a5,a5,a4
36: 0800 addi s0,sp,16
38: 01010737 lui a4,0x1010
3c: 853e mv a0,a5
000000000000003e <.LVL2>:
3e: 1017071b addiw a4,a4,257
42: 02f50533 mul a0,a0,a5
46: 6422 ld s0,8(sp)
48: 0185551b srliw a0,a0,0x18
"mul" instruction is leveraged as expectation, but 0x01010101 load waste several instructions.
Based on this test, force to leverage "mul" instruction might be not faster than current compiler implementations.
I am not sure above assembly is the best way to load 0x01010101? I checked the ISA manual, "lui" only
load 20 bits per time, is this the best way to load instants?
On the other hand, I try to compare ARM64 disassembly code:
.....
4: 3200c3e2 mov w2, #0x1010101 // #16843009
......
w = (w + (w >> 4)) & 0x0f0f0f0f;
20: 0b401000 add w0, w0, w0, lsr #4
24: 1200cc00 and w0, w0, #0xf0f0f0f
return (w * 0x01010101) >> 24;
28: 1b027c00 mul w0, w0, w2
Only one "mov" instructions to load 0x1010101 and one "mul" instruction for multiply.
Let me summary as below:
1. GCC 10.1 cannot generate "mul" instruction when CONFIG_ARCH_HAS_FAST_MULTIPLIER enabled.
2. force to generate "mul" didn't get better because instants load waste instructions.
3. If GCC compiler behavior is best solution for this case, we could have below work around on Riscv.
unsigned int __sw_hweight32(unsigned int w)
{
-#ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER
+/*
+ * Risc-V could not generate mul(w) instruction in this case
+ */
+#if defined(CONFIG_ARCH_HAS_FAST_MULTIPLIER) && !defined(CONFIG_RISCV)
w -= (w >> 1) & 0x55555555;
w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
w = (w + (w >> 4)) & 0x0f0f0f0f;
Chenxi
On 2020/7/21 上午9:17, Palmer Dabbelt wrote:
> On Wed, 08 Jul 2020 22:19:22 PDT (-0700), maochenxi at eswin.com wrote:
>> Enable ARCH_HAS_FAST_MULTIPLIER on RV64I
>> which works fine on GCC-9.3 and GCC-10.1
>>
>> PS2: remove ARCH_SUPPORTS_INT128 because of RV64I already enabled.
>>
>> Signed-off-by: Chenxi Mao <maochenxi at eswin.com>
>> ---
>> arch/riscv/Kconfig | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
>> index 128192e14ff2..84e6777fecad 100644
>> --- a/arch/riscv/Kconfig
>> +++ b/arch/riscv/Kconfig
>> @@ -202,6 +202,7 @@ config ARCH_RV64I
>> bool "RV64I"
>> select 64BIT
>> select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && GCC_VERSION >= 50000
>> + select ARCH_HAS_FAST_MULTIPLIER
>> select HAVE_DYNAMIC_FTRACE if MMU
>> select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE
>> select HAVE_FTRACE_MCOUNT_RECORD
>
> Ah, thanks -- this one didn't show up when I was looking at the last one. I
> think we can put the fast multiplier on rv32 and rv64, there shouldn't be any
> difference there. I guess in theory we should be sticking this all in some
> sort of "platform type" optimization flags, but that's probably bit much for
> now.
More information about the linux-riscv
mailing list