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
