ARM passes -mstrict-align (-mno-unaligned-access on ARMv7) to everything,
because when the MMU is off any unaligned access there faults. The
compiler then has to assemble every wide unaligned load out of single
bytes: one ldr becomes eight ldrb and seven orr/lsl.

This is especially painful for decompressors which refill their buffer
in 64-bit words. On a Rockchip RK3576 SPL, zstd unpacks a 30MiB Falcon
mode Linux kernel in 1947ms built this way and 596ms without the flag,
and the SPL gets 2.3KiB smaller by cutting down on the byte-wise loads and
ORs. gzip, whose inflate walks its input a byte at a time anyway, gains
6%.

Add SAFE_UNALIGNED_ACCESS for a platform to state that the MMU is on by
the time it unpacks an image, and let the decompression libraries drop
the flag when it is set. The arch exports the flag it added rather than
lib/ naming it, so this stays correct for anything else which adds one.

No functional change anywhere until the symbol is selected.

Signed-off-by: Alexey Charkov <[email protected]>
---
 arch/Kconfig                 | 27 +++++++++++++++++++++++++++
 arch/arm/cpu/armv7/config.mk |  3 +++
 arch/arm/cpu/armv8/config.mk |  3 +++
 lib/Makefile                 |  3 +++
 lib/lzma/Makefile            |  8 ++++++++
 lib/lzo/Makefile             |  8 ++++++++
 lib/zlib/Makefile            |  8 ++++++++
 lib/zstd/Makefile            |  8 ++++++++
 8 files changed, 68 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index 8d63afeb138e..4bac67c388a7 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -27,6 +27,33 @@ config SUPPORT_LITTLE_ENDIAN
        bool
        default y if !SUPPORT_BIG_ENDIAN
 
+config SAFE_UNALIGNED_ACCESS
+       bool
+       help
+         Selected by platforms on which an unaligned access cannot fault by
+         the time U-Boot proper loads and unpacks an image.
+
+         ARM builds pass -mstrict-align (-mno-unaligned-access on ARMv7),
+         because an unaligned access with MMU off there raises an alignment
+         fault. The compiler then assembles every wide unaligned load out of
+         single bytes, which costs a decompressor refilling a bit buffer in
+         64-bit words up to four times its speed. Code which only ever runs
+         with the MMU enabled can use this to opt back out of the flag.
+
+         Select it only where that is guaranteed, and under the same
+         condition the platform's own code tests before enabling caches, so
+         that the two cannot drift apart.
+
+config SPL_SAFE_UNALIGNED_ACCESS
+       bool
+       depends on SPL
+       help
+         The same for SPL, which on most platforms runs with the MMU off and
+         must therefore leave this alone. See SAFE_UNALIGNED_ACCESS.
+
+         There is deliberately no TPL or VPL variant: those phases run before
+         DRAM is up, so the flag always stays.
+
 config SYS_CACHE_SHIFT_4
        bool
 
diff --git a/arch/arm/cpu/armv7/config.mk b/arch/arm/cpu/armv7/config.mk
index 68036d6fbdaf..b8728207f18e 100644
--- a/arch/arm/cpu/armv7/config.mk
+++ b/arch/arm/cpu/armv7/config.mk
@@ -9,3 +9,6 @@
 # of our decision.
 PF_NO_UNALIGNED := $(call cc-option, -mno-unaligned-access,)
 PLATFORM_CPPFLAGS += $(PF_NO_UNALIGNED)
+# Code which only runs with the MMU on may drop this again; see
+# CONFIG_SAFE_UNALIGNED_ACCESS.
+export PF_NO_UNALIGNED
diff --git a/arch/arm/cpu/armv8/config.mk b/arch/arm/cpu/armv8/config.mk
index 4d74b2a533e0..554773c8d45b 100644
--- a/arch/arm/cpu/armv8/config.mk
+++ b/arch/arm/cpu/armv8/config.mk
@@ -6,6 +6,9 @@ PLATFORM_RELFLAGS += $(call cc-option,-mbranch-protection=none)
 
 PF_NO_UNALIGNED := $(call cc-option, -mstrict-align)
 PLATFORM_CPPFLAGS += $(PF_NO_UNALIGNED)
+# Code which only runs with the MMU on may drop this again; see
+# CONFIG_SAFE_UNALIGNED_ACCESS.
+export PF_NO_UNALIGNED
 
 EFI_LDS := elf_aarch64_efi.lds
 EFI_CRT0 := crt0_aarch64_efi.o
diff --git a/lib/Makefile b/lib/Makefile
index b7272c6f0cba..d8503d16cc6d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -93,6 +93,9 @@ obj-$(CONFIG_$(PHASE_)GZIP) += gunzip.o
 obj-$(CONFIG_$(PHASE_)LZO) += lzo/
 obj-$(CONFIG_$(PHASE_)LZMA) += lzma/
 obj-$(CONFIG_$(PHASE_)LZ4) += lz4_wrapper.o
+ifdef CONFIG_$(PHASE_)SAFE_UNALIGNED_ACCESS
+CFLAGS_REMOVE_lz4_wrapper.o += $(PF_NO_UNALIGNED)
+endif
 
 obj-$(CONFIG_$(PHASE_)LIB_RATIONAL) += rational.o
 
diff --git a/lib/lzma/Makefile b/lib/lzma/Makefile
index 09380074b3df..769d44901f7b 100644
--- a/lib/lzma/Makefile
+++ b/lib/lzma/Makefile
@@ -10,3 +10,11 @@ ccflags-y += -DZ7_LZMA_PROB32
 ccflags-$(CONFIG_$(PHASE_)LZMA_SIZE_OPTIMIZATION) += -DZ7_LZMA_SIZE_OPT
 
 obj-y += LzmaDec.o LzmaTools.o
+
+# These only run once an image is being unpacked, which on some platforms is
+# late enough that unaligned accesses are safe. Where the platform says so,
+# let the compiler use them: assembling a 64-bit read out of bytes is both
+# slower and larger.
+ifdef CONFIG_$(PHASE_)SAFE_UNALIGNED_ACCESS
+ccflags-remove-y += $(PF_NO_UNALIGNED)
+endif
diff --git a/lib/lzo/Makefile b/lib/lzo/Makefile
index 45612a83dabc..7909e9a3585d 100644
--- a/lib/lzo/Makefile
+++ b/lib/lzo/Makefile
@@ -4,3 +4,11 @@
 # Stefan Roese, DENX Software Engineering, [email protected].
 
 obj-y += lzo1x_decompress.o
+
+# These only run once an image is being unpacked, which on some platforms is
+# late enough that unaligned accesses are safe. Where the platform says so,
+# let the compiler use them: assembling a 64-bit read out of bytes is both
+# slower and larger.
+ifdef CONFIG_$(PHASE_)SAFE_UNALIGNED_ACCESS
+ccflags-remove-y += $(PF_NO_UNALIGNED)
+endif
diff --git a/lib/zlib/Makefile b/lib/zlib/Makefile
index 4a3e985a9494..6559c5cf7d2c 100644
--- a/lib/zlib/Makefile
+++ b/lib/zlib/Makefile
@@ -4,3 +4,11 @@
 # Wolfgang Denk, DENX Software Engineering, [email protected].
 
 obj-y += zlib.o
+
+# These only run once an image is being unpacked, which on some platforms is
+# late enough that unaligned accesses are safe. Where the platform says so,
+# let the compiler use them: assembling a 64-bit read out of bytes is both
+# slower and larger.
+ifdef CONFIG_$(PHASE_)SAFE_UNALIGNED_ACCESS
+ccflags-remove-y += $(PF_NO_UNALIGNED)
+endif
diff --git a/lib/zstd/Makefile b/lib/zstd/Makefile
index c9a7dfe405b4..1194a1d86445 100644
--- a/lib/zstd/Makefile
+++ b/lib/zstd/Makefile
@@ -35,3 +35,11 @@ zstd_common-y := \
                common/error_private.o \
                common/fse_decompress.o \
                common/zstd_common.o \
+
+# These only run once an image is being unpacked, which on some platforms is
+# late enough that unaligned accesses are safe. Where the platform says so,
+# let the compiler use them: assembling a 64-bit read out of bytes is both
+# slower and larger.
+ifdef CONFIG_$(PHASE_)SAFE_UNALIGNED_ACCESS
+ccflags-remove-y += $(PF_NO_UNALIGNED)
+endif

-- 
2.55.0

Reply via email to