From 35bf4bc59ccc3544c3c9fa1a7e5c7143cf50698e Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Thu, 1 Feb 2024 15:16:56 -0800 Subject: [PATCH] commonlib: Add generic word-at-a-time optimization to ipchksum() This patch adds a generic optimization to calculate a machine-word-sized "wide sum" for the ipchksum() algorithm. This is often not as efficient as handcrafted assembly (about half as fast on arm64 and x86_32, about the same speed on x86_64), but likely still much better than nothing on architectures that we don't have handcrafted assembly for. Change-Id: I8f0fe117e2788d1b6801b73824b97e1e31ecc694 Signed-off-by: Julius Werner Reviewed-on: https://review.coreboot.org/c/coreboot/+/80304 Reviewed-by: Yu-Ping Wu Tested-by: build bot (Jenkins) --- src/commonlib/bsd/ipchksum.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/commonlib/bsd/ipchksum.c b/src/commonlib/bsd/ipchksum.c index b7434e5fd7..10f343f43a 100644 --- a/src/commonlib/bsd/ipchksum.c +++ b/src/commonlib/bsd/ipchksum.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ +#include #include /* See RFC 1071 for mathematical explanations of why we can first sum in a larger register and @@ -34,7 +35,7 @@ uint16_t ipchksum(const void *data, size_t size) :: "cc" ); } -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) || defined(__x86_64__) /* __aarch64__ */ size_t size8 = size / 8; const uint64_t *p8 = data; i = size8 * 8; @@ -57,7 +58,18 @@ uint16_t ipchksum(const void *data, size_t size) [size8] "+c" (size8) /* put size in ECX so we can JECXZ */ :: "cc" ); -#endif /* __i386__ || __x86_64__ */ +#else /* __i386__ || __x86_64__ */ + size_t aligned_size = ALIGN_DOWN(size, sizeof(unsigned long)); + const unsigned long *p_long = data; + for (; i < aligned_size; i += sizeof(unsigned long)) { + unsigned long new_sum = wide_sum + *p_long++; + /* Overflow check to emulate a manual "add with carry" in C. The compiler seems + to be clever enough to find ways to elide the branch on most archs. */ + if (new_sum < wide_sum) + new_sum++; + wide_sum = new_sum; + } +#endif while (wide_sum) { sum += wide_sum & 0xFFFF;