Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Implement __multi3 for GCC7 MIPS64r6 builds

GCC7 is a bit too eager to generate suboptimal __multi3 calls (128bit
multiply with 128bit result) for MIPS64r6 builds, even in code which
doesn't explicitly use 128bit types, such as the following:

unsigned long func(unsigned long a, unsigned long b)
{
return a > (~0UL) / b;
}

Which GCC rearanges to:

return (unsigned __int128)a * (unsigned __int128)b > 0xffffffffffffffff;

Therefore implement __multi3, but only for MIPS64r6 with GCC7 as under
normal circumstances we wouldn't expect any calls to __multi3 to be
generated from kernel code.

Reported-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: James Hogan <jhogan@kernel.org>
Tested-by: Waldemar Brodkorb <wbx@openadk.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Maciej W. Rozycki <macro@mips.com>
Cc: Matthew Fortune <matthew.fortune@mips.com>
Cc: Florian Fainelli <florian@openwrt.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/17890/

authored by

James Hogan and committed by
Ralf Baechle
ebabcf17 ccf85c74

+73 -1
+2 -1
arch/mips/lib/Makefile
··· 16 16 obj-$(CONFIG_CPU_TX39XX) += r3k_dump_tlb.o 17 17 18 18 # libgcc-style stuff needed in the kernel 19 - obj-y += ashldi3.o ashrdi3.o bswapsi.o bswapdi.o cmpdi2.o lshrdi3.o ucmpdi2.o 19 + obj-y += ashldi3.o ashrdi3.o bswapsi.o bswapdi.o cmpdi2.o lshrdi3.o multi3.o \ 20 + ucmpdi2.o
+17
arch/mips/lib/libgcc.h
··· 10 10 struct DWstruct { 11 11 int high, low; 12 12 }; 13 + 14 + struct TWstruct { 15 + long long high, low; 16 + }; 13 17 #elif defined(__LITTLE_ENDIAN) 14 18 struct DWstruct { 15 19 int low, high; 20 + }; 21 + 22 + struct TWstruct { 23 + long long low, high; 16 24 }; 17 25 #else 18 26 #error I feel sick. ··· 30 22 struct DWstruct s; 31 23 long long ll; 32 24 } DWunion; 25 + 26 + #if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) 27 + typedef int ti_type __attribute__((mode(TI))); 28 + 29 + typedef union { 30 + struct TWstruct s; 31 + ti_type ti; 32 + } TWunion; 33 + #endif 33 34 34 35 #endif /* __ASM_LIBGCC_H */
+54
arch/mips/lib/multi3.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/export.h> 3 + 4 + #include "libgcc.h" 5 + 6 + /* 7 + * GCC 7 suboptimally generates __multi3 calls for mips64r6, so for that 8 + * specific case only we'll implement it here. 9 + * 10 + * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981 11 + */ 12 + #if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ == 7) 13 + 14 + /* multiply 64-bit values, low 64-bits returned */ 15 + static inline long long notrace dmulu(long long a, long long b) 16 + { 17 + long long res; 18 + 19 + asm ("dmulu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b)); 20 + return res; 21 + } 22 + 23 + /* multiply 64-bit unsigned values, high 64-bits of 128-bit result returned */ 24 + static inline long long notrace dmuhu(long long a, long long b) 25 + { 26 + long long res; 27 + 28 + asm ("dmuhu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b)); 29 + return res; 30 + } 31 + 32 + /* multiply 128-bit values, low 128-bits returned */ 33 + ti_type notrace __multi3(ti_type a, ti_type b) 34 + { 35 + TWunion res, aa, bb; 36 + 37 + aa.ti = a; 38 + bb.ti = b; 39 + 40 + /* 41 + * a * b = (a.lo * b.lo) 42 + * + 2^64 * (a.hi * b.lo + a.lo * b.hi) 43 + * [+ 2^128 * (a.hi * b.hi)] 44 + */ 45 + res.s.low = dmulu(aa.s.low, bb.s.low); 46 + res.s.high = dmuhu(aa.s.low, bb.s.low); 47 + res.s.high += dmulu(aa.s.high, bb.s.low); 48 + res.s.high += dmulu(aa.s.low, bb.s.high); 49 + 50 + return res.ti; 51 + } 52 + EXPORT_SYMBOL(__multi3); 53 + 54 + #endif /* 64BIT && CPU_MIPSR6 && GCC7 */