From 379f8be2709a320be27fb16060b546a3298b80d2 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 19 Nov 2024 12:43:24 +0000 Subject: [PATCH] compiler: require ARMv8.1 Signed-off-by: Nuno Cruces --- config_supported.go | 7 ++- internal/platform/cpuid.go | 5 ++ internal/platform/cpuid_amd64.go | 4 +- internal/platform/cpuid_amd64.s | 4 +- internal/platform/cpuid_arm64.go | 76 ++++++++++++++++++++++++++ internal/platform/cpuid_arm64.s | 21 +++++++ internal/platform/cpuid_unsupported.go | 2 +- internal/platform/platform.go | 2 +- internal/platform/platform_arm64.go | 4 +- 9 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 internal/platform/cpuid_arm64.go create mode 100644 internal/platform/cpuid_arm64.s diff --git a/config_supported.go b/config_supported.go index 27dd62a0db..214c2bb8c1 100644 --- a/config_supported.go +++ b/config_supported.go @@ -9,6 +9,11 @@ package wazero +import "github.com/tetratelabs/wazero/internal/platform" + func newRuntimeConfig() RuntimeConfig { - return NewRuntimeConfigCompiler() + if platform.CompilerSupported() { + return NewRuntimeConfigCompiler() + } + return NewRuntimeConfigInterpreter() } diff --git a/internal/platform/cpuid.go b/internal/platform/cpuid.go index 0dc6ec19ce..0220d56fd4 100644 --- a/internal/platform/cpuid.go +++ b/internal/platform/cpuid.go @@ -28,3 +28,8 @@ const ( CpuExtraFeatureAmd64ABM CpuFeature = 1 << 5 // Note: when adding new features, ensure that the feature is included in CpuFeatureFlags.Raw. ) + +const ( + // CpuFeatureArm64Atomic is the flag to query CpuFeatureFlags.Has for Large System Extensions capabilities on arm64 + CpuFeatureArm64Atomic CpuFeature = 1 << 21 +) diff --git a/internal/platform/cpuid_amd64.go b/internal/platform/cpuid_amd64.go index fbdb539366..c68be39022 100644 --- a/internal/platform/cpuid_amd64.go +++ b/internal/platform/cpuid_amd64.go @@ -1,4 +1,4 @@ -//go:build amd64 && !tinygo +//go:build gc package platform @@ -12,7 +12,7 @@ type cpuFeatureFlags struct { } // cpuid exposes the CPUID instruction to the Go layer (https://www.amd.com/system/files/TechDocs/25481.pdf) -// implemented in impl_amd64.s +// implemented in cpuid_amd64.s func cpuid(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32) // cpuidAsBitmap combines the result of invoking cpuid to uint64 bitmap. diff --git a/internal/platform/cpuid_amd64.s b/internal/platform/cpuid_amd64.s index 8d483f3a6f..4950ee6293 100644 --- a/internal/platform/cpuid_amd64.s +++ b/internal/platform/cpuid_amd64.s @@ -1,6 +1,9 @@ +//go:build gc + #include "textflag.h" // lifted from github.com/intel-go/cpuid and src/internal/cpu/cpu_x86.s + // func cpuid(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32) TEXT ·cpuid(SB), NOSPLIT, $0-24 MOVL arg1+0(FP), AX @@ -11,4 +14,3 @@ TEXT ·cpuid(SB), NOSPLIT, $0-24 MOVL CX, ecx+16(FP) MOVL DX, edx+20(FP) RET - diff --git a/internal/platform/cpuid_arm64.go b/internal/platform/cpuid_arm64.go new file mode 100644 index 0000000000..819c0a457e --- /dev/null +++ b/internal/platform/cpuid_arm64.go @@ -0,0 +1,76 @@ +//go:build gc + +package platform + +import "runtime" + +// CpuFeatures exposes the capabilities for this CPU, queried via the Has, HasExtra methods. +var CpuFeatures = loadCpuFeatureFlags() + +// cpuFeatureFlags implements CpuFeatureFlags interface. +type cpuFeatureFlags struct { + isar0 uint64 + isar1 uint64 +} + +// implemented in cpuid_arm64.s +func getisar0() uint64 + +// implemented in cpuid_arm64.s +func getisar1() uint64 + +func loadCpuFeatureFlags() CpuFeatureFlags { + switch runtime.GOOS { + case "darwin", "windows": + // These OSes require ARMv8.1, which includes atomic instructions. + return &cpuFeatureFlags{ + isar0: uint64(CpuFeatureArm64Atomic), + isar1: 0, + } + case "linux", "freebsd": + // These OSes allow reading the instruction set attribute registers. + return &cpuFeatureFlags{ + isar0: getisar0(), + isar1: getisar1(), + } + default: + return &cpuFeatureFlags{} + } +} + +// Has implements the same method on the CpuFeatureFlags interface. +func (f *cpuFeatureFlags) Has(cpuFeature CpuFeature) bool { + return (f.isar0 & uint64(cpuFeature)) != 0 +} + +// HasExtra implements the same method on the CpuFeatureFlags interface. +func (f *cpuFeatureFlags) HasExtra(cpuFeature CpuFeature) bool { + return (f.isar1 & uint64(cpuFeature)) != 0 +} + +// Raw implements the same method on the CpuFeatureFlags interface. +func (f *cpuFeatureFlags) Raw() uint64 { + // Below, we only set the first 4 bits for the features we care about, + // instead of setting all the unnecessary bits obtained from the CPUID instruction. + var ret uint64 + switch runtime.GOARCH { + case "arm64": + if f.Has(CpuFeatureArm64Atomic) { + ret = 1 << 0 + } + case "amd64": + if f.Has(CpuFeatureAmd64SSE3) { + ret = 1 << 0 + } + if f.Has(CpuFeatureAmd64SSE4_1) { + ret |= 1 << 1 + } + if f.Has(CpuFeatureAmd64SSE4_2) { + ret |= 1 << 2 + } + if f.HasExtra(CpuExtraFeatureAmd64ABM) { + ret |= 1 << 3 + } + } + return ret +} diff --git a/internal/platform/cpuid_arm64.s b/internal/platform/cpuid_arm64.s new file mode 100644 index 0000000000..98305ad474 --- /dev/null +++ b/internal/platform/cpuid_arm64.s @@ -0,0 +1,21 @@ +//go:build gc + +#include "textflag.h" + +// lifted from github.com/golang/sys and cpu/cpu_arm64.s + +// func getisar0() uint64 +TEXT ·getisar0(SB), NOSPLIT, $0-8 + // get Instruction Set Attributes 0 into x0 + // mrs x0, ID_AA64ISAR0_EL1 = d5380600 + WORD $0xd5380600 + MOVD R0, ret+0(FP) + RET + +// func getisar1() uint64 +TEXT ·getisar1(SB), NOSPLIT, $0-8 + // get Instruction Set Attributes 1 into x0 + // mrs x0, ID_AA64ISAR1_EL1 = d5380620 + WORD $0xd5380620 + MOVD R0, ret+0(FP) + RET diff --git a/internal/platform/cpuid_unsupported.go b/internal/platform/cpuid_unsupported.go index 291bcea65f..50a178f52b 100644 --- a/internal/platform/cpuid_unsupported.go +++ b/internal/platform/cpuid_unsupported.go @@ -1,4 +1,4 @@ -//go:build !amd64 || tinygo +//go:build !(amd64 || arm64) || !gc package platform diff --git a/internal/platform/platform.go b/internal/platform/platform.go index 7bef265415..b9af094c1c 100644 --- a/internal/platform/platform.go +++ b/internal/platform/platform.go @@ -11,7 +11,7 @@ import ( // archRequirementsVerified is set by platform-specific init to true if the platform is supported var archRequirementsVerified bool -// CompilerSupported is exported for tests and includes constraints here and also the assembler. +// CompilerSupported includes constraints here and also the assembler. func CompilerSupported() bool { switch runtime.GOOS { case "linux", "darwin", "freebsd", "netbsd", "dragonfly", "windows": diff --git a/internal/platform/platform_arm64.go b/internal/platform/platform_arm64.go index caac58a3d8..a8df707c71 100644 --- a/internal/platform/platform_arm64.go +++ b/internal/platform/platform_arm64.go @@ -2,6 +2,6 @@ package platform // init verifies that the current CPU supports the required ARM64 features func init() { - // No further checks currently needed. - archRequirementsVerified = true + // Ensure atomic instructions are supported. + archRequirementsVerified = CpuFeatures.Has(CpuFeatureArm64Atomic) }